I want to implement dynamically growing array in C.
I read the words from a file to a char** array and when the array is full grow its size by 2. But when I reallocate the memory, the first two elements of the array is lost. I tried with lines = realloc() but it's crashing. What am I doing wrong?
test.txt:
test1
test2
test3
test4
test5
test6
test7
test8
test9 (no end of line)
my output:
─
đ%
test3
test4
test5
test6
test7
test9
Code:
#include <stdio.h>
#include <malloc.h>
int size = 8;
char **read(FILE *input, char **lines, int *lSize) {
for (int i = 0; !feof(input); i++) {
*lSize += 1;
if (*lSize > size) {
realloc(lines, (size *= 2) * sizeof(char *));
for (int j = (*lSize) - 1; j < size; j++) {
lines[j] = (char *) malloc(1024 * sizeof(char));
}
}
fscanf(input, "%s", lines[i]);
}
return lines;
}
int main(){
FILE *file = fopen("test.txt", "r");
if (file == NULL) {
return -1;
}
char **lines = malloc(size * sizeof(char *));
for (int i = 0; i < size; ++i) {
lines[i] = malloc(1024 * sizeof(char));
}
int lsize = 0;
read(file, lines, &lsize);
printf("lSize:%d\n", lsize);
printf("size:%d\n", size);
for (int i = 0; i < lsize; ++i) {
printf("%s\n", lines[i]);
}
for (int i = 0; i < size; ++i) {
free(lines[i]);
}
free(lines);
return 0;
}
given char **read(FILE *input, char **lines, int *lSize)
what does: realloc(lines, (size *= 2) * sizeof(char *)) do?
Well it changes the sizes of the memory block pointed to by lines, possibly by moving it.
That is why it returns a new pointer. Just assigning that new pointer back to lines won't help much (on its own) as that only updates the local variable lines in this function.
But luckily lines is also returned from the function. But its return value is ignored in main so after read returns, the lines in main may be a bogus pointer.
do lines = read(file, lines, &lsize) in main as well.
Related
I'm trying to read a file containing wikipedia pages and turning them into a matrix of 0 and 1 depending on whether there is an arc between them. So that we call a function using this matrix to sort their [PageRank.][1] but it says segmentation fault. Any help would be much appreciated.
#include <stdlib.h>
#include <string.h>
#define SIZE 10000
int main(int argc, char
const * argv[]) {
int graphe[SIZE][SIZE] = {0};
FILE * file = NULL;
char chaine[SIZE] = "";//the line from fgets
char * page = malloc(SIZE * sizeof(char));//an array with all the first pages of the file
char ** tab = malloc(SIZE * SIZE * sizeof(char));//2D array containing all the pages
file = fopen("wiki-zulu.txt", "r");
if (file != NULL) {
int i = 0, j = 0, cpt = 0;
while (fgets(chaine, SIZE, file) != NULL) {
char * token = strtok(chaine, "|");
page[i] = * token;
while (token != NULL) {
tab[i][j] = * token;
token = strtok(NULL, "|");
j++;
cpt++;
i++;
}
for (int i = 0; i < cpt; i++) {
for (int j = 0; j < cpt; j++) {
for (int k = 0; k < cpt; k++) {
if (tab[i][j] == page[k] && i != j) {//comparing all values in the 2D array with the array of pages
graphe[i][k] = 1;//1 if there is a link between them
}
printf("%d\t", graphe[i][j]);
}
}
printf("\n");
}
free(page);
free(tab);
fclose(file);
}
}
return 0;
}```
[1]: https://i.stack.imgur.com/eITFZ.png
Your problem is most likely the way you allocate the tab matrix.
It is 1D array not a 2D one. And you cannot access it the way you are.
More standard, safer allocation of 2D matrix (you can change calloc with malloc):
// Allocate the first dimension and check for error
char **tab = (char **) calloc(SIZE, sizeof(char*));
if (!tab) {
perror("calloc");
}
// Loop through the first dim to allocate the second one.
// Don't forget to free the allocated memory if something goes wrong
for (size_t i = 0; i < SIZE; ++i) {
tab[i] = (char*) calloc(SIZE, sizeof(char));
if (!tab[i]) {
for (size_t j = i; j >= 0; --j) {
free(tab[j]);
}
free(tab);
perror("calloc");
}
}
Currently, I am trying to create a C program that prints the last few lines of a text file, read in through the command line. However, it is currently causing a segmentation error when I try to copy the strings from fgets into the main array. I have been unable to fix this, and so have not been able to test the rest of my code. How would I begin to fix the segmentation error? I have posted the code below:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
int i=1,j,printNumber;
char **arr = (char **) malloc (100 * sizeof(char *));
char *line = (char *) malloc (80 * sizeof(char));
if (argc == 1) {
printNumber = 10;
}
else {
printNumber = atoi(argv[1]);
}
while (fgets(line,80,stdin) != NULL) {
if (line != NULL) {
line[strlen(line)-1] = '\0';
strcpy(arr[i],line); //SEGMENTATION ERROR!!!!
}
else {
free(line);
strcpy(arr[i],NULL);
}
i++;
printf("%d ",i);
}
free(arr);
for (j = i-printNumber-1; j < i-1; j++) {
printf("%s ", arr[j]);
}
printf("\n");
return 0;
}
You are allocating space for arr, which is a pointer to a pointer to char, but not allocating any individual char * pointers within arr.
Since you allocated arr with the size of 100 * sizeof(char *), I assume you want 100 sub-entries in arr. Sure:
for(i = 0; i < 100; i++)
arr[i] = malloc(80 * sizeof(char));
Then, when you free arr:
for(i = 0; i < 100; i++)
free(arr[i]);
free(arr);
Note that it is good practice to always check malloc for failure (return value of NULL) and handle it, and to set pointers to NULL after freeing them once to avoid double-free bugs.
You don't always know the length of the longest line (not until you try to read) OR how many last lines you are expected to keep track of (but is given at runtime). Thus, both of these values need to be known before you allocate memory or delegated to a function that does it for you.
#include <stdio.h>
#include <stdlib.h>
struct Line {
char *line; // content
size_t storage_sz; // allocation size of line memory
ssize_t sz; // size of line, not including terminating null byte ('\0')
};
int main(int argc, char *argv[]) {
int max_lines = 10;
if (argc > 1) {
max_lines = atoi(argv[1]);
}
if (max_lines < 0) {
fprintf(stderr, "%s\n", "Sorry, no defined behaviour of negative values (yet)\n");
return EXIT_FAILURE;
}
// keep an extra slot for the last failed read at EOF
struct Line *lines = (struct Line *) calloc(max_lines + 1, sizeof(struct Line));
int end = 0;
int size = 0;
// only keep track of the last couple of lines
while ((lines[end].sz = getline(&lines[end].line, &lines[end].storage_sz, stdin)) != -1) {
end++;
if (end > max_lines) {
end = 0;
}
if (size < max_lines) {
size++;
}
}
// time to print them back
int first = end - size;
if (first < 0) {
first += size + 1;
}
for (int count = size; count; count--) {
// lines might contain null bytes we can't use printf("%s", lines[first].line);
fwrite(lines[first].line, lines[first].sz, 1u, stdout);
first++;
if (first > size) {
first = 0;
}
}
// clear up memory after use
for (int idx = 0; idx <= max_lines; idx++) {
free(lines[idx].line);
}
free(lines);
return EXIT_SUCCESS;
}
I don't know why but my code prints a list of (null)(null)(null)....
I have to print the list of words from a file 'words.txt'.
Another question is: fscanf ignore white spaces?
#define WORD_LENGTH 1024
#define SIZE_QUOTE 100
int main(){
char **quote = malloc(sizeof(char*) * (size_t)SIZE_QUOTE);
long i;
for(i = 0; i < SIZE_QUOTE; i++){
if(!(malloc(sizeof(char) * (size_t)WORD_LENGTH)))
exit(1);
}
i = 0;
FILE *pf = fopen("words.txt", "r");
while(!feof(pf) && i < SIZE_QUOTE){
fscanf(pf, "%s", quote[i]);
printf("%s", quote[i]);
i++;
}
fclose(pf);
free(quote);
}
You're never assigning the return value of malloc() to quote[i] so they end up staying NULL (if you're lucky):
char **quote = malloc(sizeof(char*) * (size_t)SIZE_QUOTE);
long i;
for(i = 0; i < SIZE_QUOTE; i++){
if(!(malloc(sizeof(char) * WORD_LENGTH)))
It should be something like this instead:
char **quote = malloc(sizeof(char*) * (size_t)SIZE_QUOTE);
for(int i = 0; i < SIZE_QUOTE; i++){
quote[i] = malloc(sizeof(char) * WORD_LENGTH);
if(!quote[i])
Also you could avoid malloc() entirely and statically initialize that array if all the sizes are known:
char quote[SIZE_QUOTE][WORD_LENGTH] = {{'\0'}};
Also, you should be free()-ing the individual quote[i] at the end too:
for(int i = 0; i < SIZE_QUOTE; ++i) free(quote[i]);
free(quote);
There's other mistakes that have been pointed out through the comments already, so I won't elaborate further.
I've done a program that opens the file (read binary), and saves all the words(in the file) in an array of char (allocated dynamically in base of the length of the word).
This is the code:
char **leggi_stringhe(const char *filename, size_t *size) {
FILE *f = fopen(filename, "rb");
if (f == NULL) {
*size = 0;
return NULL;
}
int x;
if (fread(&x, 1, 4, f) != 4) {
*size = 0;
return NULL;
}
char **stringhe = malloc((x) * sizeof(char));
for (int i = 0; i < x; i++) {
int z = 0;
if (fread(&z, 1, 4, f) != 4) {
*size = 0;
return NULL;
}
stringhe[i] = malloc((z)* sizeof(char));
if (fread(stringhe[i], 1, z, f) != z) {
*size = 0;
return NULL;
}
stringhe[i][z] = 0;
}
*size = x;
fclose(f);
return stringhe;
}
int main(void) {
size_t t;
char **a = leggi_stringhe("file1.bin", &t);
for (int i = 0; i < t; i++)
free(a[i]);
free(a);;
}
The program works, but i have problems with memory deallocation.
After calling of leggi_stringhe function, the variable a contains:
a[0] = "first"
a[1] = "second"
a[2] = "third"
but when i'm trying to deallocate the whole a variable as i wrote, the debugger stops with a warning.
I was inspired by this question for writing my code Using Dynamic Memory allocation for arrays, but do not understand why I get this error when i try to deallocate.
Your initial call to malloc is wrong. You allocate space for x characters, not for pointers to char.
Your second call inside the loop is wrong to, as you don't allocate space for the terminator.
Lastly, and unrelated to the problems you ask about, but if the fread calls inside the loop fails, you will have memory leaks.
There are some problems with your code:
This line:
char **stringhe = malloc((x) * sizeof(char));
Needs to be:
char **stringhe = malloc((x) * sizeof(char*)); /* or sizeof *stringhe */
As you need to allocate x char* pointers for stringhe.
Inside your first for loop, you are not adding +1 for null-terminator. It needs to be instead:
stringhe[i] = malloc(z+1); /* sizeof(char) = 1 */
You need to check return of malloc(). It can return NULL if unsuccessful. You can do this by simply checking if (ptr == NULL), then exit the program. It would unsafe to allow a failed malloc() to continue in the program.
for (int i = 0; i < t; i++) is comparing an int with size_t. This should be for (size_t i = 0; i < t; i++) instead.
here's a really simple C program, and I just can't figure out why it crashes:
int main () {
size_t argc = 2;
char **argv = malloc(argc * sizeof(char *));
for (int i = 0; i < 20; i++) {
if (i >= argc) {
argc *= 2;
argv = realloc(argv, argc);
}
argv[i] = strdup("hello world!");
}
for (int i = 0; i < 20; i++) {
printf("argv[%d] = \"%s\"\n", i, argv[i]); // it crashes on this line
free(argv[i]);
}
free(argv);
}
argv = realloc(argv, argc);
The size is wrong; you want argc * sizeof(*argv) instead.
When you do your realloc, you're allocating space for N chars instead of N pointers to char.
Then again, given that you just want space for 20 items anyway, why not just start by allocating space for 20 items, putting the data there, and being done with it?
#define size 20
char **argv = malloc(size * sizeof(char *));
if (argv == NULL) {
fprintf(stderr, "Allocation failed!");
return 1;
}
for (int i=0; i<size; i++)
argv[i] = strdup("hello world!");
Also note that realloc can/will return a null pointer in case of failure, so you generally want to do something like:
char **temp = realloc(old_ptr, new_size);
if (temp != NULL)
old_ptr = temp;