Pointer values changes when using realloc() on pointer to pointer - c

I have to read from a file which has a unknown number of students records in it written in binary, then sort the students by their GPA and send to stdout.
Our sort function had to be like
void insertion_sort(Student **, int);
That's why I choose to use an pointer to a pointer to Student (probably not the best solution? I think I could have just sent an pointer to Student like this (&p_to_Student, n) ?)
The code is bellow, the problem is that when I print the first element of what p is pointing to (the first students name) I get gibberish, the other students are fine.
I checked the value of p, and it does change after realloc() is called, and because it's also the address of the first element of p (right?).
Also checked with Valgrind and it returns a bunch of errors about memory leaks!
The code runs fine when there is no realloc() call, also when I initialize p after I'm done reading the file. So it must be something to do with not using realloc() correctly.
Bonus question: is this a proper way to read an unknown number of data entries from a file?
#include <stdio.h>
#include <stdlib.h>
struct student {
char name[30];
char surname[30];
double GPA;
};
typedef struct student Student;
void insertion_sort(Student **arr, int n)
{
int i,j;
for (i=1; i<n; i++)
{
Student *tmp = arr[i];
for (j=i; j>0 && (tmp->GPA > arr[j-1]->GPA); j--)
arr[j] = arr[j-1];
arr[j] = tmp;
}
}
int main(int argc, char **argv)
{
FILE *in;
Student s, *arr, **p;
size_t ret;
int i = 1, n=0, c=2;
in = fopen(argv[1], "rb");
if (in == NULL)
return printf("Can't open file!\n"), 1;
arr = (Student*) malloc(c*sizeof(Student*));
p = (Student**) malloc(c*sizeof(Student*));
do
{
ret = fread(&s, sizeof(Student), 1, in);
if (ret)
{
if (n == c)
{
arr = (Student*) realloc(arr, (c*=2)*sizeof(Student));
p = (Student**) realloc(p, c*sizeof(Student*));
}
// when I print the value of pointer p
// the values is changed when realloc() is called
printf("p = %p\n", p);
arr[n] = s;
p[n] = arr+n;
n++;
}
} while (ret);
fclose(in);
// If I do this instead the program runs correctly
//p = (Student**) malloc(c*sizeof(Student));
//for (int i=0; i<n; i++)
//{
//p[i] = arr+i;
//}
insertion_sort(p, n);
for (i=0; i<n; i++)
{
printf("%2d. %-20s %-20s %7.2lf\n", i+1, p[i]->name,
p[i]->surname, p[i]->GPA);
}
free(arr);
free(p);
return 0;
}

realloc may change the pointer. That means all pointers into that pointer may become invalid. In your case, p holds pointers into arr.
Your problem is not that the value of p changes, but that the old values of p are no longer valid when the value of arr changes.
To illustrate (all pointer and size values are made up):
sizeof(stud) == 16;
allocate arr: arr == 0x00100000;
1st value: arr[0] = stud1; p[0] = &arr[0]; // 0x00100000
2nd value: arr[1] = stud2; p[1] = &arr[1]; // 0x00100010
reallocate arr: arr == 0x00200000;
old address of arr is no longer valid!
3rd value: arr[0] = stud1; p[2] = &arr[2]; // 0x00200020
Now your pointer array looks like this:
p[0] == 0x00100000 // no longer valid!
p[0] == 0x00100010 // no longer valid!
p[0] == 0x00200020 // okay
Because you need p only for your sorting, the approach you have commented out – to allocate p at one go before sorting – is better.
realloc is useful only if you don't now beforehand how big your array is, so you should use it as long as you are building the array. When you are done building the array and you can be sure that arr will stay the same you should create the array of pointers, p.

Related

sprintf Producing segmentation fault

