How to fill an array of structs in a function [duplicate] - c

This question already has answers here:
How do I modify a pointer that has been passed into a function in C?
(7 answers)
Closed 4 years ago.
I'm trying to create an array of a structure in an external function "add", and print it's fields, but when I get back to the main function "arr" it is still NULL.
I'm confused because I've been creating arrays in external functions many times and it worked.. probably this time the dynamic memory allocation is messing the things up. Can I please get an advice on this matter?
Thanks!
typedef struct {
char* id;
char gender;
char *name;
}Member;
void add(Member arr[], int size);
void print(Member arr[], int *size);
int main()
{
char temp[100];
int size=0;
Member *arr = NULL;
Member *data = (Member*)malloc(sizeof(Member));
//scan fields
gets(temp);
data->id = (char*)malloc((strlen(temp) + 1) * sizeof(char));
strcpy(data->id, temp);
gets(temp);
data->gender = temp;
gets(temp);
data->name = (char*)malloc((strlen(temp) + 1) * sizeof(char));
strcpy(data->name, temp);
add(data, &arr, &size);
print(arr, &size);
return 0;
}
void add(Member *data, Member arr[], int *size)
{
arr = (Member*)realloc(arr, (*size + 1) * sizeof(Member));
arr[*size] = *data;
}
void print(Member arr[], int *size)
{
for (int i = 0;i < *size;i++)
{
puts(arr->id);
puts(arr->gender);
puts(arr->name);
}
}

Imagine code like this:
#include <stdio.h>
void f(int i){
i++;
}
int main(){
int i = 3;
f(3);
printf("%d\n", i);
}
We all know that f() incremented its local copy of i, not the variable that was passed into f() to initially set that value. With that having been said, let's take another look at your add():
void add(Member *data, Member arr[], int *size)
{
arr = (Member*)realloc(arr, (*size + 1) * sizeof(Member));
arr[*size] = *data;
}
When arr is passed into the function, it contains a memory address of the current arr, which starts as NULL. But just like when we change the local value of i in f() above, setting arr to a new value within add() only changes the local value; it does not change main()'s arr.
We also know that if we pass a function an address of data we want it to change, the function can then change the data at that address and the data at that address will reflect the change in the calling function:
#include <stdio.h>
void f(int * i){
*i = *i + 1;
}
int main(){
int i = 3;
f(&i);
printf("%d\n", i);
}
The same logic applies ( though it gets more confusing) when you want to change a pointer's value; send that pointer's address! Let's start with a very simple case:
#include <stdio.h>
#include <stdlib.h>
void f(int** i){
*i = (int*)malloc(sizeof(int));
**i = 99;
}
int main(){
int *i = NULL;
f(&i);
printf("%d\n", *i);
}
Here we create a pointer to an int in main, and initialize it to NULL. Then we send the address of that pointer (that is, the address we stored the NULL) to f(), which (like in your program) allocates some memory and puts the address of the newly allocated pointer _at the address of main's i. Now, the data stored at &i has changed, and dereferencing i from main() will dereference the newly allocated address.
In your code, just as in mine, you'll have to change the way you're passing arr to add() as well as how you interact with it - an exercise you'll get the most out of thinking through yourself. But in short, something like this should get you started:
pass add arr's address, not the address it stores.
Store new address of reallocated memory back to the same address, that is, &arr
make sure to update add() to dereference the pointer to a pointer twice to set the member at the address stored at the address &arr.

Related

inconsistent results for struct with array in c

