void struct_tokens(struct Words **w_count, int size)
{
*w_count = (struct Words*)calloc(size, sizeof(struct Words));
char *temp;
int n = 0;
int i = 0;
printf("Value 1: %s\n", (*w_count[0]).word);
printf("Value 2: %s\n", (*w_count[1]).word);
}
My struct looks like this:
struct Words
{
char word[20];
unsigned int count;
};
It crashes as soon as I access the next element in the array, so w_count[0] is accessed but it cannot access w_count[1] without crashing. So the Value 1 prints to console but Value 2 does not, why is this?
The calloc call is wrong - it would be
*w_count = calloc(size, sizeof(struct Words));
Accessing the instance of the structure would be
printf("Value 1: %s\n", (*w_count)[0].word);
[] has higher precedence than *. In earlier case you were having undefined behavior (which lead to the crash).
Also note that the memory allocated will be initialized with 0. As a result the char arrays will contain nothing other than \0 - it won't print anything when used in printf.
Few points:
void* to struct Words * conversion is done implicitly - you don't need to cast it.
Check the return value of calloc in case it returns NULL you should handle it appropriately.
Free the dynamically allocated memory when you are done working with it. (using free() ).
Code wise the correct code for calloc would be like this:-
*w_count = calloc(size, sizeof(struct Words));
if( *w_count == NULL ){
perror("calloc");
exit(EXIT_FAILURE);
}
Related
I'm learning C.
I have a structure, and if I need to set array of structures -> so I allocate memory for this array. But do I need separately allocate memory for fields in this structure?
Like this:
struct Call{
char *country;
int duration;
};
int main(){
struct Call *calls;
int n;
scanf_s("%d", n);
calls = (struct Call *) calloc(n+1 , sizeof(struct Call));
}
You need not to allocate space for data members of objects of the structure type because they belong to the objects.
But it seems you will need to allocate a character array the pointer to which will be stored in the data member country if you want that objects will be owners of the corresponding strings.
For example
struct Call *calls = calloc( 1, sizeof( struct Call ) );
const char *country = "Some country";
calls->country = malloc( strlen( country ) + 1 );
strcpy( calls->country, country );
When you will deallocate memory for objects of the type struct Call you will need at first to free the memory allocated for character arrays pointed to by data members country.
Yes, you must initialize any pointer before you can dereference it. This means allocating memory for it, or assigning it to already-allocated memory. That's a universal rule in C, there's no special cases for pointers in structures. C will not "recursively" allocate memory for you. Among other things, how would it know how much you need? Consider your simplified code below
int main(){
struct Call *calls;
calls = calloc(1 , sizeof(struct Call));
}
Assuming calloc succeeded, calls now points to a memory block that contains space for a single struct Call, which includes space for the char pointer and int. However, country itself is still an unintialized pointer, and you must allocate space for it or point it to something already-allocated before you can safely dereference it
calls->country = malloc(25);
if (calls->country == NULL) exit(-1); // handle error how you want
strcpy(calls->country, "Portugal");
printf("%s\n", calls->country); // prints Portugal
or something like
char myCountry[] = "Spain";
calls->country = myCountry;
myCountry[0] = 'X';
printf("%s\n", calls->country); // prints Xpain
Also see Do I cast the result of malloc?
You need to allocate space for the struct and for char array.
You probably want to dynamically add calls to the array so you need to know the size of the array as well:
typedef struct Call{
char *country;
int duration;
}Call;
typedef struct
{
size_t size;
Call call[];
}Calls_t;
Calls_t *addCall(Calls_t *calls, const int duration, const char *country)
{
size_t newsize = calls ? calls -> size + 1 : 1;
calls = realloc(calls, sizeof(*calls) + newsize * sizeof(calls -> call[0]));
if(calls)
{
calls -> size = newsize;
calls -> call[newsize - 1].country = malloc(strlen(country) + 1);
if(!calls -> call[newsize - 1].country)
{
/* error handling */
}
strcpy(calls -> call[newsize - 1].country, country);
calls -> call[newsize - 1].duration = duration;
}
return calls;
}
void printCalls(const Calls_t *calls)
{
if(calls)
for(size_t i = 0; i < calls -> size; i++)
printf("Call %zu: Country:%s Duration:%d\n", i + 1, calls -> call[i].country, calls -> call[i].duration);
}
int main(void)
{
Calls_t *calls = NULL, *tmp;
tmp = addCall(calls, 10, "Poland");
if(tmp) calls = tmp;
tmp = addCall(calls, 20, "UK");
if(tmp) calls = tmp;
tmp = addCall(calls, 30, "US");
if(tmp) calls = tmp;
printCalls(calls);
/* free allocated memory */
}
https://godbolt.org/z/Kb5bKMfYY
My program takes an arbitrary number of words at runtime and stores them in a dynamically-sized array of words.
Currently, my program runs well, except when I use free() to free up the memory of the temporary double pointer temp. I am not quite sure why it does this, as I thought it would cause errors if I didn't use it.
int wordSize = 10, arrSize = 1, i = 0;
char **stringArr, **temp;
char *input;
stringArr = malloc(arrSize * sizeof(char *));
puts("Accepting input...");
for (;;) {
if (i >= arrSize) {
arrSize += 1;
temp = realloc(stringArr, arrSize * sizeof(char *));
if (temp != NULL) {
stringArr = temp;
free(temp); // This is the line that is giving me issues; removing it works
} else {
puts("Could not allocate more memory");
return 0;
}
}
stringArr[i] = malloc(sizeof(input));
input = malloc(wordSize * sizeof(char));
scanf("%10s", input);
if (strcmp(input, "END")) {
strcpy(stringArr[i], input);
i++;
} else
break;
}
free(stringArr);
At the bottom of my program I use free() without any issues. How come it works OK here but not earlier on in the program.
I feel I am missing something about how free() works.
Note: this is my first program implementing malloc() and realloc(), so I am only just getting used to how they work. If you know of a better way to accomplish what I am doing that, please feel free to describe.
The free(temp); line is causing an error (later on) because, in the preceding line, stringArr = temp;, you are assigning the address that is stored in the temp pointer to that in the stringArr pointer. Thus, when you free the memory pointed to by temp you also free the memory pointed to by stringArr, because it is the same memory block. Copying a pointer's value from one variable to another does not make a (separate) copy of the memory.
Omitting the free(temp); line is correct, because that memory is freed later on, in the free(stringArr); call.
You must not free the reallocated array when reallocation was successful. If you do that, the code will modify this freed block, which has undefined behavior and you will have further undefined behavior when you later try and reallocate or free this block.
Note also the following:
pre-allocating stringArr with a size of 1 is not necessary. Just initialize stringArr to 0 and arrSize to 0. realloc() can take a null pointer and will behave like malloc().
stringArr[i] = malloc(sizeof(input)); is incorrect: it will allocate a char array with a size of 4 or 8 depending on the size of a pointer on the target architecture, not 11 bytes as it should.
if the wordSize is the maximum length of a word, you should allocate one more byte for the null terminator. The 10 in %10s must match the value of wordSize, which is cumbersome because there is no easy way to pass this to scanf() as a variable.
you do not check the return value of scanf(), causing undefined behavior in case of premature end of file.
you have memory leaks: input is allocated for each iteration but never freed, freeing stringArr without freeing the strings pointed to by its elements makes them inaccessible.
It would be more efficient to use a local array to try and read the words with scanf() and only allocate the string and reallocate the array if successful.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
int arrSize = 0;
char **stringArr = NULL;
char input[11];
puts("Accepting input...");
while (scanf("%10s", input) == 1 && strcmp(input, "END") != 0) {
char **temp = realloc(stringArr, (arrSize + 1) * sizeof(*stringArr));
if (temp != NULL) {
stringArr = temp;
} else {
puts("Could not allocate more memory");
break;
}
stringArr[arrSize] = strdup(input);
if (stringArr[arrSize] == NULL) {
puts("Could not allocate more memory");
break;
}
arrSize++;
}
puts("Array contents:");
for (int i = 0; i < arrSize; i++) {
printf("%i: %s\n", i, stringArr[i]);
}
for (int i = 0; i < arrSize; i++) {
free(stringArr[i]);
}
free(stringArr);
return 0;
}
I have this function, I realocate memory, but when i want to read string it doesn't work=>error
Student is a Struct
void insertStudent(Student **myStudents, int *size)
{
int newSize = *size + 1;
*myStudents = (Student*)realloc(myStudents, newSize*sizeof(Student));
printf("Enter your grade: ");
scanf("%f", &(*myStudents)[*size - 1].nota);
printf("Enter your first name: ");
scanf("%s", &(*myStudents)[newSize-1].firstName);
printf("Enter your second name: ");
scanf("%s", &(*myStudents)[*size - 1].lastName);
//Generate a new code
/*int code = rand() % 1+1000;
int ok = 0;
while (ok == 0)
{
ok = 1;
for (int i = 0; i < *size; i++)
if ((*myStudents)[i].cod == code)
{
code = rand() % 1 + 1000;
ok = 0;
}
}*/
(*myStudents)[*size-1].cod = 7;
printf("Your code is: %d. Do not forget it! ", 7);
}
void insertStudent(Student **myStudents, int *size)
{
*myStudents = (Student*)realloc(myStudents, newSize*sizeof(Student));
// ^ <- look here
This is a pointer to a pointer to your students. realloc() expects the pointer to the data originally allocated, so you most certainly have to pass *myStudents here!
Also change the code to use a temporary variable. realloc() may return NULL on error, in which case the original memory is still allocated and you have to free() it.
For calculating the size, it's better to use the expression syntax of sizeof (newSize * sizeof **myStudents) because this prevents errors when later changing the type.
For sizes, you should always use size_t, as this is guaranteed to hold any possible size of an object (int is not ...)
Further side note: converting to and from void * is implicit in C and it's arguably better style not to write this cast explicitly.
All in all, the code should be written like this
void insertStudent(Student **myStudents, size_t *size)
{
size_t newSize = *size + 1;
Student *newStudents = realloc(*myStudents, newSize * sizeof *newStudents);
if (!newStudents)
{
free(*myStudents);
*myStudents = 0;
return; // check for this error in calling code
}
*myStudents = newStudents;
*size = newSize;
// [...]
}
realloc() needs pointer to the memory to be reallocated which is *myStudents and not myStudents.
Change
*myStudents = (Student*)realloc(myStudents, newSize*sizeof(Student));
to
*myStudents = (Student*)realloc(*myStudents, newSize*sizeof(Student));
You are reallocting to myStudents. It is not your intention and it is not possible.
From standard 7.22.3.5
void *realloc(void *ptr, size_t size);
Otherwise, if ptr does not match a pointer earlier returned by a
memory management function, or if the space has been deallocated by a
call to the free or realloc function, the behavior is undefined. If
memory for the new object cannot be allocated, the old object is not
deallocated and its value is unchanged.
Earlier you had undefined behavior, you didn't pass the originally allocated memory's address. Rather you passed a local variable. You had undefined behavior.
Student* t = realloc(*myStudents, newSize*sizeof(Student))
if(t){
*myStudents = t;
(*size)++;
}
else {
perror("realloc failed");
free(*myStudents);
exit(EXIT_FAILURE);
}
Also as you are increasing the memory you should do increase it if the call is successful. Then consistently access *size-1 throughout the code, it is easier to handle and go through.
The right way to do it shown above. In case realloc returns NULL you won't lose the reference to the already allocated memory. Also with this comes the advise to check the return value of realloc. Casting is redundant in this case - don't do it.
In the scanf you can simply write
scanf("%s",(*myStudents)[newSize-1].firstName);
otherwise you were passing char (*)[] it expects char*.
I have written the following code to resize an array if the an item is going to go out of range when storing it. This code works as intended.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//! Our location struct for storing locations
struct location
{
char loc_name[35];
char description[85];
float latitude;
float longitude;
};
void runMenu();
void add_location(struct location** p_location_array, int* p_array_size, int* p_current_size);
void resize_array(struct location** p_location_array, int* p_array_size);
void print (struct location* p_array, int p_current_size);
// Print out the main menu to the user.
void runMenu()
{
printf("[A]dd additional Locations\n");
printf("[P]rint the current list of locations\n");\
printf("[Q]uit the program\n");
}
//! Resize the array to two times it's origional size.
void resize_array(struct location** p_location_array, int* p_array_size)
{
// Allocate enough space for two times the size of the array
int new_size = 2 * (*p_array_size);
struct location* new_location_array = malloc(new_size * sizeof(struct location));
if (!new_location_array)
{
printf ("Cannot add more elements heap has exhausted all space\n");
exit(1);
}
// Copy the old array to the new array.
memcpy(new_location_array, *p_location_array, ((*p_array_size ) * sizeof(struct location)));
// We will update the current size of the array for later checking.
*p_array_size = 2 * (*p_array_size);
// We have a copy of the old array so we can free it.
free(*p_location_array);
// The contents of the pointer reference get the array we malloced in this function
*p_location_array = new_location_array;
}
//! Add a new location to our array. If the array isn't large enough resize it then insert the new struct.
void add_location(struct location** p_location_array, int* p_array_size, int* p_current_size )
{
// Get the users input
struct location new_location;
printf("Enter the new location name\n ");
fscanf(stdin, "%s", new_location.loc_name);
printf("Enter a description of the location\n");
fscanf(stdin, "%s", new_location.description),
printf("Enter the latitude\n");
fscanf(stdin, "%f", &new_location.latitude);
printf("Enter the longitude\n");
fscanf(stdin, "%f", &new_location.longitude);
// Check to see if the size is correct.
if (*p_array_size <= *p_current_size)
{
// If not the correct size resize the array
resize_array(p_location_array, p_array_size);
}
// Insert our sruct
(*p_location_array)[*p_current_size] = new_location;
}
//! Loop over and print out the locations
void print (struct location* p_array, int p_current_size)
{
int i;
for (i = 0; i < p_current_size; i++)
{
struct location current = p_array[i];
printf("%s : %s : %f : %f\n", current.loc_name, current.description, current.latitude, current.longitude);
}
}
int main()
{
char choice = ' ';
short control = 1;
int size;
int currentSize = 0;
printf("Enter the inital size of the array\n");
scanf(" %d", &size);
// Make a new struct array from the heap
struct location* m_location_array =
malloc(size * sizeof(struct location));
// Make sure we have a valid chunk of the heap.
if (!m_location_array)
exit(1);
while(control)
{
runMenu();
scanf(" %c", &choice);
switch (choice)
{
case 'a':
case 'A':
// Do Add additional
add_location(&m_location_array, &size, ¤tSize);
currentSize++;
break;
case 'p':
case 'P':
// Do printing
print (m_location_array, currentSize);
break;
case 'Q':
case 'q':
control = 0;
break;
default:
printf("Invalid input\n");
}
}
// clean up after ourselves.
free (m_location_array);
return 0;
}
However, when I originally wrote this function I thought it would be possible to just pass in a the pointer to the array instead of a reference to the pointer like so:
void resize_array(struct location* p_location_array, int* p_array_size)
Calling this function without the reference to pointer threw a segfault indicating the memory was being doubly freed. Is that because the pointer when passed to the function someway gets freed and reallocated? Furthermore, why is it necessary to pass a pointer by reference like this? Even if the pointer is a copy of the original pointer wouldn't it still point to the same piece of memory? Any points in the correct direction is much appreciated.
You gave a pointer to the function and you have a call to free there. So the memory is freed. After that using that pointer causes undefined behaviour and you may not use it.
Modifying the pointer variable inside the function doesn't change the pointer outside the function. That is why you need a pointer to the pointer so that you can modify the pointer variable outside the function.
Even if the pointer is a copy of the original pointer wouldn't it still point to the same piece of memory?
Yes, and that is the point: it will keep on pointing to the same place unless you change it. If you do a new malloc it will point to a completely different place.
Also a hint: realloc might be worth checking out.
If you pass pointer to function it is a copy of original one. Then even if you assign to this pointer inside the function, like
p_location_array = new_location_array;
the original pointer (that outside the function) still has unchanged value. So in case the original pointer pointed to some memory area and you have passed it to the function
void resize_array(struct location *p_location_array, int* p_array_size)
and you have called free() inside thefunction and assigned NULL to the pointer, after your function has returned the original pointer will comapre as not NULL.
// warning, changed prototype
void resize_array(struct location *p_location_array, int* p_array_size);
struct location *loc = malloc(size * sizeof(struct location)); // assume loc = 0x12345678
if (loc == NULL) EXIT_FAILURE;
// change pointer inside the function
// assign NULL to the pointer
resize_array(loc, size_p);
if (loc != NULL)
free(loc); // this will be called, loc is still 0x12345678,
// double free, UB
struct student
{
int roll;
char *name;
};
int main()
{
int i;
struct student arr[2];
arr[0].roll = 12;
arr[1].name = "John";
arr[1].roll = 13;
arr[1].name = "Craig";
struct student *ptr;
ptr=arr;
// This is perfect.
for(i = 0; i<2; i++)
{
printf("%d %s", ptr->roll, ptr->name);
}
// This is also ok.
printf("%d %s", ptr->roll, ptr->name);
ptr++ // getting to next structure.
printf("%d %s", ptr->roll, ptr->name);
// But this isn't ok
while(*ptr || ptr->name != NULL)
{
ptr++;
}
return 0;
}
How to check pointer in while loop?
ptr points to an array if you increment it, ptr start pointing to a memory out size array that is not null.
You can do something like:
ptr = arr;
while (ptr < (arr + sizeof(arr)/sizeof(arr[0])) ){
ptr++;
}
Note: this technique will nnot work for dynamic arrays.
To learn what is this formula about read: Weird behavior when printing array in C?
You can use ptr check for null if you traversing an array of pointers AND when you know that the end of such array is marked with NULL. You are not.
You actually are traversing an array of structures with the ptr pointing straight to the memory location of the first element.
You have to keep track of the size of the array (or amount of its filled elements) and just stop after you have gone through them all.
int count = 2;
struct student arr[count];
struct student* ptr = arr;
for (int i=0; i<count; i++) {
// do your stuff
ptr++; // place it in for if you like
}
You can use this:
struct student *ptr=arr;
int max_len = sizeof(arr)/sizeof(*arr);
while(max_len--)
{
//do something...
ptr++;
}
One more thing I want to point out is you need to allocate memory for the char* pointer before allocating it with a string.The last loop which you have written doesn't execute at all, since all are NULL pointers.
Other way around is to put some special value in the roll number(say a negative value) of the last struct in the array. You then traverse the array until the roll number is positive.
Note that this method can also be used for dynamic arrays.
struct student *arr = (struct student*)malloc(20*sizeof(struct student));
*(arr+19).roll = -1; //this acts as a sentinel to indicate the end of the array....
struct student *ptr=arr;
while(ptr->roll > 0)
{
//do something...
ptr++;
}
while(*ptr || ptr->name != NULL)
First things first: the value of *ptr is a struct, which can't be converted to a boolean value and so while(*ptr) won't compile.
In order to null-terminate an array of structs, you must choose some struct member which you know will never be NULL when the struct is initialized (usually an important pointer), and use it as the flag. You do have that: the name field.
while(ptr->name != NULL)
Then the only problem is that your array isn't actually NULL terminated. You need to reserve an extra, third element at the end, just like with strings. An easy way to do so is by zero-initializing the whole array at declaration. So:
struct student arr[3] = {0};
arr[0].roll = 12;
arr[0].name = "John";
...
Which would have the same effect as manually setting ar[2].name = NULL;