C, allocating memory for strings inside array of structs - c

I've looked enough about this problem on this site and still haven't found a solution.
I have an array of struct, and I want to read from a file some records and store them in the structs. The problem is the allocation of the memory.
This is the struct I use:
struct Rec{
int mat;
char *nome;
char *cognome;
};
typedef struct Rec* Record;
This is the readFromFile function:
void readFromFile(char* fileName, Record** data, int* pn)
{
char line[LINE_LENGTH];
int n, i;
char* token;
printf("\n\nReading file %s\n", fileName);
FILE* fin = fopen(fileName, "r");
if (fin == NULL)
{ printf("Error readinf file\n");getch();
goto end;
}
n = 0; // first read to know the number of lines
while (fgets(line, LINE_LENGTH, fin) != NULL) n++;
n = (n < MAX_LENGTH ? n : MAX_LENGTH);
printf("N: %d\n", n);
*pn = n;
//Then I allocate the memory for the n lines I previously read
*data = (Record*)malloc(n * sizeof(Record));
if(*data == NULL){
printf("Problem allocating memory\n");
exit(0);
}
i = 0;
for(i = 0; i < n; i++){
(*data)[i].nome = malloc(sizeof(char) * MAX_LENGTH + 1);
if((*data)[i]->nome == NULL){
printf("Problem allocating memory\n");
exit(1);
}
//Here comes the problem, the allocation of the second string fails and the program exit
(*data)[i]->cognome = malloc((sizeof(char) * MAX_LENGTH + 1));
if((*data)[i]->cognome == NULL){
printf("Problem allocating memory\n");
exit(2);
}
}
rewind(fin);
n = 0;
while (fgets(line, LINE_LENGTH, fin) != NULL && n < MAX_LENGTH)
{
token = strtok(line, ";");
strcpy((*data)[n]->nome, token);
token = strtok(line, ";");
strcpy((*data)[n]->cognome, token);
token = strtok(line, ";");
(*data)[n]->mat = atoi(token);
n++;
}
fclose(fin);
end:return;
}
I've tried to modify the structure and the code in many ways but have not found a solution, I think that probably is a pointer problem but I can't figure it out. The readFromFile function was provided from the professor and was built to read int from file and I had to modify it to read records.

There's a big difference between:
(*data)[i].nome = malloc(sizeof(char) * MAX_LENGTH + 1);
and:
(*data)[i]->cognome = malloc((sizeof(char) * MAX_LENGTH + 1));
The first line with the dot notation used, is implying, access to a member of a struct, whereas the -> implies accessing a member of a struct using pointer notation, i.e. pointer to a structure.
The confusion is showing there, as (*data) is a pointer to a struct of type Record which is a type definition of Rec.
typedef struct Rec* Record;
Since data when stripped down, is type definition of Record, aliased to a pointer to struct of Rec. The double pointer as part of parameter, which will be modified through pass by reference, is declared, after determining the number of lines in input dataset, as an array of pointers:
*data = (Record*)malloc(n * sizeof(Record));
Accessing the member data, for each entry in the array would be:
(*data)[i] dot name_of_member
The rule would have changed, had the type definition be like this:
typedef struct Rec Record;
i.e. a normal struct, no pointer usage.
Then access to the member data would have been, if allocation was achieved,
(*data)[i]->name_of_member
However, do not try hide pointers behind a typedef as that will give grief, come back to the code again in the future, and wondering why it failed, the hidden pointer has bitten you!

Record is defined as
typedef struct Rec* Record;
therefore it is pointer to struct Rec. malloc returns pointer to allocated memory (or NULL) but you cast this to pointer to pointer
*data = (Record*)malloc(n * sizeof(Record));
// = Rec**

Related

Memory leak after realloc call