I'm new to programming and to C, and I just learned about structs. I'm trying to use them to make an array which can change size as required (so, if the array gets full, it creates a new array double the size, copies the old array into the new one and deletes the old one). All I've done so far is create the struct and the functions for setting it up, and already I'm having problems. The main problem is that, sometimes when I run it it does exactly what I expect it to, which is create the struct, return a pointer to said struct, and then print all elements of the contained array. Other times when I run it, it does nothing at all! I don't get how it can work sometimes, and sometimes not! Obviously i'm doing something really wrong, but I can't work out what. Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int cap;
int used;
void (*cpy) (int *, const int *, int);
//void (*append) (int);
int array[];
} dynArray;
dynArray * new_dynArray(int *, int);
void copy(int *, const int *, int);
int main(void) {
int start_arr[] = {1,2,3,4,5,6};
// create new dynArray, pass start array and number of elemnts
dynArray *arr = new_dynArray(start_arr, \
sizeof(start_arr) / sizeof(start_arr[0]));
// print all elements of dynArray
for (int i=0; i<(arr->used); i++) {
printf("%d, %d\n", arr->array[i], i);
}
free(arr);
return 0;
}
dynArray * new_dynArray(int init_arr[], int size) {
//printf("%d", size);
// if number of elements >= 4 then dynArray size is double, else 8
int init_cap = (size >= 4) ? 2 * size : 8;
// create pointer with enough space for struct and the actual array
dynArray *arr = (dynArray *) malloc(sizeof(dynArray) + init_cap );
arr->cap = init_cap;
arr->used = size;
// assign address of funciton copy to arr->cpy
arr->cpy = copy;
// call the function, to copy init_arr to arr->array
arr->cpy(arr->array, init_arr, size);
return arr;
}
void copy(int dest[], const int src[], int src_size) {
// just copy initial array to new array
int i;
memcpy(dest, src, src_size*sizeof(int));
/*
for (i=0; i<src_size; i++) {
dest[i] = src[i];
printf("%d\n", dest[i]);
}*/
}
So I call init_dynArray, sending a normal array and the number of elements in the array. init_dynArray uses malloc to create space in memory for the struct + the inintal size of the array, set up everything in the struct and copy the array, and then return a pointer to it. I don't get how it can only work some of the time. Hope yuo guys can help, thanks!
The problem in your code is on this line:
dynArray *arr = (dynArray *) malloc(sizeof(dynArray) + init_cap );
You need to multiply init_cap by sizeof(int)
dynArray *arr = (dynArray *) malloc(sizeof(dynArray) + sizeof(int)*init_cap );
You should also use size_t for the init_cap's type.
Note: Storing a pointer to the copying function inside the struct would be useful if your dynamic array consisted of opaque elements that require non-trivial copying. Since copying ints can be accomplished with a simple memcpy, there is no need to store a function pointer in dynArray.

Returning pointers back to itself

My coding assignments came with it's header file, meaning we need to use the same data types, and not vary anything.
There is a lot of pointers, (mainly a lot of void *). Meaning things are confusing, more than difficult.
we have to do a separate function, just to increment the value referenced by a pointer. But given the nature of program, I don't want to constantly make new pointers.
The code is as follows:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void* intal_create(const char* );
void* intal_increment(void* );
void *intal_create(const char* str)
{
int a;
a=atoi(str);
return &a;
}
void *intal_increment(void *intal)
{
int *a= (int *)intal;//new pointer;
++*a;
//value referenced has been incremented;
*(int *)intal=*a;
return intal;
}
int main()
{
int * x;// void * return a pointer, need a pointert to int to pick it up
char *dummy;
gets(dummy);
x=(int *)intal_create(dummy);
printf("integer return is %d\n",*(int *)x);
printf("address stored is %p\n",(int *)x);
x=(int *)intal_increment(x);
printf("integer return is %d\n",*(int *)x);
printf("address stored is %p\n",(int *)x);
}
I wanted x to be the parameter called, and also for it to store the return value. The printf address is merely for my understanding.
The segmentation faults never end, and from my understanding, I'm just returning a pointer and asking a pointer to stop the return pointer
By incorporating all the comments. Mainly allocating memory to dummy before passing it gets() function and allocating memory in heap for the return pointer of intal_create.These two fixes solve the issue. Have a look at the following code for reference.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void* intal_create(const char* );
void* intal_increment(void* );
void *intal_create(const char* str)
{
int *a = (int *)malloc(sizeof(int));
*a = atoi(str);
return a;
}
void *intal_increment(void *intal)
{
//Here i am not allocating
int *a = (int *)intal;//new pointer;
(*a)++;
return intal;
}
int main()
{
int * x;// void * return a pointer, need a pointert to int to pick it up
char dummy[20] = {0};
fgets(dummy,5,stdin);
x = (int *)intal_create(dummy);
printf("integer return is %d\n",*x);
printf("address stored is %p\n",(void*)x);
x=(int *)intal_increment(x);
printf("integer return is %d\n",*x);
printf("address stored is %p\n",(void *)x);
//Make sure you deallocate the memory allocated in the intal_create function.
free(x);
}

Why dynamically allocated memory of a function parameter is lost when exiting the function? [duplicate]

