Crash while using array of function pointers in a structure - c

When I use an array of function pointers in a structure, the program crashes.
#include <stdio.h>
typedef void (*FUNCPTR)(void);
void Function_1()
{
printf(" In Function_1 \n");
}
void Function_2()
{
printf(" In Function_2 \n");
}
typedef struct St_FUNCPTR
{
FUNCPTR xxx[2];
}ST_FUNCPTR;
FUNCPTR fnptr1[2] =
{
Function_1,
Function_2
};
ST_FUNCPTR fnptr =
{
fnptr1
};
/* The intention is to call Function_1(); through array of function
pointers in the structure. */
int main()
{
// to call Function_1();
fnptr.xxx[0]();
return 0;
}
If the structure is defined like below, it works fine.
ST_FUNCPTR fnptr =
{
{Function_1,Function_2},
};
Where is my problem?

My bets are here:
ST_FUNCPTR fnptr =
{
fnptr1
};
What you are trying to do is initialize structure, which element is an array with an array. fnptr1 is an array of pointers to function.
With your edit it's for sure. You can't initialize array with another array like you cant say that int a1[10]=int b[10].
For clarification: fnptr1 here is pointer constans to pointer to function.And it goes to xxx[0] being converted to pointer to the function by the way.
What code does next, it takes address of that pointer, fnptr.xxx[0] , treats it like a pointer to function and calls it with (). But theres no function under that address, just pointer to the function, so there's a crash.

Related

Invoking function from the 2d array of function pointers

#include <stdio.h>
void test1(){ printf("test1\n"); }
void test2(){ printf("test2\n"); }
void test3(){ printf("test3\n"); }
void test4(){ printf("test4\n"); }
void (*hv_ptr[2])() = {test1,test2};
void (*dev_ptr[2])() = {test3,test4};
void (**ptr[2])() = {hv_ptr, dev_ptr};
int main() {
ptr[0][0];
ptr[0][1];
ptr[1][0];
ptr[1][1];
return 0;
}
I have declared the array of pointers to function pointers. But with this code, functions are not getting called. Kindly let me know what is going wrong here.
ptr is an array of pointers to pointers to functions, so ptr[0] is a pointer to a pointer to a function, so ptr[0][0] is a pointer to a function.
Thus, in the expression statement ptr[0][0];, the expression is just a pointer to a function. It does not contain a function call.
ptr[0][0](); is a call to the function pointed to by ptr[0][0].

How to correctly pass a pointer to an array of pointers?

In my code I need to pass a pointer to an array of pointers as a function argument. Code snippets:
struct foo * foos[] = {NULL, NULL, NULL};
some_function(&foos);
and:
static void some_function(struct foo ** foos) {
foos[0] = get_a_foo();
/* some more code here */
}
This works as expected (after some_function() returns, foos[] contains the pointers I set there), but I get a compiler warning for the call to some_function():
note: expected ‘struct foo **’ but argument is of type ‘struct foo * (*)[3]’
What’s the correct way to accomplish what I want (i.e. pass a pointer to the array of pointers to the function, so that the function can change pointers in the array)?
Pass it as some_function(foos)
struct foo ** is a pointer to a (single) pointer to a struct foo, not a pointer to an array of pointers, hence the compiler warning.
An easy way to silence the compiler warning is to call the function as follows:
some_function(&foos[0]);
This will pass a pointer to the first member, i.e. a struct foo **, rather than to the whole array; the address is the same in both cases.
If I understand what you are trying to do (fill your array of pointers with a call to a function), then your understanding of how to accomplish that is a bit unclear. You declare foos, which itself is an array. (an array of what? pointers).
You can treat it just like you would treat an array of char (from the standpoint that you can simply pass the array itself as a parameter to a function and operate on the array within a function) You can do that and have the changes visible in the caller because despite the array address itself being a copy in the function, the values it holds (the individual pointer address) remains the same.
For example:
#include <stdio.h>
char *labels[] = { "my", "dog", "has", "fleas" };
void fillfoos (char **f, int n)
{
int i;
for (i = 0; i < n; i++)
f[i] = labels[i];
}
int main (void) {
char *foos[] = { NULL, NULL, NULL };
int i, n = sizeof foos / sizeof *foos;
fillfoos (foos, n);
for (i = 0; i < n; i++)
printf ("foos[%d] : %s\n", i, foos[i]);
return 0;
}
Above foos is simply treated as an array passed to the function fillfoos which then loops over each pointer within foos filling it with the address to the corresponding string-literal contained in labels. The contents of foos is then available back in main, e.g.
Example Use/Output
$ ./bin/fillptp
foos[0] : my
foos[1] : dog
foos[2] : has
If I misunderstood your question, please let me know and I'm happy to help further.
You need a pointer to an array as clearly mentioned in the warning.
Below is a minimal code sample that explains the same.
#include<stdio.h>
typedef struct foo{
}FOO;
static void some_function(FOO* (*foos)[]) {
// foos above is a pointer to an array of pointers.
// Refer the link to start with a simple example.
// Access it like foos[0][0] which is the same as (*foos)[0]
/* some more code here */
}
int main(int argc, char **argv) {
FOO* foos[]={0,0,0}; // Here you have an array of pointers
some_function(&foos);
}

using calloc on an array of struct, in a function

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....

Passing pointers to arrays to functions from within another function

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)
{
*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;
}
But here's the problem: I don't call GraphIntialize directly from main(). Instead, I first 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 retrieves 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 (triple pointer)' but argument is of type 'struct graph 4ASTERISKS (quadruple pointer)'. This is so confusing. If there is a way I can work this out without needing all these pointers that might be the best answer, but I am trying to create and abstract data type and so I don't want to be creating a graph_array array in my main function.
I suppose, you don't have to use '&' here:
if ((GraphInitialize(&graph_array, *verts)) == -1)
You want to initialize a double pointer (graph**), but to do that you pass a pointer to it into your functions, so both of them get a triple pointer (graph ***) as an input.
The chain of calls looks something like this (this is more of a pseudocode):
void GraphInitialize(struct graph *** graph_array);
void getdata(..., struct graph *** graph_array )
{
...
GraphInitialize(graph_array); //graph_array here is the same triple pointer, that 'getdata' recieved as an input, so there is no need to use '&' operator.
...
}
void main()
{
graph ** graph_array = ...; // this is a double pointer, obviously
getdata( ..., &graph_array); //getdata gets a triple pointer as an input, so we get the graph_array address by '&' operator;
}
So the correct form would be
if ((GraphInitialize(graph_array, *verts)) == -1)
use
if ((GraphInitialize(graph_array, *verts)) == -1)
{
printf("GraphCreate failed");
return 0;
}
this works i hope..

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