I have a **void where each position point to *void that point to my struct record with 3 fields (*char, int, float). I want to load data from a csv in my struct, but when it's time to reallocate memory, because size is equal to array's capacity, I got realloc(): invalid next size. I did not get my printf("realloc fails") so I think that tmp is not null, but anyway I lost my memory pointer.
struct record{
int id;
char* field1;
int field2;
float field3;
};
long array_size = 0;
long array_capacity = INITIAL_CAPACITY;
void** array;
void** array_create(){
void **array = (void**)malloc(INITIAL_CAPACITY*sizeof(void*));
if(array == NULL){
fprintf(stderr, "array_create: unable to allocate memory for the array");
exit(EXIT_FAILURE);
}
return array;
}
void add_on_array(void** array, void* elem){
if(array == NULL){
fprintf(stderr,"add_on_array: array parameter cannot be NULL");
exit(EXIT_FAILURE);
}
if(elem == NULL){
fprintf(stderr,"add_on_array: elem parameter cannot be NULL");
exit(EXIT_FAILURE);
}
if(array_size >= array_capacity){
void** tmp = realloc(array, 2*(sizeof(void*)*array_capacity));
if(tmp == NULL){
printf("Realloc fails\n");
exit(EXIT_FAILURE);
}
array = tmp;
array_capacity = 2*array_capacity;
}
array[array_size] = elem;
array_size++;
}
while(fgets(buffer,buf_size,fp) != NULL){
read_line_p = strdup(buffer);
if(read_line_p == NULL){
fprintf(stderr,"main: unable to allocate memory for the read line");
exit(EXIT_FAILURE);
}
// strcpy(read_line_p,buffer);
char *id_field_in_read_line_p = strtok(read_line_p, ",");
char *field1_in_read_line_p = strtok(NULL,",");
char *field2_in_read_line_p = strtok(NULL,",");
char *field3_in_read_line_p = strtok(NULL, ",");
char *field1 = malloc((strlen(field1_in_read_line_p)+1)*sizeof(char));
if(field1 == NULL){
fprintf(stderr,"main: unable to allocate memory for the string field of the read record");
exit(EXIT_FAILURE);
}
int id = atoi(id_field_in_read_line_p);
strcpy(field1,field1_in_read_line_p);
int field2 = atoi(field2_in_read_line_p);
float field3 = atof(field3_in_read_line_p);
struct record *record_p = malloc(sizeof(struct record));
if(field1 == NULL){
fprintf(stderr,"main: unable to allocate memory for the read record");
exit(EXIT_FAILURE);
}
record_p->id = id;
record_p->field1 = field1;
record_p->field2 = field2;
record_p->field3 = field3;
add_on_array(array, (void*)record_p);
free(read_line_p);
}
fclose(fp);
stampa_array(array, array->size);
printf("\nData loaded\n");
}
Well, you are doing some "real bad" things here.
First of all you have a global variable with the name array here:
void** array;
In general global variables is something you should avoid. (In the rare cases where you really need a global variable, I'll recommend that you give it an "ugly" name that isn't used for anything else - like: globalVariableArray)
But worse - you also have a function that has an argument named array here:
void add_on_array(void** array, void* elem){
...
}
What does that mean? Having both a global array and an argument array? Which will be accessed in the function?
The answer is that the function argument array acts as a function local variable and it will hide the global variable.
So when you do:
array = tmp;
you change the local array variable - not the global.
When the function return the local variable no longer exists, i.e. any changes made to it is lost.
In other words - the memory allocated by realloc is lost and you have a leak.
First steps to fix this is:
Move the global variable into main
When calling a function that needs to change array, you need to pass the address of array, i.e. &array. Functioon prototype must be changed accordingly.
Likewise for the other globals...
But why not put all the array stuff into a struct?
Like:
struct ArrayContainer
{
long array_size;
long array_capacity;
void** array;
}
And in main do:
struct ArrayContainer container = {0, INITIAL_CAPACITY, NULL};
container.array = ...; // Allocate INITIAL_CAPACITY
I think that will simplify your code a lot as you only need to pass a pointer to this struct to the functions. Then you can change all three members through that pointer.

Cannot use malloc with struct data type?

This is just part of a bigger code, but it's full of errors so I try to fix them one by one. When I try to use malloc on my pointer vector the line returns this error
main.c|14|error: expected '{' before '*' token
Any resolutions?
struct students {
int group;
char name[20];
int grade;
};
int main()
{
struct students *ptr[100];
int num, i, max=0;
scanf("%d", &num);
ptr = (struct*) malloc(num * sizeof(struct));
if(ptr == NULL)
{
printf("error");
exit(0);
}
}
Struct is reserved keyword for declaring/defining structures in C, it isn't variable, nor something you cant get size of it. You have declared struct students (according to your code, i think it should be student instead of students), now you have to define a variable and allocate space for 100 structs via a double pointer, the code should be something like this
struct student {
int group;
char name[20];
int grade;
};
int main()
{
struct student ** ptr;
int num, i, max=0;
scanf("%d", &num);
ptr = malloc(num * sizeof(struct student));
if(ptr == NULL)
{
printf("error");
exit(0);
}
}
Now you can access individual students with array subscript
ptr[0]->grade = 20; // putting 20 in grade of first student
Also, there is no need for casting malloc result in C
While using malloc for a 1D array, you should allocate a pointer, not an array of pointers as you have done.
While allocating you are using sizeof(struct). The type here is struct students and you need sizeof(struct students)
Do not cast the result of malloc. See Do I cast the result of malloc?
The final code is
struct students *ptr;
ptr = malloc (num * sizeof(struct students));
You have an array of pointers to structure. You should allocate memory for them separately.
for(int i=0; i<100 && i<num; i++)
{
ptr[i] = malloc(sizeof(struct students));
if(0 == ptr[i])
{
/* Handle this case. */
}
}
/* Your code. */
/* At the end free the memory. */
for(int i=0; i<100; i++)
{
if(0 != ptr[i])
{
free(ptr[i]);
ptr[i] = 0;
}
}
But I think you just wanted to allocate an array of struct students. In that case you just need one pointer.
struct students *ptr = 0;
/* You allocate memory and store it in that pointer. */
ptr = malloc(num * sizeof(struct students));
if(0 == ptr)
{
/* Handle this case. */
}
You can access ith element of the array like ptr[i]. But add necessary checks and make sure i < num.
You need to free the allocated memory whenever you are done using the array.
if(0 != ptr)
{
free(ptr);
ptr = 0;
}

