If I have a multidimensional array like this:
int arr2d[2][3] = {{0,1,2}, {10,11,12}};
I can pass it to a function like this:
void foobar(int arg[][3])
This is not a call by value, this is call by reference, so just an pointer to the start address, but the compiler still knows it is a 2D array and I'm able to access it like one in the function.
Now how does the same work in a struct?
typedef struct {
int arr2d[][3];
} Foobar_t
First this gives me: error: flexible array member in otherwise empty struct. I can fix this by doing so:
typedef struct {
int dummy;
int arr2d[][3];
} Foobar_t
It will compile without errors or warnings. But when I try to use it like Foobar_t foobar = {1337, arr2d} I get some warnings:
missing braces around initializer
initialization makes integer from pointer without a cast
And when accessing it: subscripted value is neither array nor pointer nor vector.
One dimensional arrays can easily be treated as pointers. But for multi dimensional arrays the compiler needs to know the size of the different dimensions to calculate the offsets correctly. Is there a way without cast (int (*)[3]) and why does the syntax differ from the function parameter?
So this is the work-around I want to avoid:
#include <stdio.h>
static int testArr[2][3] = {{0,1,2},{10,11,12}};
typedef struct {
int *arr2d;
} Foobar_t;
int main( int argc, char** argv ) {
Foobar_t foobar = {(int*)testArr};
int (*arr2d)[3] = (int (*)[3]) foobar.arr2d;
printf("testStruct_0_0: %d\n", arr2d[0][0]);
printf("testStruct_1_0: %d\n", arr2d[1][0]);
return 1337;
}
Edit:
Some comments suggest that reference is not the correct word. Of course in the C language this is implemented by a pointer.
So the TLDR of this questions is: How does the syntax for a pointer type to a multi dimensional array look like.
The answer can already be seen in my work-around code. So that is all, move along, nothing to see here ;) Nevertheless thanks for the replies.
There is no "call by reference" in the C language. Function arguments are always passed by value. Arrays do appear special, since an array decays to a pointer to its first element in most expressions (including function calls). This means that when an array is used as an argument in a function call, a pointer to the first element is passed to the function instead of an array; but it is the value of this pointer which is passed.
In function declarators, array types are adjusted to pointers to appropriate types. This is specific to the semantics of function declarators. Thus, a function declaration like:
void foobar(int arg[][3]);
is adjusted to take a pointer to an array of three ints as an argument:
void foobar(int (*arg)[3]);
In general, a type expression such as int arg[][3] is an incomplete type, since it is impossible to know the size of the array arg[][] without more information.
Structures in C do not allow member types to be specified with incomplete types (with one exception), since there is no way to know the size of the struct without this information. Further, struct specifiers do not make the same adjustment to array types that function declarators do, since structs may actually include array members.
The exception to the incomplete type rule in structs is with flexible array members. The last member of a struct with at least two named members may have an incomplete array type.
The simple solution to the problem in the question is to change the specifier for the struct to use a pointer to an array. Note that here the member .arr2d is not an array, but a pointer to an array of three ints:
typedef struct {
int (*arr2d)[3];
} Foobar_t;
You could try using either int **arr2d, which would allow you to access it via arr2d[x][y] or simply convert it to a one-dimensional array like int *arr2d = malloc(2*3*sizeof(int));.
That way, you'd need to access values like this: arr2d[x*m + y];, where x and y are the same of the previous example, while m is the size of a row.
I'd also suggest you to store both row number and column number of your 2-dimensional array into the struct.
Turns out just looking at the work-around showed me the solution:
typedef struct {
int (*arr2d)[3];
} Foobar_t;
This is the correct type for a pointer to a 2D array. type (*name)[n] also works for function parameters.
So why is the other syntax type name[][n] still valid for function parameters? Probably the conflicts with the flexible array feature of structs keep it from working there.
Related
So I have two functions that does the same thing but on different type variables.
The first function fills up an array of integers when given an int arr[] argument.
The second function fills up a linked list with integers also when given a struct as an argument.
The struct for the linked list argument looks something like this
typedef struct {node_t *head;int size;}
list_t;
Now I have implemented a table of function pointers for the two functions as such:
typedef struct{
char *name; //name of the function
void (*fill)(int arr[]); //fill up the array
} alg_t;
alg_t algs[] = {
{"func1",fill_up_arr},
{"func2",fill_up_linkedList}
};
Notice that inside the struct that holds my pointers, the fill function pointer
takes int arr[] as an argument.
I only want one pointer of a function in that struct, is there a way I can use
typecasting so that other functions such as fill_up_linkedList require argument to be of type list_t instead of int arr[]?
//This is what I want my main to look like.
//I want func.fill to be called only once thus
//dynamically perform the operations for all functions inside the table of
//functions array
int arr = malloc(sizeof(int)algs.size);
for(int i = 0; i<algs.size;i++){
alg_t func = algs[i];
func.fill(arr);
}
It seems that the problem with this code would be when the loop would try and perform the fill_up_LinkedList function as it needs a different argument.
How can I use a typecast in this situation?
Thanks
The good news
C11 §6.3.2.3 Pointers ¶8 (under the general topic §6.3 Conversions) says:
A pointer to a function of one type may be converted to a pointer to a function of another
type and back again; the result shall compare equal to the original pointer. If a converted
pointer is used to call a function whose type is not compatible with the referenced type,
the behavior is undefined.
That means you can store any and all function pointers in a common function pointer type, for example typedef void (*GenericFunctionPointer)(void). What is crucial, though, is that you convert from that generic function pointer type back to the correct function pointer type before you invoke the function via the pointer (and that you provide the correct argument list, and that you handle the return type appropriately, though ignoring the return value, if any, is always an option).
The bad news
For two different function pointer types, each with one instance of the the function, the infrastructure needed to support this is probably more elaborate than the savings, if any. On the other hand, if you have two or more different function pointer types, and most if not all of the types have many representative functions ('many' meaning 'more than one', as in the computer engineer's counting: "zero, one, many"), then the infrastructure can pay off. One of the issues is marshalling the function arguments — how are the arguments made accessible so that the function can be called via the pointer with the correct arguments.
So, doing things this way is complex and verbose.
The stated requirement
In a comment, the OP Moi says:
I only want to put one function in the struct. My goal is to find a way to allow fillArray to allow the passing of different args.
I have major reservations about the use of an uncounted array as the argument list as shown in the question — void (*fill)(int arr[]) is shown. In my view, it should be void (*fill)(size_t n, int arr[n]), using the variable length array notation. You can omit the n in the subscript if you wish — void (*fill)(size_t n, int arr[]) — or even use void (*fill)(int arr[], size_t n), which is the more classic order for the arguments.
Putting this concern aside, if you want a single function to accept different arguments, one way to achieve that is with void * as the type, but you have to be aware of the problems — one of which is type safety. You'll also need to borrow ideas from the standard C functions qsort() and bsearch(). The argument list will include the pointer to the start of the array as a void *, the size of each element of the array, and the number of elements in the array. You may also need analogues to the comparator functions.
Internal to the single called function, though, you will probably end up with two code paths, so although you call a single function via the pointer, you end up doing the equivalent of implementing two functions. You could use an interface similar to qsort()'s so that the two functions have the same interface and different bodies, and you use two pointers in the alg_t array.
Summary
You probably can't achieve the stated requirement cleanly.
You will probably need two logically separate functions to handle the two separate interfaces, even if you smush all the code inside a single function.
Use a union:
typedef struct {
char *name; //name of the function
union {
void(*fillArray)(int arr[]); //fill up the array
void(*fillList)(YourListType list); //fill up the list
};
} alg_t;
alg_t a;
a.fillArray(...);
a.fillList(...);
Or:
typedef struct {
char *name; //name of the function
union {
void(*fillArray)(int arr[]); //fill up the array
void(*fillList)(YourListType list); //fill up the list
} functions;
} alg_t;
alg_t a;
a.functions.fillArray(...);
a.functions.fillList(...);
Since you only have 2 types, you can just use an enum and a macro
enum e_type {e_foo,e_bar};
#define do_foobar(e,...) (e)?bar(__VA_ARGS__):foo(__VA_ARGS__)
replace foo and bar with your function names
If you only have the problem with the function parameters and not with the return value, there's a trick related to incomplete type definitions. The trick consists in leaving empty the list of parameters in the pointer type declaration, as in:
typedef void (*callback_ptr)();
which is different from:
typedef void (*callback_ptr)(void);
in the first case, the compiler will not check the parameters passed to the function, as the pointer is a pointer to an incompletely defined function type, while the second explicitly says the function doesn't require parameters and will give you an error if you try to call it with them.
Any historical or logical reasons behind it?
Explanation:
when you pass an array to a function in C you actually only pass a pointer to an array.
However, when you pass a struct, you can either pass a copy of the struct or the pointer.
//this:
int function(int array[10])
// is equivalent to this:
int function(int *array)
//and they both pass a pointer
//but this:
int function(struct tag name)
//passes a struct by value, where as this:
int function(struct tag *name)
//passes a pointer to it.
Why the difference?
In the original K&R, you could not pass structs by value. That was a syntax error. Because many compiler vendors offered it as an extension, pass-by-value eventually made its way into the standard.
Why the restriction, and why the evolution? The machines that C was developed on were tiny. Segment sizes of 64 kilobytes were common. Memory was precious, so why copy something when you could just pass the address? Duplicating a 64-byte structure on the stack was an error, probably not even what the user intended.
By the mid-1990s, that wasn't true anymore. 32-bit addressing and RAM of 4 MB or more were common. The restriction was a hinderance and led to some complexity, because without const a structure passed by reference could be modified, perhaps unwittingly.
Why not do the same with arrays? No demand. Arrays and pointers are closely related in C, as you know. The C standard library depends heavily on passing by reference, consider memset and strcpy. Whereas passing a struct by value meant just dropping the & on the call, passing an array by value would have entailed adding new syntax. A compiler vendor that offered, say, by value as C syntax would have been laughed out of the conference.
int function(int array[10]) and int function(int *array) are the same because of 6.7.5.3 Function declarators (including prototypes) (http://www.open-std.org/JTC1/SC22/wg14/www/docs/n1124.pdf page 118)
7 A declaration of a parameter as ‘‘array of type’’ shall be adjusted
to ‘‘qualified pointer to type’’, where the type qualifiers (if
any) are those specified within the [ and ] of the array type
derivation. If the keyword static also appears within the [ and
] of the array type derivation, then for each call to the
function, the value of the corresponding actual argument shall
provide access to the first element of an array with at least as many
elements as specified by the size expression
.
Of course, you can pass an array by value; all you need to do is wrap it in a struct. But that only works if the array has a definite (and non-variable) size. You can include an indefinitely sized array in a struct, but the resulting type is incomplete and can only be used as the target of a pointer.
That's probably as close as we're going to get to an explanation. The vast majority of arrays passed as arguments are not of fixed size, and passing them by value would not be possible, even if it were intended, which is also unlikely.
There is a similar (but different) decay with functions; a function cannot be passed as an argument, only function pointers. Since it would be tedious to explicitly write & every time you wanted to refer to a function, the language takes care of it for you.
On the whole, questions of the form "Why is this language like this snd not like that?" can only be answered "because that's how it is".
Types are different in both function declarations -
struct tag /* and */ struct tag *
One is structure variable whereas another is pointer to structure.
You can do similar with structures -
int function(struct tag name[]) /*--> int function(struct tag *name) */
These above would be equivalent.
structs are used for declaring own kind of data types with primitive data types like int,float,long(or structs of structs) etc. they are supposed to hold a few of them e.g. struct of students will contain id,name,rollno,subjects,etc. so mostly any struct element will contain 10-20 fields at most (in logical cases), so when you pass a struct to a function, it has to copy 40-100 bytes approx. to make copy of that struct variables. where as arrays can be of huge size and are used to store same kind of information. they can be of size 10^7 in case of integers so if we implement a language to copy whole array for function calls it may have to copy (10^7)*4 bytes which is a huge amount and will impact the performance badly.and typical size of arrays is 10^4 to 10^6 which is still a lot. but if you create struct of array of int(or any other array) you can pass it to a function as a copy of that array. e.g.
#include<stdio.h>
typedef struct {
int arr[10];
}arrayStruct;
void change(arrayStruct a){
a.arr[2]=5;
}
int main(){
arrayStruct a;
for(int i=0;i<10;i++){
a.arr[i]=i;
}
printf("Before:\n");
for(int i=0;i<10;i++){
printf("%d ",a.arr[i]);
}
change(a);
printf("\nAfter:\n");
for(int i=0;i<10;i++){
printf("%d ",a.arr[i]);
}
return 0;
}
this is not done in most of the cases but few times when you need to pass arrays but don't want to alter the contents of them but also require some kind of changes to their copies and want to return that copy you can use this kind of structs of arrays and return them from functions of return type of structs e.g.
arrayStruct returnChange(arrayStruct a){
a.arr[2]=332;
return a;
}
I am trying to implement a checkers game.
I have to use a given header file which contains some #defines and statements.
one of them is:
typedef char** board_t;
I understand that this is a pointer to a pointer to a char i.e that:
**board_t = board_t[0][0]
In the checkers.c file I am implementing some methods with a board 2d array input.
In some of them I'm interested in changing the board so I want to insert it using a pointer.
considering the header typedef, is it ok to define the board this way:
char board_t [BOARD_SIZE][BOARD_SIZE];
and then to use it in a method :
void init_board(board_t){...}
in this writing am I really passing on a pointer to the previously defined 2d array?
if so, how do I approach board[i][j] using this pointer in the method?
If you did it like this:
typedef char** board_t;
Then, declare and allocate memory like:
int i;
board_t arr = malloc(BOARD_SIZE * sizeof(char *));
for(i = 0; i < BOARD_SIZE; i++)
arr[i] = malloc(BOARD_SIZE * sizeof(char));
For a function defined like:
void init_board(board_t arr){...}
Pass on to it like:
init_board(arr);
Some points to note:
If you have used typedef for aliasing a pointer type, you cannot have an array-like declaration (where you specify two index sizes) of the newly created type name. You must declare and allocate memory just like you do with pointers.
In function init_board you can treat the parameter arr as a 2-D array, and refer to its values like arr[row][col].
Note that pointers and arrays are not equivalent in all aspects. There are subtle differences which should be elaborated in a good C book or tutorial (otherwise it is not good).
board_t = board_t[0][0]; that is wrong. You assign an integer (char) to a pointer. Hint: always enable most warnings. For gcc, at least -Wall ist strongly recommended, adding -Wextra is good advice.
If you just want a 2D array, it is still char *board_t. You do not have to add one * per dimension (that would be for an array of pointer to array).
Notes:
A _t suffix should be reserved for implementation defined types. Your name might collide with such a type for a later version of the language. For custom types, CamelCase is commonly used (no specific prefix/suffix).
Using the same name for a type name and a variable is not allowed (it will generate an error).
For conversion of pointers, you should read the standard, expecially 6.3.2.1p3 and 6.7.6.3p7 are of interest here.
Briefly, if you have a formal function argument of array type, that will implicitly converted to a pointer to that same type. If you apply the array operator on a pointer, that will have the same semantics as for an array.
I am currently studying for my C-Midterm and I encountered this declaration:
int **foo[][]()
When looking for the solution as to what this declaration means my tutors actually gave two different answers:
1) foo is an array of arrays of functions with return type pointer to pointer to an int
2) foo is an array of arrays of pointers to pointers to a function with return type int
I know the "start with the name of the variable, continue to the right until you reach the end or ')' then go back to your last starting point and continue to the left until you reach the start or '('" rule so I think 1) is the correct answer here but I am not entirely sure.
Thanks,
Ozelotl
It is nothing specific. Meaning that on the surface it looks like a C declaration, but it is not well-formed. It is illegal and as such it means nothing.
Firstly, it appears like a two-dimensional array declaration, but in C language an array declaration is required to specify all sizes except possibly for the very first one. Your declaration omits the second size as well, which makes it illegal.
Secondly, even if we ignore the missing sizes, it looks like a declaration for an array of functions. It is illegal to declare arrays of functions in C.
For example, this would make a legal C declaration
int (**foo[][42])()
but not what you have originally.
The syntax of this declaration is that foo[][] declares a 2-D array (or it would, if the second bound had a dimension specified - as it stands that's illegal); and then the rest of it is:
int **bar(); // with bar = foo[][]
which is a function taking unspecified arguments and returning int **. However, since bar is an array type here this attempts to declare an array of functions, which is illegal. (Not to be confused with an array of function pointers, which would be OK).
The grammar rules are that the ** bind to the int (not to the bar) unless you use parentheses to force them to bind to the bar; so they are part of the type int ** and they are not saying that bar is a pointer.
I'm trying to initialize an empty 2 dimensional array of pointers that point to data structures I have created known as "node"s. For some reason, I'm getting this scoping issue when I'm trying to modify the list, which is a crucial part of the program. A function called "repository_init" is supposed to truly initialize the array. However, I am using the array in other functions, which is why I'm getting the error. It doesn't recognize that the array exists. I'm new to C but after doing some research I found something on "extern", and the site told me that I could use extern to declare an empty variable. So, I declared my list as a global variable by putting it before main and writing it like so:
extern node *main_list[][];
#define MAX_LEVEL 10000
Here is the repository_init() function:
void repository_init(int p){
int new;
new = (max_range/2) + 5;
max_height = 1;
while(new > 1){
new = (new * probability)/100;
max_height++;
}
main_list[max_height][MAX_LEVEL];
/*fill the array with empty values*/
}
The error output to the screen is "error: array type has incomplete element type
extern node *main_list[][];"
I need this list to be global. I don't see what the problem is with just having the array initialized in a separate function. I've done this tons of times in other languages.
The problem is twofold: The first is that the declaration of main_list is incomplete, just as the compiler is telling you. You need sizes for the array(s) when declaring an array. The second problem is the expression main_list[max_height][MAX_LEVEL]; which uses this incomplete array. It doesn't set the sizes, it tries to access the array(s).
In your case it's probably best solved by having pointers to pointers. Something like
extern node ***main_list;
...
main_list = malloc(sizeof(node **) * max_height);
for (size_t i = 0; i < max_height; ++i)
main_list[i] = malloc(sizeof(node *) * MAX_LEVEL);
What your declaring is not a array.
It's a pointer to 2D array. You can't declare a pointer to a 2D array without defining its size(at least rows while using pointers concept).
If you want 2D array declare it as following:-
extern node main_list[][];
If you want a pointer to 2D try using a triple pointer
extern node ***main_list;
Note:- array size is calculated at compile time so compiler has to know its size.
From C99 standard, section 6.2.5, article 20 -
An array type describes a contiguously allocated nonempty set of
objects with a particular member object type, called the element type.
Array types are characterized by their element type and by the number
of elements in the array. An array type is said to be derived from its
element type, and if its element type is T, the array type is
sometimes called array of T.
It furthers says in the footnote #36 -
Since object types do not include incomplete types, an array of
incomplete type cannot be constructed.
This means that conceptually there's no difference between 1-D array and 2-D array etc. They are all the same type - the array type. The following statement
extern node *main_list[][];
is intended to declare an array main_list of type node *[] of unknown size.
The type node *[] is itself an array type but its size is not know and hence it's an incomplete type. You can declare an array of unknown size which will be resolved with its external definition, but you cannot declare an array of incomplete type as clearly stated in the standard which is quoted above. The type of the array element must be a complete type. This explains the error statement.
Here is what I suggest -
// header file myheader.h
#define MAX_LEVEL 10000
extern node *main_list[][MAX_LEVEL];
I would suggest not to use variable names like new which is a keyword in many languages and may cause confusion. Also, you can initialize your array with NULL.
// myfile.c
void repository_init(int p) {
int newval;
newval = (max_range / 2) + 5;
max_height = 1;
while(newval > 1) {
newval = (newval * probability) / 100;
max_height++;
}
// definition of the array. specifies the array size
// and allocates memory on the stack. size is resolved with
// the extern declaration of the array main_list.
// initialize all elements of the array to NULL.
node *main_list[max_height][MAX_LEVEL] = {0};
// process the array main_list
}