Saving lines from a text file to a dynamic array of strings - c

This is my first year with C, so I am a bit lost.
I have the function:
void read(char** lines){
FILE *fpointer = fopen("input1.txt","r");
char *p_input = (char*) malloc(sizeof(char)*200);
int i,len;
i=0;
lines = malloc(sizeof(char*));
while( fgets(p_input,200,fpointer) ){
len = strlen(p_input);
char temp[len];
strcpy(temp,p_input);
lines[i] = temp;
i++;
}
}
and in main:
int main(){
char **lines;
read(lines);
return 0;}
And when I try printing something from the array, I face errors and the code stops, something like:
printf("%s\n",lines[0]);
Can you please tell me what is wrong.

lines = malloc(sizeof(char*));
...
lines[i] = temp;
This is wrong, you don't have enough space for an array of pointer to chars (you need to know the number of lines to reserve)
Change to something like
char **read(void) {
size_t n = file_lines;
char **lines = malloc(sizeof(char*) * n);
...
return lines;
}
int main(void) {
char **lines;
lines = read();
return 0;
}
If you dont know the number of lines before-hand you can use realloc on each iteration of the while loop.
char **read(void) {
...
char **lines = NULL;
char **tmp;
...
while (fgets(p_input,200,fpointer)) {
...
tmp = realloc(lines, sizeof(char *) * (i + 1));
if (tmp != NULL) {
lines = tmp;
} else {
return NULL;
}
lines[i] = temp;
i++;
}
return lines;
}
int main(void) {
char **lines;
lines = read();
if (lines == NULL) {
perror("read");
exit(EXIT_FAILURE);
}
return 0;
}

