Array of structs only outputs last entry read in C - arrays

I am reading from a file and inserting the read entries into a struct as shown:
typedef struct card
{
unsigned int id;
char* name;
char* cost;
unsigned int converted_cost;
char* type;
char* text;
char* stats;
enum rarity rarity;
} card_t;
int main(int argc, char **argv) {
FILE *input_file;
input_file = fopen(argv[1], "r");
card_t **cards = NULL;
int cardsaccum = 0;
char *buf = NULL;
char *name_duplicate;
size_t bufsiz = 0;
ssize_t result = getline(&buf, &bufsiz, input_file);
while (result > 0)
{
// COPIES BUFFER TO SAVE THE MEMORY ADDRESS
char *stringp = buf;
// ALLOCATES MEMORY
cards = realloc(cards, sizeof(card_t *) * num_entries);
cards[cardsaccum] = malloc(sizeof(card_t));
name_duplicate = strsep(&stringp, "\"");;
cards[cardsaccum]->name = name_duplicate;
cardsaccum++;
num_entries++;
result = getline(&buf, &bufsiz, input_file);
}
for(int i = 0; i < cardsaccum; i++)
{
printf("%s\n",cards[i]->name);
}
// FREEING MEMORY
for(i = 0; i < cardsaccum;i++)
{
free(cards[i]);
}
free(cards);
free(buf);
fclose(input_file);
return 0;
}
The file should be reading the names of Bob, Marley, Frank. However, my output is only printing the last entry read:
Frank
Frank
Frank
Do I have a problem with my allocation of memory or is it something else? Any help is appreciated!

The function getline() calls realloc() internally. As the result, all previous pointers originating from buf like strsep(&stringp, "\"") get invalidated or overwritten.
Note that previous cards[X]->name point to some location inside previous buf.
Make a copy of a string before processing a next line:
cards[cardsaccum]->name = strdup(name_duplicate);
other solution is setting buf back to NULL just before calling getline.

Related

Read a CSV file into a dynamic array of structs C