I am getting a segmentation fault from the below program.
#include <stdio.h>
#include <string.h>
void removeProcess(int*, int);
void removeProcessN(char**, int, int);
void main() {
int numPro = 0, quanTime = 0, contTime = 0, i, elemNum, time = 0;
//Supply variables with user input
printf("Enter number of processes: ");
scanf("%d", &numPro);
printf("Enter context switch time: ");
scanf("%d", &contTime);
printf("Enter quantum of time: ");
scanf("%d", &quanTime);
//Create array of number of process time
int proTime[numPro];
//Create string array for better output
char *proNames[numPro];
//Retrieves process time from user
for (i = 0; i < numPro; i++){
printf("Enter execution time for process %d: ", i);
scanf("%d", proTime + i);
sprintf(proNames[i], "p%d", i);
}
elemNum = 0;
//While a process remains active
while (numPro != 0) {
//Retrieves the element being worked with
elemNum = elemNum % numPro;
//Describe process working with
printf("Executing process %s\nStart time = %d\n", proNames[elemNum], time);
proTime[elemNum] -= quanTime;
//If process time complete, remove process
if (proTime[elemNum] <= 0){
removeProcess(proTime, elemNum);
removeProcessN(proNames, elemNum, numPro);
--numPro;
}
//Add amount of time with context time
time = time + quanTime + contTime;
elemNum++;
}
}
/**
*#param *array pointer to an array of integers
*#param elem int of the element to remove
* Removes an element 'elem' from the supplied integer array.
*/
void removeProcessN(char **array, int numElem, int elem) {
char *temparray[numElem - 1];
//Copy array to temparray except for elem to remove
int i;
for (i = 0; i < elem; i++) {
if (i == numElem) {
continue;
} else {
temparray[i] = array[i];
}
}
//End by setting the pointer of array to the temparray
array = temparray;
}
/**
*#param *array pointer to an array of integers
*#param elem int of the element to remove
* Removes an element 'elem' from the supplied integer array.
*/
void removeProcess(int *array, int elem) {
//Number of elements in the array
int numElem = sizeof(array) / sizeof(int);
int temparray[numElem - 1];
//Copy array to temparray except for elem to remove
int i;
for (i = 0; i < numElem; i++) {
if (i == elem) {
continue;
} else {
temparray[i] = array[i];
}
}
//End by setting the pointer of array to the temparray
array = temparray;
}
I know the segmentation fault is coming from sprintf. I am trying to simulate how an operating system would complete a process using round robin. I have tried using sprintf because that's what tutorials were saying online to use when trying to manipulate strings. The removeProcessN is just removing an index from the array proNames. I am mostly just concerned with the sprintf.
I have tried malloc when I do the sprintf but it would not even compile at that point. If someone could offer an explanation I'd be appreciative.
The problem here is that proNames is an array of pointers, but they are
uninitialized, so passing it to sprintf to write something, will crash. You
would have either use a double array or allocate memory with malloc. But as
you are only printing integers and the string representatuion of integers has a
maximal length, allocating memory with malloc will be more harder, because you
have to check that malloc doesn't return NULL, you have to free the memory
later, etc.
So I'd do:
char proNames[numPro][30]; // 28 characters for an int (usually 4 bytes long)
// should be more than enough
//Retrieves process time from user
for (i = 0; i < numPro; i++){
printf("Enter execution time for process %d: ", i);
scanf("%d", proTime + i);
sprintf(proNames[i], "p%d", i);
}
Your removeProcessN would need to change as well:
void removeProcessN(int numElem, int elem, int dim, char (*array)[dim]) {
for(int i = elem; i < numElem - 1; ++i)
strcpy(array[i], array[i+1]);
array[numElem - 1][0] = 0; // setting last element to empty string
}
Note that I moved the array argument at the last position, otherwise numElem
is not known and the compiler would return an error.
And now you can call it like this:
removeProcessN(elemNum, numPro, 30, proNames);
The 30 comes from the char proNames[numProp][30]; declaration.
I'd like to comment on the last line of your function removeProcessN:
//End by setting the pointer of array to the temparray
array = temparray;
That is not correct, first because temparray is local variable and ceases to
exist when the function returns. And array is local variable in the function,
so changing it doesn't affect anybody.
The alternative with memory allocation would look like this:
char *proNames[numPro];
//Retrieves process time from user
for (i = 0; i < numPro; i++){
printf("Enter execution time for process %d: ", i);
scanf("%d", proTime + i);
int len = snprintf(NULL, 0, "p%d", i);
proNames[i] = malloc(len + 1);
if(proNames[i] == NULL)
{
// error handling, free the previously allocated
// memory, and return/exit
}
sprintf(proNames[i], "p%d", i);
}
and removeProcessN:
void removeProcessN(char **array, int numElem, int elem) {
char *to_remove = array[elem];
for(int i = elem; i < numElem - 1; ++i)
array[i] = array[i+1];
free(to_remove);
array[numElem - 1] = NULL; // setting last element to NULL
// makes freeing easier as
// free(NULL) is allowed
}
And the way you originally called the removeProcessN would be OK.
If you eventually call removeProcessN for all processes, then all the memory
should be freed because removeProcessN frees it. If there are some elements
that remain in the array, then you have to free them later.
OP posted in the comments
My theory was that temparray would be a pointer to an array so I could just remove an index from the main array.
So when I say array = temparray, the pointer for array points to temparray. I know it worked for removeProcess. Is it different for strings?
The array = temparray also has no effect in removeProcess, array is still
a local variable and changing where it points to has no effect at all, because
you are changing a local variable only.
Besides the code is wrong:
int numElem = sizeof(array) / sizeof(int);
this only works for pure arrays, it does not work for pointers because
sizeof(array) returns you the size that a pointer of int needs to be stored.
Like the other function, you need to pass the site the array to the function.
If you say that this function worked, then just only by accident, because it
yields undefined behavior. By incorrectly calculating the number of elements,
temparray will have the wrong size, so here temparray[i] = array[i]; you may
access beyond the bounds which leads to undefined behaviour. Undefined behaviour
means that you cannot predict what is going to happen, it could be anything from
crashing to formatting your hard drive. Results that result from undefined
behaviour are useless.
And again array = temparray; just changes where the local variable array is
pointing, the caller of removeProcess doesn't see that.
The correct version would be:
int removeProcess(int *array, int elem, int numElem) {
if(array == NULL)
return 0;
// nothing to do if the elemnt to be removed is
// the last one
if(elem == numElem - 1)
return 1;
// overwriting the memory, because memory
// regions overlap, we use memmove
memmove(array + elem, array + elem + 1, numElem - elem - 1);
return 0;
}
So, to make it clear:
Let's look at this code:
void sum(int *array, size_t len);
{
int c[len];
array = c;
}
void bar(void)
{
int x[] = { 1, 3, 5 };
size_t len = sizeof x / sizeof *x;
sum(x, sizeof x / sizeof *x);
printf("x[0] = %d, x[1] = %d, x[2] = %d\n", x[0], x[1], x[2]);
}
sum has only a copy of the pointer you've passed in bar, so from bar's
point of view, sum changed the copy, so bar will print
x[0] = 1, x[1] = 3, x[2] = 5.
But if you want that the caller sees any change, then you to access through the
pointer:
void sum(int *array, size_t len)
{
int c[len];
for(size_t i = 0; i < len; ++i)
array[i] += 10;
array = c;
}
With this version bar would print x[0] = 11, x[1] = 13, x[2] = 15 and
and array = c will have no effect on bar.