This question already has answers here:
How do I modify a pointer that has been passed into a function in C?
(7 answers)
Closed 8 years ago.
I want to make a function in C that would dynamically allocate memory for a pointer in parameter of the function.
#include <stdio.h>
#include <stdlib.h>
int allocate(char * arr, int size){
int code = -1;
arr = malloc(size);
if(arr != NULL) code = size;
return code;
}
void main(){
char * array;
if(allocate(array,4) != -1){
printf("allocated!\n");
if(array == NULL) printf("Oops it actually didn't allocate!\n");
}
}
When I execute the program; it will only display "allocated!" and "Oops it actually didn't allocate!". That means the memory allocation did happen (because the return code of the function is not -1. But then when I check if array is equal to NULL; it actually is!
This is a programming problem that I've had and sadly in some cases I can't use a workaround like this char * allocate(char * arr, int size); and assigning the return value to char * array.
You lack a level of indirection, you need char**.
Excuse the bad formatting, I write from my phone.
Char* array, array is bound to a memory slot (that will contain a value that points to another memory slot that would be interpreted as a char).
So you copy that value to the function and modify that value locally in allocate, but the modification never reaches the outside scope.
#include <stdio.h>
#include <stdlib.h>
int allocate(char ** arr, int size){
int code = -1;
*arr = malloc(size);
if(*arr != NULL) code = size;
return code;
}
void main(){
char * array;
if(allocate(&array,4) != -1){
printf("allocated!\n");
if(array == NULL) printf("Oops it actually didn't allocate!\n");
}
}
Not done C in something like 10 years but it should be OK.
You can allocate memory inside your function and return the address as shown below
There are also changes like instead of void main it should be int main()
#include <stdio.h>
#include <stdlib.h>
char *allocate( int size){
char *arr;
arr = malloc(size);
return arr;
}
int main(){
char * array;
if((array = allocate(4)) != NULL){
printf("allocated!\n");
}
return 0;
}
Arguments to functions in C are passed by value. This means following function has no sense:
void f(int x) {
x = 1;
}
int y = 0;
f(y);
// y is still 0
When f is invoked, y is copied to x. Any change to x changes that copy and won't affect y. To work around this, you need to either use return value or pass a pointer to y:
void f(int* x) {
*x = 1;
}
int y = 0;
f(&y);
// y is now 1
Here x is still a copy (of a pointer) but it points to y. Changes to x wont be visible outside that function. But changing *x modifies y.
The same rules apply to pointer arguments. You just need one more * for arguments you want to modify:
int allocate(char** arr, int size) {
*arr = malloc(size);
}
char *ptr;
allocate(&ptr);
Also note that checking array for NULL isn't sufficient here, because a locally defined variable could contain garbage value (thus, not being NULL). You have to assign NULL to it before the allocation:
char *array = NULL;

Malloc-ating inside a Function vs malloc-ating in Main

I have a struct that contains an int pointer
struct mystruct {
int *myarray;
};
I want to make a function that mallocates for mystruct and also initializes myarray. But, when I try to access an element of myarray, I get a seg. fault
void myfunction(struct mystruct *s, int len) {
s = malloc(sizeof(mystruct));
s->myarray = malloc(sizeof(int) * len);
int i;
for (i=0; i<len; i++) {
s->myarray[i] = 1;
}
}
main() {
struct mystruct *m;
myfunction(m, 10);
printf("%d", m->myarray[2]); ////produces a segfault
}
However, mallocating m in main seemed to solve my problem.
Revised Code:
void myfunction(struct mystruct *s, int len) {
int i;
s->myarray = malloc(sizeof(int) * len);
for (i=0; i<len; i++) {
s->myarray[i] = 1;
}
}
main() {
struct mystruct *m = malloc(sizeof(mystruct)); //this was in myfunction
myfunction(m,10);
printf("%d", m->myarray[2]); ///Prints out 1 like I wanted
}
Why did the 2nd attempt work and why did the first attempt not work?
The problem is that the first version assigns the result of malloc to a parameter, which effectively a local variable; the assigned value vanishes when the function returns
So, an alternative is to pass to the function a pointer to the location where you want to store the result of malloc. This is named pps in the code below. At the beginning of the function we do the malloc and assign to a local variable s. Then we do things with s. Then, just before the function exits, we assign the local variable s to the location pointed to by the parameter pps. *pps = s;
void myfunction(struct mystruct **pps, int len) { // note double "**"
struct mystruct *s = malloc(sizeof(mystruct);
s->myarray = malloc(sizeof(int) * len);
int i;
for (i=0; i<len; i++) {
s->myarray[i] = 1;
}
*pps = s; // now pass the alloc'ed struct back to main through parameter pps
}
Now, back in main we pass &m to the function. This passes a pointer to m to the function. When the function returns, the local variable m holds the value returned by malloc and passed through the parameter pps.
main() {
struct mystruct *m;
myfunction(&m, 10); // PASS THE ADDRESS OF m, not m itself
printf("%d", m->myarray[2]); // this will work now
}

dynamic memory created inside a function [duplicate]

This question already has answers here:
C Programming: malloc() inside another function
(9 answers)
Closed 5 years ago.
I would like to know the technical reason(in terms of memory) why this piece of code will not work:
#include <stdio.h>
#include <stdlib.h>
int* fun(int*);
int main()
{
int a=5;
int* ptr;
// ptr=(int*)malloc(sizeof(int));
fun(ptr);
a=*ptr;
printf("\n the val of a is:%d",a);
return 0;
}
void fun(int* ptr)
{
ptr = (int*)malloc(sizeof(int));
*ptr = 115;
}
Why will this not work? I thought that the heap(more importantly the addresses) is common to all the function's variables in the stack .
Also, why would this work.
If i comment the memory allocation inside the function fun and uncomment the one in main . It works fine.
In C, everything is passed by value.
What you are passing to fun() is a copy of the pointer you have in main().
That means the copy of ptr is aimed at the allocated memory, and that memory set to 115.
The ptr in main() still points at an undefined location because it has never been assigned.
Try passing a pointer to the pointer, so that within fun() you have access to the pointer itself:
#include <stdio.h>
#include <stdlib.h>
int* fun(int**); // <<-- CHANGE
int main()
{
int a=5;
int* ptr;
// ptr=(int*)malloc(sizeof(int));
fun(&ptr); // <<-- CHANGE
a=*ptr;
printf("\n the val of a is:%d",a);
return 0;
}
int* fun(int** another_ptr) // <<-- CHANGE
{
*another_ptr = (int*)malloc(sizeof(int)); // <<-- CHANGE
**another_ptr = 115; // <<-- CHANGE
return *another_ptr;
}
The other option would be to make fun() actually return the updated pointer (as advertised), and assign this to ptr:
#include <stdio.h>
#include <stdlib.h>
int* fun(int*);
int main()
{
int a=5;
int* ptr;
// ptr=(int*)malloc(sizeof(int));
ptr = fun(ptr); // <<-- CHANGE
a=*ptr;
printf("\n the val of a is:%d",a);
return 0;
}
int* fun(int* another_ptr)
{
another_ptr = (int*)malloc(sizeof(int));
*another_ptr = 115;
return another_ptr; // <<-- CHANGE
}
Edit: I renamed the variable in fun() to make it clear that it is different from the one you use in main(). Same name doesn't mean anything here.
The fun() function parameter is a copy of the variable you passed into fun(). So when you do:
ptr = (int*)malloc(sizeof(int));
*ptr = 115;
you only change that copy. You should change the function signature:
int* fun(int** ptr)
{
*ptr = (int*)malloc(sizeof(int));
**ptr = 115;
}
and change how you call it accordingly.
You are confused about several things here, but one easy way of writing the function is:
int * fun()
{
int * ptr = (int*)malloc(sizeof(int));
* ptr = 115;
return ptr;
}
You are now responsible for freeing the memory, so in main():
int * ip = fun();
printf( "%d", * ip );
free( ip );
The alternative is to pass the address of apointer (a pointer to a pointer) to the function:
void fun( int ** pp )
{
* pp = (int*)malloc(sizeof(int));
** pp = 115;
}
then your code in main() looks like:
int * ip;
fun( & ip );
printf( "%d", * ip );
free( ip );
I think you can see that the first function is simpler to use.
You need to pass the address of the pointer in main if you want to change it:
fun(&ptr);
(and change fun appropriately, of course)
At the moment, it's changing the local variable ptr inside the function, and of course that change doesn't magically appear anywhere else.
You're passing the ptr by value to fun. fun will recieve a copy of ptr which will be modified. You need to pass ptr as int**.
void fun(int** ptr)
{
*ptr = (int*)malloc(sizeof(int));
**ptr = 115;
}
and call it with:
fun(&ptr);
(I also removed the return value from fun since it wasn't used)
The variable int* ptr is passed by value to the function fun. So the value assigned to ptr inside the function using ptr = (int*)malloc(sizeof(int)); will not be reflected outside the function. So when you do a = *ptr; in main() you are trying to use an un-initialized pointer. If you want to to reflect the changes done to ptr outside the function then you need to change the signature of fun to fun(int** ptr) and do *ptr = (int*)malloc(sizeof(int));
Remember that if you want a function to modify the value of an argument, you must pass a pointer to that argument. This applies to pointer values; if you want a function to modify a pointer value (not what the pointer points to), you must pass a pointer to that pointer:
void fun (int **ptr)
{
/**
* Do not cast the result of malloc() unless you are
* working with a *very* old compiler (pre-C89).
* Doing so will supress a valuable warning if you
* forget to include stdlib.h or otherwise don't have
* a prototype for malloc in scope.
*
* Also, use the sizeof operator on the item you're
* allocating, rather than a type expression; if you
* change the base type of ptr (say from int to long),
* then you don't have to change all the corresponding
* malloc() calls as well.
*
* type of ptr = int **
* type of *ptr = int *
* type of **ptr = int
*/
*ptr = malloc(sizeof **ptr);
*ptr = 115;
}
int main(void)
{
int *p;
fun(&p);
printf("Integer value stored at %p is %d\n", (void *) p, *p);
return 0;
}
BTW, you have a type mismatch in your example; your initial declaration of fun returns an int *, but the definition returns void.

Resources