I'm fairly new to C. I'm trying to read a .CSV file, then parse each line, then store the data in a dynamic array of pointers to structs. Unfortunately I've gone wrong somewhere in my implementation which is resulting in an infinite loop.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct dataSet {
char ID;
char postcode;
int population;
char contact;
double x;
double y;
}data;
int main(int argc, char* argv[]) {
char line[100] = "";
int count = 0;
int each = 0;
data *allData = NULL;
data *temp = NULL;
FILE *file = fopen("dataset.csv", "r");
if (file == NULL)
{
printf("Error! File null");
return 1;
}
while (fgets(line, sizeof line, file))
{
if(NULL == (temp = realloc(allData, sizeof(*allData) * (count + 1))))
{
fprintf(stderr, "realloc problem\n");
fclose(file);
free(allData);
return 0;
}
allData = temp;
if (6 == scanf(line, "%s, %s, %d, %s, %lf, %lf",
&allData[count].ID,
&allData[count].postcode,
&allData[count].population,
&allData[count].contact,
&allData[count].x,
&allData[count].y)) {
count++;
}
else {
printf("Problem with data\n");
}
}
fclose(file);
for (each = 0; each < count; each++)
{
printf("%s, %s, %d, %s, %lf, %lf\n",
&allData[count].ID,
&allData[count].postcode,
&allData[count].population,
&allData[count].contact,
&allData[count].x,
&allData[count].y);
}
free(allData);
return 0;
}
Any help or tips would be greatly appreciated.
[s]scanf() is a nasty function. You don't have enough control once it fails. Problem is: there are too many conditions: the input can be incorrect, or the destination is not large enough. Even reading complete lines with fgets(), and parsing them afterwards, will only allow you to skip complete lines; also: the line buffer is mostly fixed sized, and fgets() could read incomplete lines. A way to keep complete control is to read character-based. This might imply a Finite State machine.
A simpler reader (using a zero-state machine) could be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct omg {
char o;
int m;
char g[11];
};
struct wtf {
unsigned size;
unsigned used;
struct omg *array;
};
#define INITIAL_SIZE 7
struct wtf read_stuff(char *name)
{
FILE *fp;
unsigned icol,irec,len;
char buff[123];
struct wtf this = {0,0,NULL};
fp = fopen(name, "rb" );
if (!fp) return this;
for (icol=irec=len=0; ; ) {
int ch;
if (this.used >= this.size) {
size_t newsize;
struct omg *tmp;
newsize = this.size? this.size*2: INITIAL_SIZE;
fprintf(stderr, "Realloc(%zu)\n", newsize);
tmp = realloc(this.array, sizeof *this.array * newsize);
this.array = tmp;
this.size = newsize;
}
ch = getc(fp);
switch(ch) {
case '\r' : continue;
/* End of field or record: terminate buffer */
#if 0
case ',' :
#else
case '\t' :
#endif
case '\n' :
buff[len] = 0;
break;
case EOF :
goto done;
/* Normal character: assign to buffer
** You may want to report too long fields here
*/
default:
if (len >= sizeof buff -2) continue;
buff[len++] = ch;
continue;
}
/* When we arrive here, we have a new field. Let's process it ...*/
switch (icol) {
case 0: /* Assign first field here from buff[], (dont forget to check len!) */
this.array[this.used].o = buff[0];
break;
case 1: /* Assign second field from buff[], this may need some additional checks
** You may want to avoid sscanf() here ...
*/
sscanf(buff, "%d", &this.array[this.used].m );
break;
case 2: /* Assign third field from buff[] */
if (len >= sizeof this.array[this.used].g)
len = sizeof this.array[this.used].g -1;
memcpy (this.array[this.used].g, buff, len);
this.array[this.used].g[len] = 0;
break;
default: /* Ignore excess fields
** You may want to report hem.
*/
break;
}
/* Do some bookkeeping */
len = 0;
if(ch == '\n') {
/* You may want to check if icol==2, here */
icol=0; irec++; this.used++;
}
else icol++;
}
done:
fclose(fp);
/* You could do a final realloc() here */
return this;
}
int main(int argc, char **argv)
{
struct wtf result;
unsigned idx;
result = read_stuff(argv[1] );
fprintf(stderr, "Result=%u/%u\n", result.used,result.size);
for (idx=0; idx < result.used; idx++) {
printf("%c %d %s\n"
, result.array[idx].o
, result.array[idx].m
, result.array[idx].g);
if (idx >= 10) break;
}
return 0;
}
You ask for tips...
1 - your struct is wrong if your plan was to use dynamic memory. The char members should be pointers to char, ( char * not char ) as shown below. But to reduce complexity, use char arrays instead of forcing dynamic allocation for struct members: i.e. do not use this:
typedef struct dataSet {
char *ID;
char *postcode;
int population;
char *contact;
double x;
double y;
}data;
Rather use this:
typedef struct dataSet {
char ID[80];
char postcode[11];
int population;
char contact[80];
double x;
double y;
}data;
If the lengths are not right, then make them bigger, but this will reduce calls to calloc() and free().
2 - suggested steps:
Count lines in file. (example here). This will essentially open the file, count the lines and close the file.
Use the count to allocate memory for that number of instances of data (i.e. data *records = malloc(sizeof(*records)*countOfLines); )
Open the file again. If file != NULL, then...
Begin to read file line by line in a loop, such as the fgets(...) loop you have.
In this loop, suggest replacing scanf() with a series of calls to strtok() making the appropriate conversion one-by-one. Its a few more lines of code, but is easier in the long run to see what parsing problems you might run into.
The following pseudo code illustrates...
data *record = malloc(CountOfLines*sizeof(*record));
if(record)
{
int i = 0;
while(fgets(line, sizeof line, file))
{
tok = strtok(line, ",");
if(tok)
{ //convert string
strncpy(record[i].ID, tok, sizeof(record[i].ID) - 1);
tok = strtok(NULL, ",");
if(tok)
{//convert string
strncpy(record[i].postcode, tok, sizeof(record[i].postcode) - 1);
tok = strtok(NULL, ",");
if(tok)
{//convert int
record[i].population = atoi(tok);
//and so on ...

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));
}
}

Program Hangs when printing contents of array in loop

Hi, i am using the MinGW C Compiler with Code::Blocks and my code hangs when trying to print the contents of an array (well it is a custom data type).
For a quick summary: the program is taking the contents of a txt file and splits
the string up into individual words using a custom data type called a stringArray (the name explains itself). It then should print each word of the file to the user.
The problem is, it hangs and gives me the usual "[PROGRAM NAME HERE] is not responding." After pressing cancel it gives me this result:
Process returned -1073741819 (0xC0000005) execution time : 3.861 s
Press any key to continue.
I am a sort of beginner.
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
typedef struct stringArray
{
char *string;
}stringArray;
const char delim[2] = " ";
int string_to_array(char *filecontents)
{
char *token;
token = strtok(filecontents, delim);
int i;
int dirtyContentsLength;
stringArray newContents[100];
for(i = 0; i < 100; i++)
{
newContents[i].string = "";
}
i = 0;
while (token != NULL)
{
newContents[i].string = token;
i++;
token = strtok(NULL, delim);
}
return newContents;
}
int open_file(char filename[30])
{
char *file_contents;
long input_file_size;
FILE *input_file = fopen(filename, "rb");
fseek(input_file, 0, SEEK_END);
input_file_size = ftell(input_file);
rewind(input_file);
file_contents = malloc(input_file_size * (sizeof(char)));
fread(file_contents, sizeof(char), input_file_size, input_file);
fclose(input_file);
return file_contents;
}
int lex(char filecontents[30])
{
char *tok = "";
int state = 0;
char *string = "";
}
int main(int argc, char *argv[] )
{
const char *cleanContents;
char *messyContents;
char input[30];
printf("What is the filename? ");
scanf("%s", input);
messyContents = open_file(input);
cleanContents = string_to_array(messyContents);
int contentsLength = sizeof(cleanContents) / sizeof(cleanContents[0]);
int i;
for(i = 0; i < contentsLength; i++)
{
printf("%s\n", cleanContents[i]);
}
printf("Done");
return 0;
}
You have multiple problems with your code:
string_to_array() is declared to return an int, but in reality it is returning a stringArray
Same with open_file() function, Declared to return an int, but actually returning a char*
string_to_array is returning an element that was declared locally. This means that once the function is returned, that memory is no longer valid, but it has passed it on to the caller.
Your structure name is misleading. A char* is a character array (a string). Thus the name charArray would be more appropriate. For the structure to be a string array it has to be a char**, ie an array of character arrays (array of strings)
Int the printf() in the main() function you are not passing the string (thus a compilation warning is generated)
You are not initializing memory to all 0. This is ideal as otherwise the memory will contain random data which will be interpreted as a string untill the first null terminator (\0 encountered)
The following code is a modified working version of what you are trying to achieve with comments about each change:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
typedef struct stringArray
{
char *string;
}stringArray;
const char delim[2] = " ";
// Now string_to_array takes the memory location to write output to as a first parameter so that the
// memory will reside in the callers scope (refer to problem 3 above)
// Additionally return type was now set to void (refer to problem 1)
void string_to_array(stringArray newContents[100], char *filecontents)
{
char *token;
token = strtok(filecontents, delim);
int i;
int dirtyContentsLength;
for(i = 0; i < 100; i++)
{
newContents[i].string = "";
}
i = 0;
while (token != NULL)
{
newContents[i].string = token;
i++;
token = strtok(NULL, delim);
}
// return now was removed. result written directly in memory passed as parameter by the caller.
}
// open_file changed to return a char* (refer to problem 2)
char* open_file(char filename[30])
{
char *file_contents;
long input_file_size;
FILE *input_file = fopen(filename, "rb");
fseek(input_file, 0, SEEK_END);
input_file_size = ftell(input_file);
rewind(input_file);
file_contents = malloc(input_file_size * (sizeof(char)));
fread(file_contents, sizeof(char), input_file_size, input_file);
fclose(input_file);
return file_contents;
}
int lex(char filecontents[30])
{
char *tok = "";
int state = 0;
char *string = "";
}
int main(int argc, char *argv[] )
{
stringArray cleanContents[100];
// Initializing memory to all 0s (refer to problem 6)
memset(cleanContents, 0 ,sizeof(cleanContents));
char *messyContents;
char input[30];
printf("What is the filename? ");
scanf("%s", input);
messyContents = open_file(input);
string_to_array(cleanContents, messyContents);
int contentsLength = sizeof(cleanContents) / sizeof(cleanContents[0]);
int i;
for(i = 0; i < contentsLength; i++)
{
// Checking that at least one character is present in the string before printing it...
if (cleanContents[i].string[0])
{
// Printing the string within the 'stringArray'. (refer to problem 5)
printf("%s\n", cleanContents[i].string);
}
}
printf("Done\n");
return 0;
}

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

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.

Resources