dynamically increase size of array (int*) in c

i want to dynamically add numbers to an array in c. My idea is to just allocate a new array with size + 1, add the number, free the root array and change the pointer from the temp to the root array. Like this:
void addNumber(int* a, int* size, int number)
{
*size = *size + 1;
int* temp = (int*)(calloc(*size, sizeof(int)));
int i, j = 0;
for(i = 0; i < *size-1; i++) {
if(a[i] < number) {
printf("add ai");
temp[j] = a[i];
j++;
} else {
printf("add number");
temp[j] = number;
}
}
if(j != *size) {
printf("add new number");
temp[j] = number;
}
free(a);
a = temp;
}
int main(int argc, char* argv[])
{
int n = 10;
int* a;
int size = 1;
a = (int*) (calloc(1, sizeof(int)));
a[0] = 1;
if(!contains(a, size, 2)) {
addNumber(a, &size, 2);
}
printArray(a,size);
return 0;
}
The problem is that in the addNumber function the code works and the *a has the right values of the new array. But in the main function the array *a has the values 1,0. So the new inserted value 2 is not added. Why? Can't get the reason.
To dynamically change the array size, you can use the realloc() routine. Apart from being eaiser to use, it can be faster than the approach of calling free() and malloc() sequentially.
It is guaranteed the reallocated block will be populated with the content of the old memory block.
The problem is that in the addNumber function the code works and the *a has the right values of the new array
There are two major flaws in your code. The first is that you your addNumber() routine doesn't return the newly allocated memory block (thus it is being leaked), you should either use double pointer or return the new block as function result.
And the second one results from the first - after a has been freed, you continue to write to it.
If you prefer to stick to your current approach, this modified code should work:
void addNumber(int** a, int* size, int number)
{
*size = *size + 1;
int* temp = (int*)(calloc(*size, sizeof(int)));
int i, j = 0;
for(i = 0; i < *size-1; i++) {
if((*a)[i] < number) {
printf("add ai");
temp[j] = (*a)[i];
j++;
} else {
printf("add number");
temp[j] = number;
}
}
if(j != *size) {
printf("add new number");
temp[j] = number;
}
free(*a);
*a = temp;
}
int main(int argc, char* argv[])
{
int n = 10;
int* a;
int size = 1;
a = (int*) (calloc(1, sizeof(int)));
a[0] = 1;
if(!contains(a, size, 2)) {
addNumber(&a, &size, 2);
}
printArray(a,size);
return 0;
}
What you're looking for is realloc(). It can be used to grow or shrink memory while retaining its contents.
/* array is now sizeof(int) * new_size bytes */
array = realloc(array, sizeof(int) * new_size);
realloc() might change the existing memory allocation, or it might allocate a whole new block of memory. This is why it's important to reassign the result back to the thing being reallocated.
But if addNumber() reallocates the array by making new memory, main() won't know it. This is for the same reason this doesn't work.
void incrementNumber(int num) {
num = num + 1;
}
int num is a number that gets passed by value. If you want it to be reflected in the caller, you need to pass it as a pointer.
void incrementNumber(int *num) {
*num = *num + 1;
}
Pointers are the same way. They're still numbers. int *a passes a pointer by value. If you change a in addNumber it won't be seen by the caller. Just like before, you need to pass it as a pointer. A pointer to a pointer used like this is known as a double pointer.
void addNumber( int **array_ptr, size_t *array_size, size_t type_size, int number ) {
/* Increment the size and make sure that bubbles up */
*array_size = *array_size + 1;
/* realloc might grow the memory, or it might allocate new memory
either way, assign the result back to its original variable
by dereferencing the double pointer.
*/
*array_ptr = realloc(*array_ptr, *array_size * type_size);
/* Since it's a double pointer, we have to first dereference it before using
it as an array */
(*array_ptr)[*array_size - 1] = number;
}
(Note that I also pass in the sizeof the elements in the array, that can't be assumed).
This is called by passing a pointer to the array.
addNumber(&a, &size, sizeof(int), 5);
After that, everything is the same.
for( int i = 0; i < size; i++ ) {
printf("%d ", a[i]);
}
puts("");
Eventually you'll want to improve this by having the array, size, and type in a struct so you can pass that around in a neat package.
typedef struct {
int *array;
size_t size;
} IntArray;
This is great to do as an exercise, you'll learn a lot and kick a lot of bad habits about static memory. But doing dynamic data structures correctly and efficiently is difficult (for example, allocating one extra slot at a time is very inefficient).
There are many, many libraries out there which provide such dynamic structures. So continue with this as an exercise, but for real code use a library such as Gnome Lib.
Why? Can't get the reason.
That's because you are modifying the value of a locally in addNumber. That does not change the value of a in main.
In order for main to have access to the newly allocated memory, you need to change addNumber to return the newly allocated pointer.
int* addNumber(int* a, int* size, int number){
...
return a;
}
and then change main to:
if(!contains(a, size, 2)){
a = addNumber(a, &size, 2);
// Assign to a the new pointer value.
}
Your 'a' in main is already a pointer, passing it to a function passes a copy of it. What you have to do is - pass the adress '&a' and receive it in funtion as double pointer '**a' and inside the function, use dereference to get values inside array ( like *a[i] and free(*a).
Change the last line to 'return temp' and collect it in main as a=addnumber(&a,&size,2);
By the way, instead of going through all these hassle why don't you just use realloc() function. It increases the size of array dynamically. After using realloc you can just add the new number at the last index.

C array of pointers and malloc

I'm trying to get data from std stored into an array, using pointers. The main declares d as int *d; and the function is called using x = getdata(&d); When I step through it with gdb it dies at the first iteration of the for loop, when it tries *d[i]=val;
int getdata(int **d)
{
int count,val,i,j=0;
scanf("%d", &count);
d = malloc(sizeof *d * count);
for( i = 0; i < count-1; i++) {
scanf("%d",val);
*d[i]=val;
}
for ( i = 0; i < count; i++)
printf("Number %d\n",*d[i]);
return count;
}
The memory should be allocated as follows
*d = malloc(count * sizeof **d);
The values should be accessed as follows
(*d)[i] = val;
It is also not clear why you allocate count elements and only initialize count - 1 elements in the input cycle (and later print all count elements in output cycle).
*d = malloc(count * sizeof(int));
then
(*d)[i] = val
What you have is a pointer to an array, not an array of pointers.
1) malloc returns a void* so you need to assign the result to *d instead of d
2) the data size you want is of an int, not an int* (using *d gets you an int* where **d is an int)
*d = malloc(sizeof(**d) * count);
3) Indexing the array requires slightly different syntax
(*d)[i] = val;
printf("%d\n", (*d)[i]);
4) The second scanf needs a pointer, not an int
scanf("%d", &val);

