using calloc on an array of struct, in a function - c

This is my struc
//global
typedef struct {
char idCode[MATLENGTH + 10];
char *name;
} stud;
In main, I do this
Int main() {
stud *students;
createStudentArray(students);
....
What I'm trying to do, is:
-pass the array (*student) to a function
-make the function alloc. the array
This is the function i wrote
createStudentArray(stud *students) {
//I get this value from a file
int nstud = 3;
students calloc(nstud, sizeof(students));
return;
}
the problem is:
-when I try to assign any value to a students field, it doesn't work
ex.
Int main() {
stud *students;
createStudentArray(students);
....
strcpy(students[0].name, "Nicola"); //this is where i get the error
My guess is that, in some way, I'm not allocating the array correctly, because, when i try to do
strcpy(students[0].name, "Nicola");
in the createStudentArray function, it wortks just fine. So it looks like I am passing the array by value, and not by reference.
Thanks in advance for your help.

This is because students pointer is passed by value. Any assignment to it inside createStudentArray remains invisible to the caller.
You have two options to fix this:
Return the newly allocated pointer and assign it in the caller, or
Accept a pointer-to-pointer, and assign with an indirection operator.
Here is the first solution:
stud *createStudentArray() {
//I get this value from a file
int nstud = 3;
stud *students = calloc(nstud, sizeof(students));
...
return students;
}
...
stud *students = createStudentArray();
Here is the second solution:
void createStudentArray(stud ** pstudents) {
//I get this value from a file
int nstud = 3;
*pstudents = calloc(nstud, sizeof(students));
...
}
...
stud *students;
createStudentArray(&students); // Don't forget &

In C, arguments are passed by values, not by reference.
Changes made to arguments in callee function won't affect variables in caller function.
To modify caller's variables from callee function, use pointers.
createStudentArray(stud **students) {
//I get this value from a file
int nstud = 3;
*students = calloc(nstud, sizeof(stud)); // this should be sizeof(stud), not students
return;
}
int main() {
stud *students;
createStudentArray(&students);
....

It's because in your function only the local pointer will be assigned the new address to the chunk of your allocated memory.
To get it from outside you need to use a pointer to a pointer like so:
createStudentArray(stud **students) { ... }
and call it like this:
createStudentArray(&students);

you are correct, students is passed as value to CreateStudentsArray(), either you change it to accept a **stud or you make it return a pointer to the created array.
My suggestion is to use a pointer to a pointer and use the * operator to dereference it.
createStudentArray(stud **students) {
//I get this value from a file
int nstud = 3;
*students = calloc(nstud, sizeof(students));
return;
}
void main(){
stud = *students;
...
createStudentsArray(&students);
...
strcpy....

Related

Dynamic array in structs

I have a problem using dynamic memory in C.
I am creating a struct whose data is a number and a pointer to another struct (in short, an array of struct). The goal is for the parent struct to store an array of another struct using dynamic memory.
The problem I have is to access the cells of the created array, because I don't know if it's due to syntax issues (I'm new to C), or that I'm creating the array wrong, I can't modify the information contained in each cell of the contained array inside the parent struct. I can only modify by default the first cell.
This is my code, any idea or suggestion will be appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char string[64];
void* date;
void* colour;
} DataState;
typedef struct {
int number;
DataState* array;
} Book;
Book* makeBook (int number){
int a=5;
void* auxiliary=&a;
Book* book_A=(Book*)(malloc(sizeof(Book)));
book_A->number=number;
book_A->array=(DataState*)(malloc(number*sizeof(DataState))); //creating array of structs inside main struct.
//And what I want to do is something like this, modify the information contained in cells of the array of structs of the main struct.
book_A->array[3]->date=auxiliary;
return book_A;
}
From already thank you very much.
Here:
book_A->array[3]->date=auxiliary;
you assign a value of auxiliary to the 3rd element of an array, but auxiliary is defined as
void* auxiliary=&a;
while a is an automatic variable in your function.
Automatic variables disappear at the return from the function, hence the pointer assigned to book_A->array[3]->date becomes invalid as soon as the return is executed.
If you want the data saved in a to remain valid after makeBook returns, then you must allocate it in more persistent storage than automatic. You've essentially done this:
int* foo()
{
int a = 5;
// WRONG, cannot return the address of a local variable
return &a;
}
int main(void)
{
int* a_addr = foo();
// WRONG, invokes undefined behavior
printf("a = %d\n", *a_addr);
}
If you want a to persist outside of foo, a possible option is:
int* foo()
{
int* a = malloc(sizeof *a);
// always check the return value of malloc
if (a != NULL)
{
*a = 5;
}
// this is ok. `a` is still in automatic storage, but this _returns_ its
// value, which is a pointer to data _not_ in automatic storage.
return a;
}
int main(void)
{
int* a_addr = foo();
// still must check here, malloc in `foo` could have failed. Probably
// better to design an architecture where you only check validity once
if (a_addr != NULL)
{
printf("a = %d\n", *a_addr); // prints a = 5
// don't forget to `free(a_addr)` when you're done with it, or you can
// let the OS clean up the memory when the process exits.
}
else
{
// handle error how you want
fprintf(stderr, "Out of mem!\n");
}
return 0;
}

Passing a struct to a function

So I am trying to pass my struct to a function and I am also trying to assign my variable to the struct, which does not seem to work. I don't know what's wrong with it either.
This is how my code looks:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ACE 1;
#define CardSize 52
#define colors 4
struct MyCards {
int *cards;
char *color[4];
};
void count(struct MyCards record);
int main() {
struct MyCards record;
count(record);
system("pause");
return 0;
}
void count(struct MyCards record) {
int i, j, f;
// I actually want to put this variable and the values into the struct, how do i do it?
char *color[4] = { "Diamon", "Heart", "Spade", "Clubs" };
record.cards = malloc(CardSize * sizeof(int));
for (f = 0; f < 4; f++) {
for (i = 0; i < 13; i++) {
record.cards[i] = (i % 13) + 1;
printf("%d of %s\n", record.cards[i], color[f]);
}
}
}
As you might see, the thing I commented out, I also want to put that variable AND the values that I have assign to it, but I dont know how to do that, would love some help there as well.
C uses pass-by-value. record inside count is a different variable to record in main - a copy is made when you call the function.
If you want main to see the changes you either need to return the changed object (in which case you wouldn't pass it in in the first place, in this example), or use pass-by-reference which you implement by passing a pointer to the object.
Returning the object would look like:
struct MyCard count(void)
{
struct myCard record;
// ... do stuff with record ...
return record;
}
Passing by reference would look like:
void count(MyCard *p_record)
{
// ... do stuff with (*p_record)
}
Also you want record.color[f] = color[f]; as the first line of the f loop. And (as discussed last time you posted about this code) you should be using string or char const *, not char *.
You have to pass a pointer to the struct in order to edit it, or you will edit the variable only in the stack of the function, which will be deleted once the function returns. Try passing &record to your function.
Also change your prototype: you have to accept a pointer to the struct.
When you have a pointer, to resolve the struct you have to use the -> operator. Let's do an example:
records->cards[i] = ...

struct as an argument of sucessive functions... little error need fixing. (C)

So I got that:
typedef struct {
int score;
char init;
} Student;
int changestuff(Student *students) {
students->score = 100;
students++;
students->score = 200;
changestuffagain(students);
}
int changestuffagain(Student *students) {
students->score = 100;
students++;
students->score = 200;
}
int main() {
Student students[2];
changestuff(students);
}
I call the function changestuff and it modifiys correctly the socre of players[0] and players[1], but my little problem, is that when I call the second function changestuffagain, the argument does not pass players[0] and players[1], it passes just players[1] (the value of players at the moment of calling changestuffagain)
How i could fix that?
Well, that's what you told it to do. When your changestuff function calls changestuffagain, you've incremented the value of students, so it now points to the second Student in the array. If you want to keep the pointer in the same place, you should pass the non-incremented value.
When you call students++ you jump to the next memory position where it is allocated a Student(in your case students[1]). You could decrement students before making the call of changestuffagain().
For example:
students--;
changestuffagain(students);

understanding how to dynamically create an array of structure and access its elements

I need to pass the address of a pointer to a structure to a function, which inturn will dynamically allocate the memory for an array of structures and fill in the values.
Now from my calling method, once i return from the func1, i should be able to iterate through the array of structure and display the value of the structure variables.
Can someone explain how to pass the address of the pointer to the structure, also iterating through the array of structures created dynamically ?
my sample code looks like this:
struct test {
int a;
int b;
};
void func1(int *n,struct test **testobj)
{
n=5;
*testobj = (struct test*) malloc(n*sizeof(struct test));
for(i=0;i<n;i++)
{
(*testobj)[i].a=1;
(*testobj)[i].b=2;
}
}
int main()
{
struct test testobj;int n;
func1(&n,&testobj);
for(i=0;i<n;i++)
{
printf("%d %d",(*testobj)[i].a,*testobj)[i].b);
}
free(testobj);
}
In main() define a pointer to a test structure:
struct test *testPtr;
To take the address of that pointer use the & address-of operator:
&testPtr;
This returns the address of the pointer and has type struct test **
You can then pass this into your function func1, which does the correct allocation (although casting malloc() is generally considered bad practice - Do I cast the result of malloc?). Other than that func1() looks good... the line...
*testobj = malloc(n*sizeof(struct test));
... is correct. *testobj dereferences your double pointer that you got by doing &testPtr, and stores the address of the new memory in your pointer. You are also correct when you dereference your double-pointer using (*testobj)[i] because [] has higher precedence than * you needed to (as you've correctly done) surround the dereference with brackets to make sure that happens before you take the index.
Thus, when func1() returns the pointer testPtr should now point to the array of n test structures you allocated and can be accessed using testPtr[i].a etc.
EDIT: Your for loop should become
for(i=0;i<n;i++)
printf("%d %d", testobj[i].a, testobj[i].b);
Your original for loop should have given you compilation errors? In the original code testobj is not a pointer, therefore dereferencing it should not be possible.
So the summary answer is in main() declare testobj as a pointer and then access the array elements as testobj[n] :)
EDIT: As eric has pointed out, remove n=5; from func1(). I think you meant *n=5 perhaps as some kind of debugging step... You probably mean to use n as the input to the function to say how many objects you want in your structure array. Either initialise n or perhaps re-define func1() to be
void func1(int n,struct test **testobj) // n is no longer a poitner, just a number
create your array of pointers to structures in declaration step itself and simply pass it to the function
struct test *testobj[10];
func1(&n,testobj);
This passes the whole array of pointers to the function
It isn't entirely clear which version you're asking for, but one of these should cover it:
/* allocate some number of tests.
*
* out_n: out parameter with array count
* returns: an array of tests
*/
struct test* allocate_some_tests(int *out_n) {
int n = 5; /* hardcoded, random or otherwise unknown to caller */
*out_n = n
struct test *t = malloc(n * sizeof(*t));
while (n--) {
t[n].a = 1;
t[n].b = 2;
}
return t;
}
/* allocate a specific number of tests.
*
* n: in parameter with desired array count
* returns: an array of tests
*/
struct test* allocate_n_tests(int n) {
struct test *t = malloc(n * sizeof(*t));
while (n--) {
t[n].a = 1;
t[n].b = 2;
}
return t;
}
Note that you can just return the allocated array, you don't need a pointer-to-pointer here.
As for calling them, and iterating over the result:
void print_tests(struct test *t, int n) {
for (; n--; t++)
printf("{%d, %d}\n", t->a, t->b);
}
int main()
{
int count1; /* I don't know how many yet */
struct test *array1 = allocate_some_tests(&count1);
print_tests(array1, count1);
int count2 = 3; /* I choose the number */
struct test *array2 = allocate_n_tests(count2);
print_tests(array2, count2);
}
Your code appears pretty much ok to me.
only edit that should make it fine is--
in place of
struct test testobj;
put the following code
struct test *testobj;
and keep the remaining as it is..!
here's the working version of what's required, here the memory is allocated in the called function just as required
#include <stdlib.h>
#include <stdio.h>
struct tests {
int a;
int b;
};
void func1(int *n,struct tests **testobj)
{
int i;
*n=5;
*testobj = (struct tests*) malloc((*n)*sizeof(struct tests));
for(i=0;i<(*n);i++)
{
(*testobj)[i].a=1;
(*testobj)[i].b=2;
}
}
int main()
{
int i;
struct tests *testobj;int n;
func1(&n,&testobj);
for(i=0;i<(n);i++)
{
printf("%d %d",(testobj)[i].a,testobj[i].b);
}
free(testobj);
}

Passing pointers to arrays to functions

I am storing my information in an array of pointers to structs. In other words, each element of the array is a pointer to a linked list.
I don't know how long the array should be, so instead of initializing the array in my main() function, I instead intialize the double pointer
struct graph** graph_array;
Then once I obtain the length of the array, I try to initialize each element of graph_array using a function GraphInitialize:
int GraphInitialize(struct graph* *graph_array,int vertices)
{
struct graph* graph_array2[vertices+1];
graph_array = graph_array2;
int i;
for (i=0;i<vertices+1;i++)
{
graph_array[i] = NULL;
}
return 0;
}
But for some reason this is not returning the updated graph_array to main(). Basically, this function is updating graph_array locally, and no change is being made. As a result, any time I try to access an element of graph_array it seg faults because it is not initialized. What am I doing wrong?
Edit: Following the convo with Tom Ahh I should add something else that makes this more confusing.
I don't call GraphIntialize directly from main(). Instead, I call getdata() from main, and pass a pointer to graph_array to getdata as shown below.
getdata(argc, argv, vertpt, edgept, &graph_array)
int getdata(int argc, char *argv[], int *verts, int *edges, struct graph* **graph_array)
Then getdata gets the number of vertices from my input file, and uses that to call GraphInitialize:
if ((GraphInitialize(&graph_array, *verts)) == -1)
{
printf("GraphCreate failed");
return 0;
}
This results in an error: "expected 'struct graph 3ASTERISKS' but argument is of type 'struct graph 4ASTERISKS'.
When you assign something to graph_array, you simply assign it to its local copy. The changes made to it in the function will not be see-able by the caller. You need to pass it by pointer value to be able to change its value. Change your function prototype to int GraphInitialize(struct graph ***graph_array,int vertices) and when you call it, use GraphInitialize(&graph_array, 42).
Second problem in your code is when you create graph_array2, you declare it to be local to your GraphInitialize() function. Thus, when exiting your function, graph_array2 is destroyed, even if you assigned it to *graph_array. (the star dereferences the pointer to assign it to the value it points to).
change your assignation to *graph_array = malloc(sizeof(*graph_array) * vertices); and you should be fine.
Memory is divided into two parts, the stack and the heap. Malloc will give you back a chunk of memory from the heap, which lives on between functions, but must be freed. Thus your program must be careful to keep track of the malloced() memory and call free() on it.
Declaring a variable graph_array2[vertices+1] allocates a local variable on the stack. When the function returns the stack pointer is popped "freeing" the memory allocated in the function call. You don't have to manage the memory manually, but when the function call is over it no longer exists.
See here for some discussion of the two allocation styles:
http://www.ucosoft.com/stack-vs-heap.html
You're using C99-style local array allocation. The array disappears when the function returns. Instead you need to use malloc() to allocate memory that will persist after the function. You can use typedefs to make your code more readable:
typedef struct graph_node_s { // linked list nodes
struct graph_node_s *next;
...
} GRAPH_NODE;
typedef GRAPH_NODE *NODE_REF; // reference to node
typedef NODE_REF *GRAPH; // var length array of reference to node
GRAPH AllocateGraph(int n_vertices)
{
int i;
GRAPH g;
g = malloc(n_vertices * sizeof(NODE_REF));
if (!g)
return NULL;
for (i = 0; i < n_vertices; i++)
g[i] = NULL;
return g;
}
You have two problems.
First, graph_array2 has auto extent, meaning that it only exists within its enclosing scope, which is the body of the GraphInitialize function; once the function exits, that memory is released, and graph_array is no longer pointing anywhere meaningful.
Second, any changes to the parameter graph_array are local to the function; the changes won't be reflected in the caller. Remember, all parameters are passed by value; if you pass a pointer to a function, and you want the value of the pointer to be modified by the function, you must pass a pointer to the pointer, like so:
void foo(int **p)
{
*p = some_new_pointer_value();
return;
}
int main(void)
{
int *ptr = NULL;
foo(&ptr);
...
}
If you intend for InitializeGraph to allocate the memory for your array, you'll need to do something like this:
int InitializeGraph(struct graph ***graph_array, int vertices)
{
*graph_array = malloc(sizeof **graph_array * vertices);
if (*graph_array)
{
int i;
for (i = 0; i < vertices; i++)
{
(*graph_array}[i] = NULL; // parentheses matter here!
}
}
else
{
return -1;
}
return 0;
}
int main(void)
{
int v;
struct graph **arr;
...
if (GraphInitialize(&arr, v) == 0)
{
// array has been allocated and initialized.
}
...
}
Postfix operators like [] have higher precedence than unary operators like *, so the expression *arr[i] is interpreted as *(arr[i]); we're dereferencing the i'th element of the array. In GraphInitialize, we need to dereference graph_array before subscripting (graph_array isn't an array, it points to an array), so we need to write (*graph_array)[i].

Resources