-2

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);
}
Noman Hanafi
  • 377
  • 2
  • 8

4 Answers4

3

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]);
Community
  • 1
  • 1
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • if s[] can receive the address and can act as *s why it can't do the same thing in the main() – Noman Hanafi Jul 01 '16 at 11:13
  • @NomanHanafi Because the argument in the function, despite the syntax, is *not* an array, it's a *pointer*. In the `main` function you declare `a` as an actual array. This is also why you can't use the `sizeof` "trick" in a function to get the number of elements in the array, it's not an array in the function, it's a pointer. – Some programmer dude Jul 01 '16 at 11:16
  • @NomanHanafi there is a difference in `char s[]` as an argument in a function, in that case, it isn't actually an array - it is a pointer, even though the syntax is the same as declaring a normal array. – nos Jul 01 '16 at 11:17
  • How they are same? *s and char s[] its look like declaring an array. – Noman Hanafi Jul 01 '16 at 11:32
  • @NomanHanafi The important part here is "***looks*** like". It only looks like an array declaration, but when done in a function it is actually a pointer declaration. The compiler will translate the "declare array" syntax into "declare pointer". This is only done for function arguments, nowhere else. – Some programmer dude Jul 01 '16 at 11:34
3

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.

John Bode
  • 119,563
  • 19
  • 122
  • 198
1

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.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
0

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.

Lundin
  • 195,001
  • 40
  • 254
  • 396