dynamic char pointers array to strings

in this code I want to get a number of friends and then get the names i want the strings will be allocated dynamically with the lengh of the user input i have used with 2 functions:
void getNum(char** names, int* num)
{
//somecode
names = (char*)malloc(*num * sizeof(char));
//check
}
void getNames(char** names, int* num)
{
int i = 0;
int len = 0;
char name[LEN] = { 0 };
getchar(); //buffer cleaning
for (i = 0; i < *num; i++)
{
printf("enter #%d friend name: ", i+1);
myFgets(name, LEN); //getting name and cleaning "\n" at end
len = strlen(name)+1; // getting the size of string include "/0"
*(names + i) = (char*)malloc(len * sizeof(char));
if (*(names[i]) == NULL)
{
printf("Error allocating memory!\n"); //print an error message
return 1; //return with failure
}
strncpy(*names, name, len);
}
}
the second dynamic allocation doens't work for me, overflow eror: "Access violation writing location". If the first allocation will be in the second function it will work fine. Can u explain that? and what I need to do for it will work in that way?
thank you in advance...
In function getNames, you used the wrong pointer to check for NULL, names[i] is *(names+i), not the same as *(names[i]), also, don't cast malloc's return value. No need to use sizeof(char), it's always 1.
*(names + i) = (char*)malloc(len * sizeof(char));
if (*(names[i]) == NULL) // compare to the wrong pointer
{
printf("Error allocating memory!\n"); //print an error message
return 1; //return with failure
}
strncpy(*names, name, len); // copy to the wrong buffer
Try the following:
names[i] = malloc(len);
if (names[i] == NULL)
{
printf("Error allocating memory!\n");
return 1; //return with failure
}
strncpy(names[i], name, len);
Also, in getNum, to allocate an array for char pointers, use
void getNum(char ***names, int *num) {
*names = malloc(*num * sizeof(char*));
}
You will call it by
char **names;
getNum(&names, &num);
You could also return it by doing char **getNum(...).
Assuming the first function should allocate an array of pointers, and that the second should allocate individual char arrays to store the individual names, you are lacking an indirection level in first function:
you pass it a copy of a char** (C pass parameters by copy)
you only use the local copy to store the result of the malloc (which is wrong BTW) and still keep original value in caller and eventually get a memory leak when leaving the function since nothing points to the allocated block any longer
It should be:
char** getNum(int num) /* no need to pass num by reference */
{
char **names;
//somecode
names = malloc(num * sizeof(char*)); /* do not cast resul of malloc in C */
//check
return names
}
And in second function, you should consistenly allocate memory for names[i] (or *(names + i)), test it for NULL and copy the string there:
names[i] = malloc(len * sizeof(char));
if (names[i] == NULL)
{
printf("Error allocating memory!\n"); //print an error message
return 1; //return with failure
}
strncpy(names[i], name, len);

realloc() invalid nxt size