How to cycle through array without indexes in C?

I need to allocate an N sized array and assign it values, how can I do it without int indexes?
Here is the code I have so far but it doesn't do what I need:
#include <stdlib.h>
#include <stdio.h>
int main() {
int *array;
int n;
printf("Size of array: ");
scanf("%d", &n);
array = (int*) malloc(n*sizeof(int));
if (array == NULL) printf("Memory Fail");
for(; *array; array++)
{
printf("Store:\n");
scanf("%d", &n);
*array = n;
}
for(; *array; array++)
{
printf("Print: %d\n",*array);
}
free(array);
return 0;
}
thanks
for(; *array; array++); you should remove ; at the end
Number of iterations for this loop is undefined and you are going to lose a pointer
You should do something like this:
int *cur;
for(cur = array; cur < array+n; ++cur)
{
*cur = ...;
}
When you allocate the memory, you have no way to determine, in the memory, where it ends (unless you decide a convention and set a value somewhere, but anyway you would use n) .
In your case you have to use n to limit the array coverage (otherwise it is only limited by your computer capacity, and until it reaches an area where it does not have access: program crash). For instance (be careful not to overwrite n !)
int v;
int x = n;
int *ptr = array;
while (x--)
{
printf("Store:\n");
scanf("%d", &v);
*ptr++ = v;
}
x = n;
ptr = array;
while (x--)
{
printf("Print: %d\n",*ptr++);
}
You are using *array as your condition, which means the for loop should continue unless *array evaluates to false, which is only if *array == 0. You are actually invoking undefined behavior because you allocate array with malloc and are trying to dereference the pointer when the underlying data could be anything, since the data block has been uninitialized.
You still need some type of counter to loop with, in this case you allocated n items.
/* I'm using a C99 construct by declaring variables in the for initializer */
for (int i = 0; i < n; ++i)
{
/* In your original code you re-assign your counter 'n', don't do that otherwise you lost the size of your array! */
int temp;
printf("Store: \n");
scanf("%d", &temp)
array[i] = temp;
}
/* This is your second loop which prints the items */
for (int i = 0; i < n; ++i)
{
printf("%d\n", array[i]);
}
Also, do not manipulate the array pointer without keeping a copy of it. You can only do free on the pointer returned by malloc.
Using indexes is the same as manipulating the pointer, your professor is being ridiculous otherwise.
If you have an array int *a; then:
a[0] is equal to *a
a[1] is equal to *(a+1)
a[2] is equal to *(a+2)
So you can go through the array by doing arithmetic on the pointer.

