in my C program i'm trying to create an array of structs. For memory allocation im trying to make it dynamic, growing as long as it needs to, unfortunately, everytime it crashes on the realloc.
void readsaveusers(char* filename, User *users) {
FILE *file;
file = fopen(filename, "r");
if (file == NULL) printf("Error reading file\n");
char line[200];
users = malloc(sizeof(struct user *));
for (int i = 0; !feof(file); i++) {
fgets(line, 200, file);
users = realloc(users,sizeof(struct user *) * (i + 1));
char *tok = strtok(line, ";");
strcpy(users[i].username, tok);
tok = strtok(NULL, ";");
strcpy(users[i].name, tok);
tok = strtok(NULL, ";");
users[i].gender = tok[0];
tok = strtok(NULL, "/");
users[i].birth_date.day = atoi(tok);
tok = strtok(NULL, "/");
users[i].birth_date.month = atoi(tok);
tok = strtok(NULL, ";");
users[i].birth_date.year = atoi(tok);
tok = strtok(NULL, "/");
users[i].account_creation.day = atoi(tok);
tok = strtok(NULL, "/");
users[i].account_creation.month = atoi(tok);
tok = strtok(NULL, ";");
users[i].account_creation.year = atoi(tok);
tok = strtok(NULL, ";");
strcpy(users[i].pay_method, tok);
tok = strtok(NULL, ";");
strcpy(users[i].account_status, tok);
}
printf("Saved file\n");
fclose(file);
}
This:
users = realloc(users,sizeof(struct user *) * (i + 1));
^
|
|
doh!
allocates memory for i + 1 pointers to struct user. Assuming the structure itself is larger than a pointer to it (a reasonable assumption), you will overwrite the allocated memory and bomb.
You meant:
void * const np = realloc(users, (i + 1) * sizeof *users);
if (np != NULL)
{
users = np;
}
else
{
fprintf(stderr, "Memory allocation failure, aborting\n");
exit(1);
}
Note use of "new pointer" (np) variable to check if the allocation succeeds. This could be simplified since the original value is not actually needed when doing an exit() to handle the error, but I left it as illustration.
Related
Creating a C program to read a csv file into a double pointer struct but I'm getting a segmentation fault.
This is the struct:
typedef struct vehicle{
char *make;
char *model;
char *color;
char *license_plate;
int year;
}vehicle_t;
I'm using a single pointer and double pointer in this program to get a better understanding of using pointers:
vehicle_t *v = NULL;
vehicle_t **car;
car = calloc(1, sizeof(vehicle_t *));
int num_cars = 0;
while(!fgets(buffer, BUF_SIZE, fp)){
//car = calloc(1, sizeof(vehicle_t *));
*(car+num_cars) = calloc(1, sizeof(vehicle_t));
*((car)+num_cars) = read_csv(fp, v);
num_cars++;
//free(v->year);
free(v->make);
free(v->model);
free(v->color);
free(v->license_plate);
free(v);
}
Here is the function I created to read csv file and return that in car:
vehicle_t *read_csv(FILE *fp, vehicle_t *v){
char buffer[BUF_SIZE] = {0};
v = calloc(1, sizeof(vehicle_t));
fgets(buffer, BUF_SIZE, fp);
char *tok = strtok(buffer, ",");
(v)->year = atoi(tok);
tok = strtok(NULL, ",");
(v)->make = calloc(strlen(tok)+1, sizeof(char));
tok = strtok(NULL, ",");
strcpy((v)->make, tok);
(v)->model = calloc(strlen(tok)+1, sizeof(char));
strcpy((v)->model, tok);
tok = strtok(NULL, ",");
(v)->color = calloc(strlen(tok)+1, sizeof(char));
strcpy((v)->color, tok);
tok = strtok(NULL, ",");
(v)->license_plate = calloc(strlen(tok)+1, sizeof(char));
strcpy((v)->license_plate, tok);
return v;
}
Valgrind is telling me that I'm getting an issue here when I try to print the vehicles from double pointer car:
for(int i= 0; i<=num_cars; i++){
printf("%d %s %s (%s) LIC:%s\n", ((*car)->year), ((*car)->make), ((*car)- >model), ((*car)->color), ((*car)->license_plate));
}
Valgrind error message:
I finish off the code by freeing the car double pointer:
for(int i= 0; i<=num_cars; i++){
//free((*car)->year);
free((*car)->make);
free((*car)->model);
free((*car)->color);
free((*car)->license_plate);
free(*car);
free(car);
}
Not sure as to why I'm getting the segmentation fault. Thought I had a decent handle of pointer usage and appeared like allocated and deallocated memory correctly.
At least these problems:
Wrong pointer
(v)->make = calloc(strlen(tok)+1, sizeof(char));
tok = strtok(NULL, ","); // ????
strcpy((v)->make, tok);
Code is strange. Why allocate to a string length and then change tok before copying?
I'd expect something more like
(v)->make = calloc(strlen(tok)+1, sizeof(char));
strcpy((v)->make, tok);
tok = strtok(NULL, ",");
Research strdup() for alternative code.
Only 1 car, wrong fgets() usage
Code only allocates for 1 car
//vehicle_t **car;
//car = calloc(1, sizeof(vehicle_t *));
//int num_cars = 0;
//while(!fgets(buffer, BUF_SIZE, fp)){
// *(car+num_cars) = calloc(1, sizeof(vehicle_t));
// *((car)+num_cars) = read_csv(fp, v);
int car_n = 1000;
// n cars
vehicle_t **car = calloc(car_n, sizeof *car);
int num_cars = 0;
// v --- no !
while(num_cars < car_n && fgets(buffer, BUF_SIZE, fp)){
car[num_cars] = calloc(1, sizeof car[num_cars][0]);
car[num_cars] = read_csv(fp, v);
bellow is the code:
from some reason the calloc inside the while loop, is failing on the second iteration.
it looks the heap is corupted (not sure) but not clear the root cause.
please also take a look on the comment added there.
appriciate fast response.
#define _CRT_SECURE_NO_WARNINGS
#include <string.h>
#include<stdio.h>
#include <stdlib.h>
struct User_
{
char* id;
char* firstName;
char* lastName;
int age;
char gender[2];
char* userName;
char* password;
char* description;
char hobbies[2];
}typedef User;
void replaceEnterInString(int lengthString, char* string, int maxChars);
int main()
{
char str1[500] = "012345678;danny;cohen;22;M;danny1993;123;1,2,4,8;Nice person";
char str2[500] = "223325222;or;dan;25;M;ordan10;1234;3,5,6,7;Singer and dancer";
int j = 0;
char *token = NULL, arrangingHobbies;
int lengthStr, tempAge, hobby[4], i;
while(j<2)
{
User* newUser = NULL;
here it pass on first time but fail on second time. but only when adding the code that map the token to the newUser. without the mapping - do manage to calloc the user again and again as much as needed
error code: Critical error detected c0000374 - TEST.exe has triggered a breakpoint.
newUser = (User*)calloc(1, sizeof(User));
if (newUser == NULL)
{
printf("error");
exit(1);
}
//start map string to user
if (j == 0)
{
token = strtok(str1, ";");
printf("%s", str1);
}
else {
token = strtok(str2, ";");
printf("%s", str2);
}
//Input ID
newUser->id = (char*)calloc(10, sizeof(char));
if (newUser->id == NULL)
{
printf("error");
exit(1);
}
strcpy(newUser->id, token);
//Input first name
token = strtok(NULL, ";");
lengthStr = strlen(token);
newUser->firstName = (char*)calloc((lengthStr + 1), sizeof(char));
if (newUser->firstName == NULL)
{
printf("error");
exit(1);
}
strcpy(newUser->firstName, token);
//Input last name
token = strtok(NULL, ",;");
lengthStr = strlen(token);
newUser->lastName = (char*)calloc((lengthStr + 1), sizeof(char));
if (newUser->lastName == NULL)
{
printf("error");
exit(1);
}
strcpy(newUser->lastName, token);
//Input Age
token = strtok(NULL, ",;");
tempAge = atoi(token);
newUser->age = tempAge;
//Input gender
token = strtok(NULL, ",;");
newUser->gender[0] = token[0];
//Input User Name
token = strtok(NULL, ",;");
lengthStr = strlen(token);
newUser->userName = (char*)calloc((lengthStr), sizeof(char));
if (newUser->userName == NULL)
{
printf("error");
exit(1);
}
strcpy(newUser->userName, token);
//Input password
token = strtok(NULL, ",;");
lengthStr = strlen(token);
newUser->password = (char*)calloc((lengthStr), sizeof(char));
if (newUser->password == NULL)
{
printf("error");
exit(1);
}
strcpy(newUser->password, token);
//Input hobbies
newUser->hobbies[0] = 0;
for (i = 0; i < 4; ++i)
{
token = strtok(NULL, ",;");
tempAge = atoi(token);
arrangingHobbies = 1;
arrangingHobbies <<= (tempAge - 1);
newUser->hobbies[0] |= arrangingHobbies;
}
//Input description
token = strtok(NULL, ",;");
newUser->description = (char*)calloc((lengthStr), sizeof(char));
if (newUser->description == NULL)
{
printf("error");
exit(1);
}
replaceEnterInString(strlen(token), token, 300);
strcpy(newUser->description, token);
j++;
}
}
void replaceEnterInString(int lengthString, char* string, int maxChars)
{
if (lengthString < maxChars)
{
//remove the /n
string[lengthString - 1] = '\0';
}
}
Maybe there are other issues as well, yet the following code leads to undefined behaviour for sure:
lengthStr = strlen(token);
newUser->userName = (char*)calloc((lengthStr), sizeof(char));
...
strcpy(newUser->userName, token);
In previous similar statements, you correctly wrote ... = (char*)calloc((lengthStr+1), sizeof(char));.
BTW: In C, you usually don't cast the results of malloc, sizeof(char) is always 1 by definition, and there is no need for setting memory to 0 using calloc if you fill the memory with a subsequent strcpy anyway. So you should write...
lengthStr = strlen(token);
newUser->userName = malloc(lengthStr+1);
...
strcpy(newUser->userName, token);
Look through your code for similar issues, please.
This is a sample from my code that im working on. However i have been trying to understand what to input where I wrote "XXXX" in my sorting alghoritm. Any ideas? Am i doing something or right or is it all wrong. The only error that occurs is "XXXX" is undeclared, but i can not figure out the right input.(the struct name and the "if" is somewhat "hard to read", due to my limited skills in creating code on this website)
code:
#define NUM_ITEMS sizeof(items) / sizeof(items[0]) struct vara
{
int nummer;
char namn[100];
float pris;
float volym;
char typ[100];
char stil[100];
char forpackning[20];
char land[20];
char producent[50];
float alkoholhalt;
} items[100]; for (i = 0; i < 100 && fgets(envara, 512, fp); i++)
{
envara[strlen(envara) - 1] = '\0';
oneline = strdup(envara);
tok = strtok(oneline, delim);
items[i].nummer = atoi(tok);
tok = strtok(NULL, delim);
strncpy(items[i].namn, tok, (max(strlen(tok), sizeof(items[0].namn))));
tok = strtok(NULL, delim);
items[i].pris = atof(tok);
tok = strtok(NULL, delim);
items[i].volym = atof(tok);
tok = strtok(NULL, delim);
strncpy(items[i].typ, tok, strlen(tok));
tok = strtok(NULL, delim);
strncpy(items[i].stil, tok, strlen(tok));
tok = strtok(NULL, delim);
strncpy(items[i].forpackning, tok, strlen(tok));
tok = strtok(NULL, delim);
strncpy(items[i].land, tok, min(strlen(tok), sizeof(items->land)));
tok = strtok(NULL, delim);
strncpy(items[i].producent, tok, strlen(tok));
tok = strtok(NULL, delim);
items[i].alkoholhalt = atof(tok);
int nummer_sortering(const void* n1, const void* n2)
{
items *XXXX = n1;
items *XXXX = n2;
return items->nummer - items->nummer;
}
printf("\n\nItems sorted by number:");
qsort(items, NUM_ITEMS, sizeof(items), nummer_sortering);
for (i = 0; i < NUM_ITEMS; i++)
printf("nummer: %d\n"
"namn: %s\n"
"pris: %f\n"
"volym: %f\n"
"typ: %s\n"
"stil: %s\n"
"forpackning: %s\n"
"land: %s\n"
"producent: %s\n"
"alkoholhalt: %f\n\n",
items[i].nummer,
items[i].namn,
items[i].pris,
items[i].volym,
items[i].typ,
items[i].stil,
items[i].forpackning,
items[i].land,
items[i].producent,
items[i].alkoholhalt);
Your comparing function is wrong, the compiler should have given you an error.
What kind of type is items? You cannot declare a variable with the name of
another variable. It should be:
int nummer_sortering(const void* n1, const void* n2)
{
struct vara *item1 = n1;
struct vara *item2 = n2;
return n1->nummer - n2->nummer;
}
Also doing
envara[strlen(envara) - 1] = '\0';
is not correct. If the input is longer than 511 characters,
envara[strlen(envara) - 1] won't be a newline. The best way to remove the
newline (if any) is:
envara[strcspn(envara, "\n")] = '\0';
Also
strncpy(items[i].producent, tok, strlen(tok));
You are using strncpy wrong. The point of strncpy is that you limit the
numbers of bytes to copy based on the size of the destination. You are limiting the
number bytes based on the length of the source. If it's longer than what
producent can hold, then you will have a buffer overflow. Also remember that
strncpy does not necessarily write the '\0'-terminating byte. You have to
make sure that for yourself:
size_t max = sizeof items[i].producent;
strncpy(items[i].producent, tok, max);
items[i].producent[max - 1] = '\0';
Also you should check that strtok doesn't return NULL. If it does, that
means that the line does not have the format you're looking for. In this case
you can dismiss the line and read the next one.
I'm kinda stuck when trying to sort int number. I'm trying to display the items based on its number (nummer). The int main and #includes are in the program, just couldn't copy them. Is there a preferred why to handle this issue and can someone explain it, I been trying for a while now haha. Thanks
char *oneline, *tok;
char envara[512];
char delim[] = ",";
FILE *fp;
int i;
struct vara
{
int nummer;
char namn[100];
float pris;
float volym;
char typ[100];
char stil[100];
char forpackning[20];
char land[20];
char producent[50];
float alkoholhalt;
} items[100];
if ((fp = fopen("varor.csv", "r")) == NULL)
{
fprintf(stderr, "Filen varor.csv gick inte att öppna\n");
exit(-1);
}
for (i = 0; i < 100 && fgets(envara, 512, fp); i++)
{
envara[strlen(envara) - 1] = '\0';
oneline = strdup(envara);
tok = strtok(oneline, delim);
items[i].nummer = atoi(tok);
tok = strtok(NULL, delim);
strncpy(items[i].namn, tok, (max(strlen(tok), sizeof(items[0].namn))));
tok = strtok(NULL, delim);
items[i].pris = atof(tok);
tok = strtok(NULL, delim);
items[i].volym = atof(tok);
tok = strtok(NULL, delim);
strncpy(items[i].typ, tok, strlen(tok));
tok = strtok(NULL, delim);
strncpy(items[i].stil, tok, strlen(tok));
tok = strtok(NULL, delim);
strncpy(items[i].forpackning, tok, strlen(tok));
tok = strtok(NULL, delim);
strncpy(items[i].land, tok, min(strlen(tok), sizeof(items->land)));
tok = strtok(NULL, delim);
strncpy(items[i].producent, tok, strlen(tok));
tok = strtok(NULL, delim);
items[i].alkoholhalt = atof(tok);
printf("nummer: %d\n"
"namn: %s\n"
"pris: %f\n"
"volym: %f\n"
"typ: %s\n"
"stil: %s\n"
"forpackning: %s\n"
"land: %s\n"
"producent: %s\n"
"alkoholhalt: %f\n\n",
items[i].nummer,
items[i].namn,
items[i].pris,
items[i].volym,
items[i].typ,
items[i].stil,
items[i].forpackning,
items[i].land,
items[i].producent,
items[i].alkoholhalt
);
free(oneline);
}
fclose(fp);
}
int cmpVara(const struct vara *left, const struct vara *right)
{
// quick & dirty way to compare two ints.
return (long) left.nummer - (long) right.nummer;
}
// :
// :
qsort(items, i, sizeof(vara), cmpVara);
i is the number of elements in items.
I've used strdup() in the past in the same way that I am using it here. I am passing token2 into strdup which is of type char * with a valid pointer in it, yet when I try to run the line "name = strdup(token2);" my program segfaults and I am quite unsure as to why. If anyone would be able to help me it would be greatly appreciated. I also realize that my code does not return a proper type yet, I am still working on writing all of it.
struct YelpDataBST* create_business_bst(const char* businesses_path, const char* reviews_path){
if(fopen(businesses_path,"r") == NULL || fopen(reviews_path,"r") == NULL)
return NULL;
FILE* fp_bp = fopen(businesses_path, "r");
FILE* fp_rp = fopen(reviews_path, "r");
struct YelpDataBST* yelp = malloc(sizeof(struct YelpDataBST*));
int ID = -1;
int tempID;
long int addressOffset;
long int reviewOffset;
char line[2000];
char line2[2000];
char temp[2000];
char temp2[2000];
char* token;
char* token2;
char* name;
int len;
BusList* busNode = NULL;
BusList* busList = NULL;
BusTree* busTreeNode = NULL;
BusTree* busTree = NULL;
ID = -1;
tempID = 0;
fgets(line,2000,fp_rp);
fgets(line2,2000,fp_bp);
fseek(fp_rp,0, SEEK_SET);
fseek(fp_bp,0,SEEK_SET);
int ct = 0;
while(!feof(fp_rp)){
len = strlen(line);
token = strtok(line, "\t");
//printf("line: %s\n", line);
token2 = strtok(line2, "\t");
tempID = atoi((char*)strdup(token));
if(ct == 0){
tempID = 1;
ct++;
}
if((ID != tempID || (ID < 0)) && tempID != 0){
if(tempID == 1)
tempID = 0;
token2 = strtok(NULL, "\t");
//name = strdup(token2);
reviewOffset = ftell(fp_rp);
if(tempID != 0)
reviewOffset -= len;
addressOffset = ftell(fp_bp);
ID = atoi((char*)strdup(token));
busList = BusNode_insert(busList, addressOffset, reviewOffset); //replace with create node for tree
token2 = strtok(NULL, "\t");
token2 = strtok(NULL, "\t");
token2 = strtok(NULL, "\t");
token2 = strtok(NULL, "\t");
token2 = strtok(NULL, "\t");
token2 = strtok(NULL, "\n");
fgets(line2,2000,fp_bp);
}
token = strtok(NULL, "\t");
token = strtok(NULL, "\t");
token = strtok(NULL, "\t");
token = strtok(NULL, "\t");
token = strtok(NULL, "\n");
fgets(line,2000,fp_rp);
}
//BusList_print(busList);
}
strdup(token) segfaulting is most likely explained by token being NULL. (You don't need to strdup here anyway). Change that piece of code to:
if ( token == NULL )
{
fprintf(stderr, "Invalid data in file.\n");
exit(EXIT_FAILURE); // or some other error handling
}
tempID = atoi(token);
However a greater problem with the surrounding code is that you are trying to use strtok twice at once. It maintains internal state and you can only have one strtok "in progress" at any one time. The second one cancels the first one. You'll have to redesign that section of code.
Also, while(!feof(fp_rp)) is wrong, and your yelp mallocs the wrong number of bytes (although in the code posted you never actually try to store anything in that storage, so it would not cause an error just yet).