Reputation: 8869
I have generally thought that the following two prototypes were interchangeable:
int main(int argc, char ** argv);
int main(int argc, char * argv[]);
In general I had imagined that char ** argv
and char * argv[]
were interchangeable. However, I have also come accross some stuff on the internet that claim that you can declare structs like
struct S {
int size;
int ar[];
};
And then simply malloc
appropriately so that ar
can be as large as you want at runtime.
But this seems rather strange to me. If I had instead declared
struct S {
int size;
int * ar;
};
Can I still do the same thing? I would have imagined this depends on what you make ar
point to.
How exactly are int * ar
and int ar[]
different when used inside a struct? What about with char ** argv
and char * argv[]
in function prototypes? Do they have different semantics in C as opposed to in C++?
Upvotes: 1
Views: 88
Reputation: 263567
There's a special-case rule for function parameters that look like arrays. Any such parameter is "adjusted" to a pointer to the (possibly qualified) element type.
Because of this rule, these definitions:
int main(int argc, char **argv) { /* ... */ }
and
int main(int argc, char *argv[]) { /* ... */ }
are exactly equivalent. (There's a similar rule for parameters of function type, which are adjusted to function pointers.)
This rule applies only to parameter declarations.
One annoying consequence is that if you declare an array parameter with a size, it's silently ignored:
void func(int array[42]);
really means
void func(int *array);
(There is a usage of the static
keyword, added in C99, which I won't go into here.)
struct S {
int size;
int ar[];
};
This is a flexible array member, a feature added in C99. It declares that ar
is an array (not, I repeat not, a pointer) of unspecified size. To use it, you have to allocate enough space to hold however many elements you're going to need at run time. This was added to the language as a replacement for the "struct hack", described in question 2.6 of the comp.lang.c FAQ.
Section 6 of the same FAQ is an excellent resource for explaining the often confusing relationship between arrays and pointers.
Upvotes: 4
Reputation: 8215
You can use both the first and the second layout for the same purpose, but the layout in memory will not be the same. For the first example, you'd have (assuming a 32 bit architecture):
[size (4 bytes)][ar (size bytes)]
And for the second one:
[size (4 bytes)][pointer to ar (4 bytes)][ar (size bytes)]
So the second solution wastes memory.
Upvotes: 1
Reputation: 11126
There is a special rule for using arrays types in function arguments: They become pointer types. However, that rule only holds for function arguments.
This becomes very obvious when you replace the undefined size with, e.g. a size of two:
void f(int x[2]); // equivalent to void f(int* x);
struct A {
int q[2]; // obviously not equivalent to int* q;
};
The structure definition you used
struct S {
int size;
int ar[];
};
is intended to say that an arbitrary amount of int
s is supposed to follow the size
member - probably exactly size
elements. You cannot just leave out the last member, because that could lead to errors w.r.t. alignment and padding (assume for a second that ar
was an array of double
s, and you will see the problem).
This syntax is an evolution of an old trick and has been added in C99.
Upvotes: 4
Reputation: 2149
char **
and char *[]
are indeed the same. In fact, the below will work:
void foo(int a1[]) {
int a2[3];
a2 = 0; // ERROR: Can't assign to an array.
a1 = 0; // OKAY: You can assign to it, because it's actually a pointer!
...
The struct S that you show is using a flexible array member, though. It takes up no space. You can't assign to it. Basically, int []
means different things depending on whether it is a member or a parameter. In the struct where you have the pointer, though, you can basically make that point anywhere.
Upvotes: 0