Convert list to array

I have functon that convert list in array:
void* list_to_array(SList* list)
{
int i;
int array_size = list_get_length(list);
void* array[array_size];
for (i = 0; i < array_size; i++)
{
array[i] = list_get_n_data(list,i);
}
return *array;
}
But when i try to test it:
int* a = (int*)list_to_array(list);
printf("%d" (int)a);
it's ok. I see first element. But when i try to get second or third element:
int* a = (int*)list_to_array(list);
a++;
printf("%d" (int)a);
I see first element + 4. if i try get third element i see first element value + 8 and etc... Why? What's wrong?
Thank you
You are returning a pointer to a stack memory location. That memory region is no longer valid once the function returns.
Also, instead of actually returning a pointer to the array, you are returning the first element in the array. The following code will return 1, not a pointer the array.
int array[] {1, 2, 3, 4};
return *array
You probably only need to make minimal changes your code to get it to work.
void** array = (void **) malloc(sizeof(void *) * array_size);
...
return array;
Just make sure that you release the memory that memory used for array when you are finished with it.
void **array = list_to_array(list);
// Use array
...
// Finished with array
free(array);
When you increase the pointer int* a by 1, it would actually increase it by sizeof(int), which is - on most systems, at least - 4.
So if
int* a = 0x40b8c438
then
a + 1
= ((void*) a) + sizeof(int)
= 0x40b8c43c
and
a + 2
= ((void*) a) + sizeof(int) * 2
= 0x40b8c440
You have three problems here. The first is trivial, you're returning the first element of the array with return *array, when what you mean is to return a pointer to the first element of the array with return array. DON'T STOP HERE! The second is that you are incrementing your pointer by 1, rather than by the size of the data you're pointing to. This will cause you to get wrong results. The third problem is much more serious:
You allocate memory for your array on this line:
void* array[array_size];
This memory is allocated on the stack, and this memory is no longer allocated when you return from the function. When you later reference this memory with the line:
int* a = (int*)list_to_array(list);
a is pointing to a region on the stack which is no longer in use. You get somewhat reasonable results with the code you have, but if you modify the stack after returning from your function, a will be pointing at the new memory. For example, if you use the following code:
int* a = (int*)list_to_array(list1);
int* b = (int*)list_to_array(list2);
printf("%d" (int)a);
You will (likely) see the first element of b. This is not guaranteed - You may also get a segmentation fault. Other code between the assignment to a and its use will also overwrite the contents of the memory you access in your printf statement.
You need to allocate your memory with the same scope as a.
// Prototype
void* list_to_array(SList* list, void* dest_array);
// C99 (Or use malloc)
void* array[list_get_length(list)];
int* a = (int*)list_to_array(list, array);
other_functions();
// Works every time!
printf("%d" (int)a);
The less serious problem is the fact that you're not incrementing your pointer by the correct amount. You need to use the sizeof() operator. Alternatively, you can access your array elements with [].
int* a = (int*)list_to_array(list, array);
printf("%d" a[1]); //Prints second element of a
a += sizeof(int) * 2;
printf("%d" (int)a); //Prints third element of a
You need to be careful with pointers, declare it this way:
int main()
{
int *array;
int i;
i = size(list);
array = list_to_array(list, i);
...
free(array);
}
int size(t_list *list)
{
int i;
i = 0;
while (list)
{
i++;
list = list->next;
}
return (i);
}
int *list_to_array(t_list *list, int size)
{
int *array;
int i;
t_list *temp;
i = 0;
if (list == NULL)
return (NULL);
array = (int*)malloc(sizeof(int) * size + 1);
temp = list;
while (temp)
{
array[i] = temp->data;
temp = temp->next;
i++;
}
return (array);
}

Resources