An array type decays to a pointer type when it is passed to a function
That means in
int func(int x[*p])
*p should not be evaluated as the declaration is equivalent to int func(int *x)
Does the same hold for pointer to arrays?
Here is the code
int *p=0;
void func(int (*ptr)[*p]) //A
{
// code
}
int main()
{
int arr[5][5];
func(arr);
}
Is evaluation of *p at //A guaranteed by the Standard?
I tried with and without optimization on g++4.6. With optimizations enabled I don't get segfault. On clang the code is not giving any segfault even without any optimizations.
From the C99 Standard Section 6.7.5.2 Array declarators, paragraph 1:
In addition to optional type qualifiers and the keyword static, the [
and ] may delimit an expression or *. If they delimit an expression
(which specifies the size of an array), the expression shall have an
integer type. If the expression is a constant expression, it shall
have a value greater than zero. The element type shall not be an
incomplete or function type. The optional type qualifiers and the
keyword static shall appear only in a declaration of a function
parameter with an array type, and then only in the outermost array
type derivation.
The expression *p evaluates to 0 and does not satisfy the requirements of the above paragraph, so the behavior is undefined.
Related
I have the following code:
#include <stdio.h>
int main(void) {
int array[0];
printf("%d", array);
return 0;
}
As we know, an array always points to its first item, but we don't have items in this example, but this code produces some memory address. What does it point to?
An array of size 0 is considered a constraint violation. So having such an array and attempting to use it triggers undefined behavior.
Section 6.7.6.2p1 of the C standard regarding constraints on Array Declarators states:
In addition to optional type qualifiers and the keyword static, the [ and ] may delimit an expression or *. If they delimit an expression (which specifies the size of an array), the expression shall have an integer type. If the expression is a constant expression, it shall have a value greater than zero. The element type shall not be an incomplete or function type. The optional type qualifiers and the keyword static shall appear only in a declaration of a function parameter with an array type, and then only in the outermost array type derivation
GCC will allow a zero length array as an extension, but only if it is the last member of a struct. This is an alternate method of specifying a flexible array member which is allowed in the C standard if the array size is omitted.
Function prototype
void foo(int n, int a[][]);
gives error about incomplete type while
void foo(int n, int (*a)[]);
compiles. As per the decay rule int a[][] is equivalent to int (*a)[] in this case and therefore int (*a)[] should also give an error about an incomplete type but GCC seems to accept it. Is there anything I am missing?
This might be a GCC bug but I didn't find anything related to it.
No, they are not equivalent as function parameters. They are not equivalent in exactly the same way as parameter declarations in foo and bar
struct S;
void foo(struct S* s); // OK
void bar(struct S a[]); // ERROR: incomplete type is not allowed
are not equivalent.
C does not allow incomplete types as array elements (see C 1999 6.7.5.2/1: "[...] The element type shall not be an incomplete or function type. [...]") and this restriction applies to array parameter declarations the same way as it applies to any other array declarations. Even though parameters of array type will be later implicitly adjusted to pointer type, C simply provides no special treatment for array declarations in function parameter lists. In other words, array parameter declarations are checked for validity before the aforementioned adjustment.
Your int a[][] is the same thing: an attempt to declare an array with elements of type int [], which is an incomplete type. Meanwhile, int (*a)[] is perfectly legal - there's nothing unusual about pointers to incomplete types.
As a side note, C++ "fixed" this issue, allowing arrays of incomplete type in parameter declarations. However, the original C++ still prohibits int a[][] parameters, int (&a)[] parameters and even int (*a)[] parameters. This was supposedly fixed/allowed later in C++17 (http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#393)
An incomplete type is allowed in contexts where the size doesn't need to be known.
With this declaration:
int a[][]
It is invalid even as a function parameter because the size of one array dimension is needed to know how to perform pointer arithmetic on the second dimension.
This however is valid:
int (*a)[];
Because the size of the array doesn't need to be known in order to use a pointer to it.
Section 6.2.7 of the C standard gives an example of a declaration like this:
5 EXAMPLE Given the following two file scope declarations:
int f(int (*)(), double (*)[3]);
int f(int (*)(char *), double (*)[]);
The resulting composite type for the function is:
int f(int (*)(char *), double (*)[3]);
This example shows a declaration of type double (*)[3] that is compatible with a declaration of type double (*)[]
You can't however directly use this like a 2D array because of the missing size. Here are some examples to illustrate. If you attempt to do this:
void foo(int n, int (*a)[])
{
int i,j;
for (i=0;i<n;i++) {
for (j=0;j<n;j++) {
printf("a[%d][%d]=%d\n",i,j,a[i][j]);
}
}
}
The compiler (as expected) tells you this:
error: invalid use of array with unspecified bounds
printf("a[%d][%d]=%d\n",i,j,a[i][j]);
^
You can get around this by taking advantage of the fact that an array, even of indeterminate size, decays to a pointer in most contexts:
#include <stdio.h>
void foo(int n, int (*a)[])
{
int i,j;
for (i=0;i<n;i++) {
// dereference "a", type is int[], which decays to int *
// now manually add "n" ints times the row
int *b = *a + (n*i);
for (j=0;j<n;j++) {
printf("a[%d][%d]=%d\n",i,j,b[j]);
}
}
}
int main()
{
int a[2][2] = { {4,5},{6,7} };
foo(2,a);
return 0;
}
This compiles clean with the following output:
a[0][0]=4
a[0][1]=5
a[1][0]=6
a[1][1]=7
Even outside of a function, the int (*)[] syntax can be used:
#include <stdio.h>
int main()
{
int a[2][2] = { {4,5},{6,7} };
int i,j,n=2;
int (*aa)[];
// "a" decays from int[2][2] to int (*)[2], assigned to int (*)[]
aa = a;
for (i=0;i<n;i++) {
int *b = *aa + (n*i);
for (j=0;j<n;j++) {
printf("a[%d][%d]=%d\n",i,j,b[j]);
}
}
return 0;
}
EDIT
Having read through all the relevant parts of the standard, C11 6.7.6.2 and 6.7.6.3, I believe this is a compiler bug/non-conformance. it apparently boils down to the text that the committee sneaked into the middle of a paragraph concerning array delimiters. 6.7.6.2/1 emphasis mine:
In addition to optional type qualifiers and the keyword static, the [
and ] may delimit an expression or *. If they delimit an expression
(which specifies the size of an array), the expression shall have an
integer type. If the expression is a constant expression, it shall
have a value greater than zero. The element type shall not be an
incomplete or function type. The optional type qualifiers and the
keyword static shall appear only in a declaration of a function
parameter with an array type, and then only in the outermost array
type derivation.
Now this is of course very poorly written, basically it says
"peripheral feature of little interest, peripheral feature of little
interest, peripheral feature of little interest, OUT OF THE BLUE HERE COMES SOME ARRAY ELEMENT TYPE SPECIFICATION NOT RELATED TO THE REST OF THIS PARAGRAPH, peripheral feature of little interest, peripheral feature
of little interest,...."
So it is easy to misunderstand, fooled me.
Meaning that int a[][] is always incorrect no matter where it is declared, since an array cannot be an array of incomplete type.
However, my original answer below raises some valid concerns regarding whether array decay should be done before or after the compiler decides if the type is incomplete or not.
Given the specific case void foo(int n, int a[][]); only, this is a function declaration. It is not a definition.
C11 6.7.6.3/12
If the function declarator is not part of a definition of that
function, parameters may have incomplete type
So first of all, parameters are allowed to have incomplete type in the function declaration. The standard is clear. Which is why code like this compiles just fine:
struct s; // incomplete type
void foo(int n, struct s a); // just fine, incomplete type is allowed in the declaration
Furthermore:
C11 6.7.6.3/4
After adjustment, the parameters in a parameter type list in a function declarator that is part of a definition of that function shall not have incomplete type.
After adjustment is very important here.
Meaning that after adjusting int a[][] to int (*a)[], the parameter shall not have incomplete type. It does not, it is a pointer to incomplete type, which is always allowed and perfectly fine.
The compiler is not allowed to first evaluate int a[][] as an incomplete array of incomplete arrays, and then later adjust it (if it found that the type was not incomplete). This would directly violate 6.7.6.3/4.
How does char s[] act as a pointer while it looks like an array declaration?
#include<stdio.h>
void test(char s[]);
int main()
{
char s[10]="test";
char a[]=s;
test(s);
char p[]=s;
return 0;
}
void test(char s[])
{
printf(s);
}
In the context of a function parameter declaration (and only in that context), T a[N] and T a[] are the same as T *a; they declare a as a pointer to T, not an array of T.
Chapter and verse:
6.7.6.3 Function declarators (including prototypes)
...
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.
Anywhere else, T a[]; declares a as an array with an as-yet-unspecified size. At this point the declaration is incomplete, and a cannot be used anywhere until a size has been specified, either by specifying it explicitly:
T a[N];
or using an initializer:
T a[] = { /* comma-separated list of initial values */ }
Chapter and verse, again:
6.7.6.2 Array declarators
...
3 If, in the declaration ‘‘T D1’’, D1 has one of the forms: D[ type-qualifier-listopt assignment-expressionopt ]
D[ static type-qualifier-listopt assignment-expression ]
D[ type-qualifier-list static assignment-expression ]
D[ type-qualifier-listopt * ]
and the type specified for ident in the declaration ‘‘T D’’ is ‘‘derived-declarator-type-list
T’’, then the type specified for ident is ‘‘derived-declarator-type-list array of T’’.142)
(See 6.7.6.3 for the meaning of the optional type qualifiers and the keyword static.)
4 If the size is not present, the array type is an incomplete type. If the size is * instead of
being an expression, the array type is a variable length array type of unspecified size,
which can only be used in declarations or type names with function prototype scope;143)
such arrays are nonetheless complete types. If the size is an integer constant expression
and the element type has a known constant size, the array type is not a variable length
array type; otherwise, the array type is a variable length array type. (Variable length
arrays are a conditional feature that implementations need not support; see 6.10.8.3.)
142) When several ‘‘array of’’ specifications are adjacent, a multidimensional array is declared.
143) Thus, * can be used only in function declarations that are not definitions (see 6.7.6.3).
...
6.7.9 Initialization
...
22 If an array of unknown size is initialized, its size is determined by the largest indexed
element with an explicit initializer. The array type is completed at the end of its
initializer list.
So, why are arrays as function parameters treated differently than arrays as regular objects? This is why:
6.3.2.1 Lvalues, arrays, and function designators
...
3 Except when it is the operand of the sizeof operator, the _Alignof operator, or the
unary & operator, or is a string literal used to initialize an array, an expression that has
type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points
to the initial element of the array object and is not an lvalue. If the array object has
register storage class, the behavior is undefined.
Under most circumstances, an expression of type "N-element array of T" is converted ("decays") to an expression of type "pointer to T". If you pass an array expression as an argument to a function, like so:
int foo[10];
...
bar( foo );
what the function bar actually receives is a pointer to int, not a 10-element array of int, so the prototype for bar can be written
void bar( int *f );
"But why..." I hear you starting to ask. I'm getting to it, really.
C was derived from an earlier language called B (go figure), and in B the following things were true:
Pointer objects were declared using empty bracket notation - auto p[];. This syntax was kept for pointer function parameter declarations in C.
Array declarations set aside memory not only for the array elements, but also for an explicit pointer to the first element, which was bound to the array variable (i.e., auto v[10] would set aside 11 memory cells, 10 for the array contents, and the remaining one to store an offset to the first element).
The array subscript operation a[i] was defined as *(a + i) -- you'd offset i elements from the base address stored in the variable a and dereference the result (B was a "typeless" language, so all scalar objects were the same size).
For various reasons, Ritchie got rid of the explicit pointer to the first element of the array, but kept the definition of the subscript operation. So in C, when the compiler sees an array expression that isn't the operand of the sizeof or unary & operators, it replaces that expression with a pointer expression that evaluates to the address of the first element of the array; that way the *(a + i) operation still works the way it did in B, without actually setting aside any storage for that pointer value. However, it means arrays lose their "array-ness" in most circumstances, which will bite you in the ass if you aren't careful.
You can't assign to arrays, only copy to them or initialize them with a valid initializer (and another array is not a valid initializer).
And when you declare a function like
void test(char s[]);
it's actually the same as declaring it
void test(char *s);
What happens when you call a function taking an "array" is that the array decays to a pointer to the first element, and it's that pointer that is passed to the function.
So the call
test(s);
is the same as
test(&s[0]);
Regarding the function declaration, declaring a function taking an array of arrays is not the same as declaring a function taking a pointer to a pointer. See e.g. this old answer of mine as an explanation of why.
So if you want a function taking an array of arrays, like
void func2(char a[][X]);
it's not the same as
void func2(char **a);
Instead it's the same as
void func2(char (*a)[X]);
For more "dimensions" it doesn't change anything, e.g.
void func3(char a[][X][Y]);
is the same as
void func3(char (*a)[X][Y]);
char a[] is an array and not a pointer, the inittialization is invalid.
s is an array, too, but in the context of an expression it evaluates to a pointer to its first element.
char a[] is only valid if you have a following initializer list. It has to be an array of characters. As a special case for strings, C allows you to type an array of characters as "str", rather than {'s','t','r','\0'}. Either initializer is fine.
In the code char a[]=s;, s is an array type and not a valid initializer, so the code will not compile.
void test(char s[]) is another special case, because arrays passed as parameters always get replaced by the compiler with a pointer to the first element. Don't confuse this with array initialization, even though the syntax is similar.
According to the ISO/IEC 9899:1999 6.7.5 §2,
Each declarator declares one identifier, and asserts that when an operand of the same
form as the declarator appears in an expression, it designates a function or object with the
scope, storage duration, and type indicated by the declaration specifiers.
I have no idea why an expression suddenly shows up in the declarators semantics. Would you give me some examples that can help me understand the meaning?
Say you have the declaration
int foo[42];
The declarator part is foo[42]. Whenever something of same form (ie foo followed by [ followed by an expression followed by ]) appears within an expression (and the delaration is in scope), the type of that sub-expression will be of the declared type int.
To put it another way: As far as syntax goes, a declaration like
int *bar;
does not declare bar to be of type int *, but instead declares *foo to be of type int.
For a more involved example, take the declaration
float (*op[42])(float, float);
In an expression, an operand of same form could look like this
c = (*op[i])(a, b);
According to the quotation, the right-hand side would have type float.
This implies that
*op[i]
must have function type (we ignore the fact that function designators decay to corresponding pointer types and function invocation via postfix () actually works on pointers, not designators).
This in turn implies that
op[i]
must denote a function pointer and we finally arrive at
op
denoting an array of function pointers as that's what we can apply postfix [] on and get back the correct type.
Fun, isn't it ;)
Say you declare:
static int const i, j, k;
That is same as:
static int const i;
static int const j;
static int const k;
The declarator specifer, static int const is applied to all the identifiers.
You can also extend that logic to functions and function pointers.
static int i, (*fun1)(void), fun2(void);
which is same as:
static int i;
static int (*fun1)(void);
static int fun2(void);
As far as the appears in an expression part goes, there is some commentary at http://c0x.coding-guidelines.com/6.7.5.pdf. It says:
Semantics
Each declarator declares one identifier, and asserts that when an operand of the same form as the declarator appears in an expression, it designates a function or object with the scope, storage duration, and type indicated by the declaration specifiers.
Commentary
The form of an identifier in an expression is likely to be the same as that in the declarator. For instance, the declarator * x will have this form in an expression when the value pointed to by x is required and the declarator y[2] will have this form in an expression when an element of the array y is referred to. It is the declarator portion of a declaration that declares the identifier. There is a special kind of declarator, an abstract-declarator, which does not declare an identifier
I interpret the above to mean:
If you declare:
int *x;
and use *x in an expression, the type of *x is int.
If you declare
static int const *x;
and use *x in an expression, the type of *x is static int const.
Additional references
Static Variable Declaration (C)
Is [ ] also a declarator (when used in parameter declaration) in C?
C -- Accessing a non-const through const declaration
All that's saying is, given a declaration like
const int a = 5; // declaration
then the operand a in an expression like
x = a + b
within the same scope has type const int.
I'm going through K&R and it says array name is not a variable and it cannot be used in constructions like a=pa or a++.
Isn't s an array name here?
#include<stdio.h>
main(){
printf("%d", strlen("test"));
}
int strlen(char s[])
{
int n;
for (n = 0; *s!= '\0';s++) // why is s++ valid even though it is declared as an array
n++;
return n;
}
No, in this context it's a pointer to a char. Your function declaration is completely equivalent to:
int strlen(char *s)
As you'll see, it's actually impossible to pass an array to a function: a pointer to the first element is what is actually passed.
Thus, since s is actually a pointer and not an array, you're free to modify it as you please.
From the horse's mouth:
6.3.2.1 Lvalues, arrays, and function designators
...
3 Except when it is the operand of the sizeof operator, the _Alignof operator, or the
unary & operator, or is a string literal used to initialize an array, an expression that has
type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points
to the initial element of the array object and is not an lvalue. If the array object has
register storage class, the behavior is undefined.
The expression "test" is a string literal, which has type "5-element array of char". When you pass "test" as a parameter of strlen, by the rule above, what actually gets passed is a pointer whose value is the address of the first character in "test".
Which brings us to...
6.7.6.3 Function declarators (including prototypes)
...
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.
So in the prototype for strlen, char s[] is equivalent to char *s; s is declared as a pointer to char, not an array of char.
C's treatment of arrays is a bit baroque compared to other languages. That's due in part to the BCPL and B heritage. If you're curious as to why, you can read dmr's The Development of the C Language for some insights.
No, acctually s is a pointer name.
The declaration int strlen(char s[]) is same as int strlen(char *s)
When Char s[]={...} is declared address is attached to s, which never changes (like constant pointer) and anything try to change this property becomes an illegal operation such as s++.
But In function call int strlen(char s[]) , array is passed as pointer.