I use this code, with this structure, im trying to make function to add item into array of this structure
typedef struct goods{
char *name;
int num;
} goods;
void addWord(char *what, goods *where, int pnr, int *arrsize, int n){
if (pnr >= *arrsize){
where = (goods*)realloc(where,*arrsize*2*sizeof(goods*));
*arrsize*=2;
}
where[pnr].name = (char*)malloc(strlen(what)*sizeof(char));
strcpy(where[pnr].name,what);
where[pnr].num = n;
}
in main function i have this:
int extstore = 1;
goods *store = (goods*)malloc(1*sizeof(goods*));
addWord(line, store, nr, &extstore, n);
Why am I getting an "invalid next size" runtime-error on the line where = (goods*)realloc(where,*arrsize*2*sizeof(goods*)); in addWord()?
EDIT:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct goods{
char *name;
int r;
} goods;
int main()
{
int linelen, i, nr = 0, current_r;
char *line = NULL;
size_t len = 0;
int extstore = 1;
goods *store;
store = malloc(extstore*sizeof(goods*));
while (1){
while ((linelen = getline(&line, &len, stdin)) != -1){
if (line[linelen - 1] == '\n'){
line[linelen - 1] = '\0';
}
linelen = strlen(line);
if (line[0] == '#'){
if (sscanf(line,"#%d",&current_r) != 1){
printf("bad input.");
return 0;
} else continue;
}
if (nr >= extstore){
store = realloc(store,extstore * sizeof(goods*) * 2);
extstore*=2;
}
store[nr].name = malloc(strlen(line)*sizeof(char));
strcpy(store[nr].name,line);
store[nr].r = current_r;
nr++;
}
if (linelen == -1) break;
}
printf("\n");
for (i = 0;i < nr;i++){
printf("%s, [id:%d]\n", store[i].name, store[i].r);
}
return 0;
}
extstore * sizeof(goods*) * 2
should be extstore * sizeof(goods) * 2 because the space for structures should be allocated - not just for pointers.
There is a fundamental problem in your code. You are passing pointer by value, which means that any change made to a pointer (not the variable pointed to, but the pointer itself) will not be visible from outside the function. You should pass a pointer by pointer instead, and you should check the result returned from realloc. Secondly, don't assign result of realloc back to same pointer - in case of failure you will lost pointer to memory -> thus, memory leak will occur.
To pass pointer by pointer:
void addWord( char *what, goods **where, size, ...) {
if ( *where == NULL) return; // nothing to do
if ( size < 1) return; // it would result in realloc=free call
goods *res = NULL;
res = realloc( *where, size * sizeof( goods));
if ( res != NULL) {
*where = res;
}
else {
// Error (re)allocating memory
// If realloc() fails the original block is left untouched,
// it is not freed or moved, so here *where is unchanged
}
And there is no need in C to cast a result from malloc.
* Error in `path': realloc(): invalid next size: 0x0000000000ec8010 *
This failure must be because "where" is invalid due to a heap corruption earlier in the execution.
C is pass-by-value.
Which means changing an argument in the function does not change the expression it was initialized from.
Thus, the first time realloc moves the memory, the pointer in main will be bad.
To correct that, either use an extra level of indirection, or preferably return the new value as the result.
(Anyway, you should check for allocation failure (malloc and realloc),
and you should not cast from void* to any pointer-type in C.)

Issue with assignment from incompatible pointer type

Hey so im trying to attempt to read in a file, store it in a hash and then copy it. However i get the incompatible pointer type
struct hash_struct {
int id;
char name[BUFFER_SIZE]; /* key (string WITHIN the structure */
UT_hash_handle hh; /* makes this structure hashable */
};
int main(int argc, char *argv[] )
{
char *lines[80];
FILE* fp = fopen("file.txt","r");
if(fgets(*lines, BUFFER_SIZE, fp) != NULL)
{
puts(*lines);
// do something
}
fclose(fp);
const char **n;
char *names[1024];
strcpy(*names, *lines);
struct hash_struct *s, *tmp, *users = NULL;
int i=0;
for (n = names; *n != NULL; n++)
{
s = (struct hash_struct*)malloc(sizeof(struct hash_struct));
strncpy(s->name, *n,10);
s->id = i++;
HASH_ADD_STR( users, name, s );
}
HASH_FIND_STR( users, "joe", s);
if (s) printf("joe's id is %d\n", s->id);
printf("Hash has %d entries\n",HASH_COUNT(users));
/* free the hash table contents */
HASH_ITER(hh, users, s, tmp) {
HASH_DEL(users, s);
free(s);
}
return 0;
}
The code works when i initialize const char **n, *names = {array elements here};
But it doesnt work with the code i have. Please help.
lines is declared to be an array of char pointers, but doesn't allocate any space for the strings they point to. In your working version, the compiler took care of allocating space for each string.
Plus, you can't use strcpy to copy an array of 80 pointers to an array of 1024 pointers.
Instead, each line you read in needs space to be allocated for it to be read into; then the addresses of each of those can be assigned to an element of names. In fact, as #BLUEPIXY suggests, line should be an array of 80 chars, not an array of 80 pointers-to-chars. Or you could just malloc the space for each new line, and put the address of that line into names.

Resources