I'm trying to create an array of strings in C. My plan is for the program to read line from a file and them gradually build this array.
But I noticed that after I allocate memory to this array, some of its indices are 0x0
, and some are trying to access memory in a weird manner. For example:
char** arr_docs = (char**)malloc(sizeof(char*));
In gdb
, I'll try to see the memory addresses of many of this array's indexes:
arr_docs[0]
> 0x0
arr_docs[1]
> 0x0
arr_docs[2]
> 0x0
arr_docs[3]
> 0x1fae1 <error: Cannot access memory at address 0x1fae1>
arr_docs[4]
> 0x0
Wait, what?? Why does arr_docs[3]
is trying to access that address?
I have also noticed that when I'm building the array of strings, the program correctly puts the intended string in arr_docs[0]
, but at some point in the loop (In the debugger, it shows that is when i == 4), arr_docs[0] get allocated again! Here's the for-loop code and the behavior arr_docs[0]
shows in the debugger:
void getlinha(char* buf, FILE* arq){
fgets(buf, 50, arq);
int size = strlen(buf);
// final \n replaced by \0
buf[size-1] = '\0';
}
char* temp = (char*)malloc(sizeof(char));
for(i = 0; i < 6; i ){
//char* temp = (char*)malloc(50);
arr_docs[i] = (char*)malloc(sizeof(char));
getlinha(temp, input);
strcpy(arr_docs[i], temp);
}
In the debugger, when i < 4:
> arr_docs[0]: 0x55555555a530 "sigaa 2"
When i == 4 (More specifically, when arr_docs[4] = (char*)malloc(sizeof(char));
):
> arr_docs[0] : 0x55555555a530 "\260\245UUUU"
I'm completely lost.
Update
Following recommendations, I edited the code. Dumped dynamic memory allocation, since I know how many strings to store. Still, some problems arise. The new code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void getlinha(char* buf, FILE* arq){
fgets(buf, 50, arq);
int size = strlen(buf);
// final \n replaced by \0
buf[size-1] = '\0';
}
int main(void){
char* arr_docs[6];
int i;
FILE* input = fopen("file.input", "r");
for(i = 0; i < 6; i ){
getlinha(arr_docs[i], input);
}
}
In this program, fgets
rises Segmentation fault error;
In the debugger, arr_docs[0]
is correctly assigned. But arr_docs[1]
throws the error:
arr_docs[1]
> 0x5555555552bd <__libc_csu_init 77> "H\203\303\001H9\335u\352H\203\304\b[]A\\A]A^A_\303ff.\017\037\204"
fgets(arr_docs[1], 50, input)
> Program received signal SIGSEGV, Segmentation fault.
> __memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:314
Update 2
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void getlinha(char* buf, FILE* arq){
fgets(buf, 50, arq);
int size = strlen(buf);
// final \n replaced by \0
buf[size-1] = '\0';
}
int main(void){
char* arr_docs[6];
int i;
FILE* input = fopen("file.input", "r");
for(i = 0; i < 6; i ){
// assuming the upper bound size of one doc is 50 chars
arr_docs[i] = malloc(50 * sizeof(char));
getlinha(arr_docs[i], input);
}
for(i = 0; i < 6; i ){
free(arr_docs[i]);
arr_docs[i] = NULL;
}
fclose(input);
return 0;
}
CodePudding user response:
There are still some problems in your latest update:
- you do not check for
fopen
failure to open the file - you do not check for memory allocation failure
- you do not check for
fgets()
failure - if a line read by
fgets()
is longer than 49 bytes including the newline, it will be spill to the next array. - you have undefined behavior if
strlen(buf)
returns 0. - you overwrite the last byte of
buf
without checking that it is a newline.
Here is a modified version without dynamic memory allocation that truncates long lines:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// read a line of input
// return -1 and set buf to the empty string at end of file
// otherwise return the line length
// line read is truncated to fit in buf and null terminated if size>0
// truncation can be detected by testing if the return value >= buffer length.
int getlinha(char *buf, int size, FILE *fp) {
int c, i, j;
for (i = j = 0; (c = getchar()) != EOF && c != '\n'; i ) {
if (i 1 < size)
buf[j ] = (char)c;
}
if (size > 0)
buf[j] = '\0';
if (i == 0 && c == EOF)
return -1;
return i;
}
int main(void) {
char arr_docs[6][50];
int i, n;
FILE *input = fopen("file.input", "r");
if (input == NULL) {
fprintf(stderr, "cannot open %s: %s\n", "file.input", strerror(errno));
return 1;
}
for (n = 0; n < 6; n ) {
// assuming the upper bound size of one doc is 50 chars
if (getlinha(arr_docs[n], sizeof arr_docs[n], input) < 0)
break;
}
for (i = 0; i < n; i ) {
printf("%d: %s\n", i 1, arr_docs[i]);
}
fclose(input);
return 0;
}
CodePudding user response:
I suspect that that "0x0" in the first part of your question is not an address (which should be 20 bit) but rather the value stored at that position. With malloc()
you allocate memory as it is, meaning that it's not freed from the values that were stored before being assigned. If you want it to be initialized to 0 instead, use ```calloc()``.