In the realloc() example, if you use that together with the original code:
while( fgets(p_input,200,fpointer) ){
len = strlen(p_input);
char temp[len];
...
That's a mistake because temp[len] is declared inside the loop which means it'll be destroyed upon exiting the while loop. So your entries inside your realloc() array will point to nothing.
You would want to use malloc() inside the while loop to generate a separate space for each entry rather than declare a static array like the above.
And to tidy up at the end remember to free() the space at the end before your program exits completely.

Related

How do I dynamically create an array of strings using scanf in C

So I have been searching through stack overflow for a little over an hour and I don't understand why this function is giving me a segmentation error. I want to create a string array, scan strings in through scanf, dynamically change the size of each string and return the string array. Can anyone help? Thank you.
char** readScores(int* count) {
int c = 0;
char** arr =(char**)malloc(100 * sizeof(char*));
char* in;
while(scanf("%s", in) != EOF) {
arr[c] = (char*)malloc(strlen(in)+1);
strcpy(arr[c], in);
}
*count = c;
return arr;
}
char* in;
while(scanf("%s", in) != EOF) {
This tells the computer to read from standard input into the char buffer that in points to.
Which does not exist, because in is not initialised to anything (let alone a valid buffer).
I would not use scanf only fgets.
You need to allocate memory dor the arr and for every line referenced by elements of arr
char** readScores(size_t *count) {
size_t lines = 0;
char** arr = NULL, **tmp;
char* in = malloc(MAXLINE), *result;
size_t len;
if(in)
{
do{
result = fgets(in, MAXLINE, stdin);
if(result)
{
len = strlen(in);
tmp = realloc(arr, sizeof(*tmp) * (lines + 1));
if(tmp)
{
arr = tmp;
len = strlen(in);
arr[lines] = malloc(len + (len == 0));
if(arr[lines])
{
if(len) memcpy(arr[lines], in, len - 1);
arr[lines++][len] = 0;
}
else
{
// error handling
}
}
else
{
// error handling
}
}
}while(result);
free(in);
}
*count = lines;
return arr;
}

pointer of pointer of char in c, assignment crashes

I have a pointer of pointer to store lines I read from a file;
char **lines;
And I'm assigning them like this :
line_no=0;
*(&lines[line_no++])=buffer;
But it crashes why ?
According to my logic the & should give the pointer of zeroth index, then *var=value, that's how to store value in pointer. Isn't it ?
Here is my current complete code :
void read_file(char const *name,int len)
{
int line_no=0;
FILE* file;
int buffer_length = 1024;
char buffer[buffer_length];
file = fopen(name, "r");
while(fgets(buffer, buffer_length, file)) {
printf("---%s", buffer);
++line_no;
if(line_no==0)
{
lines = (char**)malloc(sizeof(*lines) * line_no);
}
else
{
lines = (char**)realloc(lines,sizeof(*lines) * line_no);
}
lines[line_no-1] = (char*)malloc(sizeof(buffer));
lines[line_no-1]=buffer;
printf("-------%s--------\n", *lines[line_no-1]);
}
fclose(file);
}
You have just a pointer, nothing more. You need to allocate memory using malloc().
Actually, you need first to allocate memory for pointers, then allocate memory for strings.
N lines, each M characters long:
char** lines = malloc(sizeof(*lines) * N);
for (int i = 0; i < N; ++i) {
lines[i] = malloc(sizeof(*(lines[i])) * M);
}
You are also taking an address and then immediately dereference it - something like*(&foo) makes little to no sense.
For updated code
Oh, there is so much wrong with that code...
You need to include stdlib.h to use malloc()
lines is undeclared. The char** lines is missing before loop
if in loop checks whether line_no is 0. If it is, then it allocates lines. The problem is, variable line_no is 0 - sizeof(*lines) times 0 is still zero. It allocates no memory.
But! There is ++line_no at the beginning of the loop, therefore line_no is never 0, so malloc() isn't called at all.
lines[line_no-1] = buffer; - it doesn't copy from buffer to lines[line_no-1], it just assigns pointers. To copy strings in C you need to use strcpy()
fgets() adds new line character at the end of buffer - you probably want to remove it: buffer[strcspn(buffer, "\n")] = '\0';
Argument len is never used.
char buffer[buffer_length]; - don't use VLA
It would be better to increment line_no at the end of the loop instead of constantly calculating line_no-1
In C, casting result of malloc() isn't mandatory
There is no check, if opening file failed
You aren't freeing the memory
Considering all of this, I quickly "corrected" it to such state:
void read_file(char const* name)
{
FILE* file = fopen(name, "r");
if (file == NULL) {
return;
}
int buffer_length = 1024;
char buffer[1024];
char** lines = malloc(0);
int line_no = 0;
while (fgets(buffer, buffer_length, file)) {
buffer[strcspn(buffer, "\n")] = '\0';
printf("---%s\n", buffer);
lines = realloc(lines, sizeof (*lines) * (line_no+1));
lines[line_no] = malloc(sizeof (*lines[line_no]) * buffer_length);
strcpy(lines[line_no], buffer);
printf("-------%s--------\n", lines[line_no]);
++line_no;
}
fclose(file);
for (int i = 0; i < line_no; ++i) {
free(lines[i]);
}
free(lines);
}
Ok, you have a couple of errors here:
lines array is not declared
Your allocation is wrong
I don't understand this line, it is pointless to allocate something multiplying it by zero
if( line_no == 0 )
{
lines = (char**)malloc(sizeof(*lines) * line_no);
}
You shouldn't allocate array with just one element and constantly reallocate it. It is a bad practice, time-consuming, and can lead to some bigger problems later.
I recommend you to check this Do I cast the result of malloc? for malloc casting.
You could write something like this:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void read_file(char const *name)
{
int line_no = 0, arr_size = 10;
int buffer_length = 1024;
char buffer[buffer_length];
char **lines;
FILE* file;
lines = malloc(sizeof(char*) * 10);
file = fopen(name, "r");
while(fgets(buffer, buffer_length, file)) {
buffer[strlen(buffer)-1] = '\0';
printf("---%s", buffer);
++line_no;
if(line_no == arr_size)
{
arr_size += 10;
lines = realloc(lines, sizeof(char*) * arr_size);
}
lines[line_no-1] = malloc(sizeof(buffer));
lines[line_no-1] = buffer;
printf("-------%s--------\n", lines[line_no-1]);
}
fclose(file);
}
PS, fgets() also takes the '\n' char at the end, in order to prevent this you can write the following line: buffer[strlen(buffer)-1] = '\0';

How to avoid buffer overflow with C struct array of strings

I'm running into buffer overflows when reading a file in C and copying character arrays. There are three potentially offending pieces of code and I can't figure out where I'm going wrong.
The first reads a file and populates it into a hashmap:
bool load_file(const char* in_file, hmap hashtable[]) {
for(int x = 0; x < HASH_SIZE; x++) {
hashtable[x] = NULL;
}
FILE *fptr = fopen(in_file, "r");
char c[LENGTH] = "";
c[0] = '\0';
while (fgets(c, sizeof(c)-1, fptr) != NULL) {
node *n = malloc(sizeof(node));
hmap new_node = n;
new_node->next = NULL;
strncpy(new_node->content, c, LENGTH-1);
// do stuff to put it into the hashtable
}
fclose(fptr);
return true;
}
The second checks whether given content is in the hashmap:
bool check_content(const char* content, hmap hashtable[]) {
char c_content[LENGTH] = "";
strncpy(c_content, content, LENGTH-1);
// do stuff to check if it's in the hashmap
return false;
}
and the third parses a given file and checks whether its content is in the hashmap:
int check_file(FILE* fp, hmap hashtable[], char * not_found[]) {
int num_not_found = 0;
char c[1000] = "";
while (fgets(c, sizeof(c)-1, fp) != NULL) {
char * pch;
char curToken[LENGTH] = "";
pch = strtok (c," ");
strncpy(curToken, pch, LENGTH-1);
curToken[LENGTH]=0;
if(!check_content(curToken, hashtable)) {
not_found[num_not_found] = malloc(LENGTH*sizeof(not_found[num_not_found]));
strncpy(not_found[num_not_found], curToken, LENGTH-1);
num_not_found++;
}
}
fclose(fp);
return num_not_found;
}
Finally, main calls these and frees mallocs:
int main (int argc, char *argv[])
{
hmap hashtable[HASH_SIZE];
load_file(argv[2], hashtable);
FILE *fptr = fopen(argv[1], "r");
char * not_found[MAX_ENTRIES];
int num_not_found = check_file(fptr, hashtable, not_found);
for(int x=0; x<num_not_found; x++) {
free(not_found[x]);
}
for(int y=0; hashtable[y] != NULL; y++) {
free(hashtable[y]);
}
return 0;
}
My question is this: for each of the three code snippets, what have I done that causes buffer overflows? Many thanks in advance!
I finally got rid of the buffer overflow problems mostly by following David's advice in the comments, plus figuring out that I had one more malloc than I needed. The fixes were:
new_node->next needed a malloc
The malloc for new_node->next should happen only if it's actually going to be used.
not_found[num_not_found] = malloc(LENGTH*sizeof(not_found[num_not_found])); was wrong and should have been notfound[num_not_found] = malloc(sizeof(char) * (strlen(pch)+1)) (assuming pch wasn't null terminated). (Side note, for whatever reason, on my computer, malloc(sizeof(char) * strlen(pch)+1) is not the same as malloc(strlen(pch)+1))
The return of every malloc really does have to be validated.

Realloc on an array of structs, address boundary error when indexing

I have some code where I'm trying to read lines in from a file and store some information from each line in a struct. Since I don't know how long the file will be, I'm dynamically adjusting the array of structs using realloc.
My issue is that my code seems to work fine for the first 3 (technically 6) lines, and then I receive SIGSEGV (address boundary error). gdb says that this happens when trying to index the array (array[i]->string = (char*) _tmp).
typedef struct {
char* string;
int len;
} buffer;
int read_into_array(char *filename, buffer** array) {
int n;
size_t size;
char* buf = NULL;
FILE *file = fopen(filename, "r");
int i = 0;
while (1) {
buffer *tmp = (buffer*)realloc(*array, sizeof(buffer) * (i + 1));
if (!tmp)
printf("Failed realloc\n");
*array = tmp;
// First line is ignored, second line is taken as data.
getline(&buf, &size, file);
n = getline(&buf, &size, file);
if (n > 0) {
void* _tmp = malloc(sizeof(char) * n);
if (!_tmp)
printf("Failed malloc\n");
array[i]->string = (char*) _tmp;
array[i]->len = n-1;
strncpy(array[i]->string, buf, n-1);
}
i++;
if (feof(file)) {
printf("saw end of file, leaving.\n");
break;
}
}
return i;
}
int main(int argc, char* argv[]) {
char *filename = argv[1];
buffer *array = (buffer*) calloc(1, sizeof(buffer));
int num = read_into_array(filename, &array);
}
Apologies for the somewhat poor formatting, I've been trying to figure this out for a while.
Since it seems to work for the first few lines, my assumption is that I'm going wrong somewhere in the realloc calculation. My other guess is that I'm somehow using/reading the file incorrectly.
Thanks for any help. For posterity, the file looks something like this https://hastebin.com/vinidiyita.sm (the real file is thousands of lines long).
when you do *array=tmp you're allocating memory for array[0]
then you're using array[i] that should be a pointer to a buffer, but points to garbage or 0
You're confusing two ways to use data.
The first is by using arrays - there's the non-dynamic:
buffer array[x] = {0};
int num = read_into_array(filename, &array);
then you can use array[i]
and there's the dynamic type:
buffer **array = calloc(initial_len*sizeof(buffer *));
int num = read_into_array(filename, array, initial_len);
read_into_array(char *filename, buffer **&array, int initial_len)
{
int len = initial_len;
...
while()
{
...
if(i>len)
{
array = realloc(array, sizeof(buffer*) * (i + 1));
len = i;
}
array[i] = calloc(sizeof(buffer));
}
}

Parsing and data overwriting issues in C using custom strtok

I'm reading in a .csv file, which I then need to parse into tokens. I tried using strtok(), but that unfortunately cannot return null fields (which my data is fulll of). So I went with a home-made version of strtok that I found, strtok_single, which returns the correct values that I need.
The data is input into my array correctly; but there is something wrong because before the initilization loops finish, the data gets overwritten. I've tried print statements and analyzing the problem but I just can't figure out what's wrong. Any insight at all would be helpful.
Here is the homemade strtok function I'm using:
char* strtok_single(char* str, char const* delims) {
static char* src = NULL;
char* p, *ret = 0;
if (str != NULL)
src = str;
if (src == NULL)
return NULL;
if ((p = strpbrk(src, delims)) != NULL) {
*p = 0;
ret = src;
src = ++p;
}
return ret;
}
Here is my code:
int main() {
int numLines = 0;
int ch, i, j;
char tmp[1024];
char* field;
char line[1024];
FILE* fp = fopen("filename.csv", "r");
// count number of lines in file
while ((ch = fgetc(fp)) != EOF) {
if (ch == '\n')
numLines++;
}
fclose(fp);
// Allocate memory for each line in file
char*** activity = malloc(numLines * sizeof(char**));
for (i = 0; i < numLines; i++) {
activity[i] = malloc(42 * sizeof(char*));
for (j = 0; j < 42; j++) {
activity[i][j] = malloc(100 * sizeof(char));
}
}
// read activity file and initilize activity matrix
FILE* stream = fopen("filename.csv", "r");
i = 0;
while (fgets(line, 1024, stream)) {
j = 0;
int newlineLoc = strcspn(line, "\n");
line[newlineLoc] = ',';
strcpy(tmp, line);
field = strtok_single(tmp, ",");
while (field != NULL) {
for (j = 0; j < 42; j++) {
activity[i][j] = field;
field = strtok_single(NULL, ",");
// when I print activity[i][j] here, the values are correct
}
// when I print activity[i][j] here, the values are correct for the
// first iteration
// and then get overwritten by partial data from the next line
}
i++;
} // close while
fclose(stream);
// by the time I get to here my matrix is full of garbage
// some more code that prints the array and frees memory
} // close main
activity[i][j] = field;
When the loops finish, each activity[i][j] points to somewhere in tmp, which is overwritten in each loop. Instead, since you pre-allocate space in each activity[i][j], you should just copy the contents of the string to that:
strcpy(activity[i][j], field);
Being careful of buffer overflow (i.e. if field is more than 99 characters).
Also, the sizeof(char) is superfluous since it's always 1 by definition.
Your line "activity[i][j] = field;" is backwards - you want the pointer assigned to the malloc'd memory.

Resources