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
}
Related
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.
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;
}
Hey I'm working on a problem and here is what I have to do:-
Write a function called initarray that takes an array of pointers to int and an int representing the size of the array, as arguments. The function should initialize the array with pointers to ints (use malloc) that have a value corresponding to the array indices at which a pointer to them are stored (the pointer stored at array index 2 should point to an integer with a value of 2).
So far I've written this, but It's giving me an error "[Error] variable-sized object may not be initialized"
Can you tell me what I'm doing wrong here?
#include<stdio.h>
void initArray(int **a, int sz){
int i;
for (i = 0; i < sz; i++) {
a[i] = calloc (1, sizeof **a);
*a[i] = i;
}
}
int main(){
const int Var = 10;
int *array[Var] = {NULL};
initArray(array,3);
}
For historical reasons, the value of a const variable is never considered a constant expression in C.
So if you use it as an array dimension, then the array is a variable-length array, and variable-length arrays are not allowed to have initializers.
One solution not mentioned yet is to use enum. Enumerators are in fact constant expressions, and they don't suffer from the same "bigger hammer" issue as preprocessor macros:
int main()
{
enum { Var = 10 };
int *array[Var] = {NULL};
initArray(array,3);
}
C has no symbolic constants with user-defined type. You encountered one of the differences to C++.
The const qualifier just is a guarantee you give to the compiler you will not change the variable(!) Var.
Arrays with initialiser and global arrays require a constant expressing which can be evaluated at compile-time. As Var is semantically still a variable, you cannot use it.
The C-way to emulate symbolic constants are macros:
#define ARRAY_SIZE 10
...
// in your function:
int *array[ARRAY_SIZE] = ...
Macros are handled by the preprocessor and are a textual replacement before the actual compiler sees the code.
Note I changed the name to a more self-explanatory one. The macro should also be at the file-level, typically near the beginning to allow easier modifications. Using the integer constant 10 directly in the code is a bad idea. Such magic numbers are often cause of errors when a modification is required.
The error would suggest that you can't use an initializer (the = {NULL} in your main function) on a variable-sized object. While it looks like it isn't variable (because of the const on Var, and because 10 is a constant) it sees it as variable because you're accessing it through a variable. If you use:
int *array[10] = {NULL}
I think your snippet will work fine.
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 have a struct that looks like this.
struct MyStruct1 {
int (*fn)();
int b;
}
And another struct that looks like this.
struct MyStruct2 {
int a;
struct MyStruct1 b[0];
}
I would like to declare a global variable of type MyStruct2, somewhat like this.
int func1() { return 1; }
int func2() { return 2; }
struct MyStruct2 a = { 1, { func1, 5 }, { func2, 6 } };
However, I get a "Initializer Element is not a compile-time constant".
I would like to know (a) if it is even possible to globally declare a variable sized struct (or at least define a chunk of space, of the correct size, to have the values inserted into later), and (b) if it is, what am I doing wrong?
It is not possible to declare a variable-size struct, neither locally nor globally. Every type has a fixed size determined at compile time.
The error message you report is surprising, however. If all the declarations you gave are at file scope, in the same file, in the order you gave them, then the initializer for variable a is a compile-time constant. However, it is not a proper initializer for a struct MyStruct2,
because it specifies more elements than that struct type has members,
because the initializer element for a.b is an initializer for a struct MyStruct1 instead of for an array of such, and
because even if you converted the last two initializer elements into one array initializer, it has more elements than there are elements in a.b (i.e. more than zero).
If you want a dynamically-sized array, whether as a variable in its own right or as a member of a struct, then you must declare a pointer to it, and allocate memory for the elements dynamically. In that case, the elements are not themselves part of the struct; only the pointer to them is. (That is different, by the way, from a fixed size array whose size is implicit in its initializer; these are possible only for independent types, though, not for types of struct or union members).
EDIT:
C99 flexible arrays are a possible alternative, as ShafikYaghmour commented. These are similar, but not identical, to a struct element that is a pointer to a dynamically-allocated array. In that case, however, you not only cannot statically declare the array elements, you also cannot statically declare instances of the struct itself, so this wouldn't at all get around your initializer issue. There are several other quirks and limitations. Personally I see few advantages to flexible arrays, but they do make it a bit easier to properly free struct instances.
You cannot create arrays of size 0 in C legitimately. In C99 or C11, you can use a 'flexible array member' like this:
struct MyStruct2 {
int a;
struct MyStruct1 b[];
};
but structures that have a flexible array member can only usefully be created with dynamic memory allocation (other forms of allocation give you an unusable flexible array of size 0).
The older 'struct hack' version of a structure with a variable size array uses an array of size 1 in the structure. You can create global versions of such a structure with an array of size 1.
But basically, you are trying to do what the language prohibits you from doing, and not very surprisingly, you are failing.
What you do about this depends on what you need. Global variables are inherently somewhat undesirable, so there's an element of "you should be trying to avoid doing this". That said, the rules apply to file scope (static) variables too, and those have many uses.
You can use an explicit pointer in place of the array, and have separate allocations of the body of the struct MyStruct2 and its array of struct MyStruct1 members. You can forgo the global variable and use dynamically allocated structures with a flexible array member.
struct MyStruct2 *ms2 = malloc(sizeof(*ms2) + N * sizeof(ms2->b[0]));
This creates an struct MyStruct2 (as shown at the top of this answer) with N members in the array. Without any further changes, you can use ms2->b[0] through ms2->b[N-1] (well, apart from error checking that the malloc() succeeded).