Why I need different declarations for **ptr and *ptr[]? [duplicate] - c

This question already has answers here:
C difference between *[] and **
(8 answers)
Closed 2 years ago.
I thought that char *ptrs[]; and char **strs; should be the same thing.
At least I can use both as string arrays.
When I try to compile char **strs, it's fine, but *ptrs[] want to be initialized or at least it want a fixed size for some reason.
char ptrs[];
char **strs;
I get the error:
gcc: "error: array size missing in ‘ptrs’"

I thought that char *ptrs[]; and char **strs; should be he same thing.
You thought wrong. First is an array of unspecified size of pointers. The second is a pointer to a pointer. An array is not a pointer and a pointer is not an array.
You may have been confused by function parameters. Function parameters cannot be arrays and if you write an array parameter, it is adjusted to be a pointer to an element of such array. As such, in a parameter declaration the first is adjusted to be the same as the second. This adjustment only occurs in parameter declarations and nowhere else.

Arrays and pointers are two different derived types.
If you declare an object with automatic storage duration then it shall have a complete type. For arrays it means that the number of elements in the array shall be known (either explicitly specified or calculated according to provided initializes).
So the compiler issue the error
gcc: "error: array size missing in ‘ptrs’"
for this declaration of an array with the automatic storage duration
char *ptrs[];
(I think you mean the type specifier char * instead of char when you are comparing these two declarations char *ptrs[]; and char **strs;) because it is unable to determine how much memory to allocate for the array.
On the other hand, pointers are always complete object types. From the C Standard (6.2.5 Types, p.#20)
A pointer type is a complete object type.
As a result the compiler always knows the size of a pointer.
If this declaration
char *ptrs[];
has file scope then such an incomplete declaration is allowed and is named as a tentative definition. the compiler will interpret it like this declaration
char *ptrs[1];
is present at the end of a translation unit.
Arrays used in expressions are implicitly (with rare exceptions) are converted to pointers to their first elements. So this array designator ptrs used in expressions as for example a function argument is converted to rvalue of the type char **.
From the C Standard (6.3.2.1 Lvalues, arrays, and function designators)
3 Except when it is the operand of the sizeof 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.

I thought that char *ptrs[]; and char **strs; should be the same thing. At least I can use both as string arrays.
Your assumption is not true and neither of those two can be used as "arrays of strings". An array of strings (if this term is anyhow in any manner appropriate) is after my opinion, an array of array of chars, where a string can be stored in each array of chars which is therefore again an element of the array of arrays. A pointer cannot store a string. It can only point to one.
char **strs; - strs is a pointer to pointer to char.
char *ptrs[]; - ptrs is an array of pointer to char with unspecified amount of elements. That is not permissible anywhere else than as function parameter.
As function parameter char *ptrs[] is equal to char **ptrs and denotes a pointer to pointer to char, but again it stores no strings itself.
If you use char *ptrs[] as usual declaration, the compiler needs to know the amounts of pointer elements in the array.
This is why you get the error message
"error: array size missing in ‘ptrs’"
with GCC.

The two declarations are equivalent only as function parameter declarations:
void foo( char *ptrs[], char **strs ) { ... }
because any parameter of type T a[N] or T a[] is "adjusted" to T *a. This is due to the fact that array expressions "decay" to pointer expressions under most circumstances - when you pass an array expression as a function argument, what the function actually receives is a pointer expression.
As regular variable declarations, however, the two are not equivalent. An array definition must either specify a size or have an initializer:
char *ptrs[] = {"foo", "bar", "bletch", "blurga", ... };
In this case, the array size will be taken from the number of elements in the initializer.

Related

About typecasting of an array to pointers

I want to understand where exactly in code an array gets converted to a pointer. For example:
void foo( int* pData, int len){}
int main(void){
char data[] = "Hello world";
foo( (int*)data, sizeof(data));
return 0;
}
I know that an array decays to a pointer to the first element if it is assigned to a pointer. However in the example above, I typecast the array data to int* first before passing it in function and assigning it to a pointer. Does the conversion/decay to pointer occurs at the typecasting point ? If so, isn't it true to say that the typecasting operation has the same effect as using the assignment operator with respect to array conversion/decay? Also would sizeof(data) be equal to the address length or array length?
Thank you for help!
The conversion of arrays to pointers in C is spelled out in section 6.3.2.1p3 of the C standard:
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.
This means that the array is immediately converted to a pointer anywhere it is used except for the three cases listed above.
So applying the above to (int*)data, data is the operand of the typecast operator. Since this operator is not one of the ones listed above, data in this expression is converted from char [12] to char *, then the cast converts the char * to an int *.
Also, as mentioned above, the array is not converted when passed to sizeof. This means sizeof(data) evaluates to the size of char [12] which is 12.
Outwith the declaration, you can consider data to be equivalent to a pointer to the start of the array, but with the following exceptions:
sizeof(data) will give you the size of the array in bytes.
_Alignof(data) will be the same as _Alignof(*data) (and both give you the alignment of the type of the array elements)
&data has the same value as just data, but have a type of char (*)[sizeof(data] so arithmetic will use the full size of the array. Eg &data+1 will give you the address after the whole array rather than the address of the second element. See How come an array's address is equal to its value in C?
you can't change it's value (ie in this sense it is equivalent to a char *const).
When you call your function, you are taking the value of data (ie the address of the start of the array) and typecasting to an int *. The resulting int * behaves like any other int * and the exceptions don't apply, this is the 'decay'.

Is * equivalent to [] in C?

Would coding:
char *argv[]
would be the same as:
char **argv
Or would:
char *string1;
int *int_array;
be exactly the same than:
char string1[];
int int_array[];
Absolutely not. One is a pointer, the other one is an array.
However, when it comes to parameters, char *param is equivalent to char param[] or char param[16], for example.
From the C11 draft N1570, §6.7.6.3/7:
A declaration of a parameter as "array of type" shall be adjusted to
"qualified pointer to type" [...]
There are several contexts where [] could be used. It gets a different treatment depending on the context:
Function parameter declaration - here char *argv[] is equivalent to char **argv
Array declarations with initialization - here int x[] = {1, 2, 3} is a way to have the compiler count array elements for you
Declaration at translation unit scope - here int x[] is treated as a tentative declaration, with storage provided at some other place
Last member of a struct - this is a flexible member declaration, which lets you "embed" arrays of variable size into a struct
Compound literals - (const int []) {1, 2, 3} lets you construct arrays "on the fly" without declaring a special variable.
As you can see, * and [] get the same treatment only in the first context; in all other contexts the treatment is different.
No! Well, it depends on the context but explicitly they aren’t the same type.
* is a pointer whereas [] is an array.
A pointer is a variable whose value is the address of another variable, i.e., direct address of the memory location. Like any variable or constant, you must declare a pointer before using it to store any variable address. The general form of a pointer variable declaration is * before the varname
Where an array is a list of objects.
I recommend looking up what pointers are as they are a strong part of C and C++.
Here is a good place to learn about them https://www.tutorialspoint.com/cprogramming/c_pointers.htm (also the place I got the quote from above)
No.
The concept of arrays is related to that of pointers. In fact, arrays work very much like pointers to their first elements, and, actually, an array can always be implicitly converted to the pointer of the proper type.
Pointers and arrays support the same set of operations, with the same meaning for both. The main difference being that pointers can be assigned new addresses, while arrays cannot.
Read more at cplusplus.

How does an unsized array declaration act as a pointer?

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.

C programming: arrays and pointers [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is array name a pointer in C?
If I define:
int tab[4];
tab is a pointer, because if I display tab:
printf("%d", tab);
the code above will display the address to the first element in memory.
That's why i was wondering why we don't define an array like the following:
int *tab[4];
as tab is a pointer.
Thank you for any help!
tab is a pointer
No, tab is an array. An int[4] to be specific. But when you pass it as an argument to a function (and in many other contexts) the array is converted to a pointer to its first element. You can see the difference between arrays and pointers for example when you call sizeof array vs. sizeof pointer, when you try to assign to an array (that won't compile), and more.
int *tab[4];
declares an array of four pointers to int. I don't see how that is related to the confusion between arrays and pointers.
tab is not a pointer it's an array of 4 integers when passed to a function it decays into a pointer to the first element:
int tab[4];
And this is another array but it holds 4 integer pointers:
int *tab[4];
Finally, for the sake of completeness, this is a pointer to an array of 4 integers, if you dereference this you get an array of 4 integers:
int (*tab)[4];
You are not completely wrong, meaning that your statement is wrong but you are not that far from the truth.
Arrays and pointers under C share the same arithmetic but the main difference is that arrays are containers and pointers are just like any other atomic variable and their purpose is to store a memory address and provide informations about the type of the pointed value.
I suggest to read something about pointer arithmetic
Pointer Arithmetic
http://www.learncpp.com/cpp-tutorial/68-pointers-arrays-and-pointer-arithmetic/
Considering the Steve Jessop comment I would like to add a snippet that can introduce you to the simple and effective world of the pointer arithmetic:
#include <stdio.h>
int main()
{
int arr[10] = {10,11,12,13,14,15,16,17,18,19};
int pos = 3;
printf("Arithmetic part 1 %d\n",arr[pos]);
printf("Arithmetic part 2 %d\n",pos[arr]);
return(0);
}
arrays can behave like pointers, even look like pointers in your case, you can apply the same exact kind of arithmetic by they are not pointers.
int *tab[4];
this deffinition means that the tab array contains pointers of int and not int
From C standard
Coding Guidelines
The implicit conversion of array objects to a
pointer to their first element is a great inconvenience in trying to
formulate stronger type checking for arrays in C. Inexperienced, in
the C language, developers sometimes equate arrays and a pointers much
more closely than permitted by this requirement (which applies to uses
in expressions, not declarations). For instance, in:
file_1.c
extern int *a;
file_2.c
extern int a[10];
the two declarations of a are sometimes incorrectly assumed by
developers to be compatible. It is difficult to see what guideline
recommendation would overcome incorrect developer assumptions (or poor
training). If the guideline recommendation specifying a single point
of declaration is followed, this problem will not 419.1 identifier
declared in one file occur. Unlike the function designator usage,
developers are familiar with the fact that objects having an array
function designator converted to typetype are implicitly converted to
a pointer to their first element. Whether applying a unary & operator
to an operand having an array type provides readers with a helpful
visual cue or causes them to wonder about the intent of the author
(“what is that redundant operator doing there?”) is not known.
Example
static double a[5];
void f(double b[5])
{
double (*p)[5] = &a;
double **q = &b; /* This looks suspicious, */
p = &b; /* and so does this. */
q = &a;
}
If the array object has register storage class, the behavior is undefined
Under most circumstances, an expression of array type will be converted ("decay") to an expression of pointer type, and the value of the expression will be the address of the first element in the array. The exceptions to this rule are when the array expression is an operand of the sizeof, _Alignof, or unary & operators, or is a string literal being used to initialize another array in a declaration.
int tab[4];
defines tab as a 4-element array if int. In the statement
printf("%d", tab); // which *should* be printf("%p", (void*) tab);
the expression tab is converted from type "4-element array of int" to "pointer to int".

How to get an array size in a function without counting or passing the length as a parameter?

I tried use sizeof(array)/sizeof(int), but it seems this doesn't work if I passed the array into another function. So is there any other way to know the size of the array?
By the way: I know a char[] ends with '\0'; which character ends the int[]?
In C, there is a difference between an array and a pointer to data. When "passing an array to a function", you in effect pass a pointer to the function. Unfortunately, you can't know if a pointer points to one element, or to a sequence of elements, and it's not possible to tell how many elements the sequence consist of.
Also, unlike a string, an int array is not ended with a specific value.
When an expression of array type appears in most contexts1, it is implicitly converted ("decays") to an expression of pointer type and its value is the address of the first element in the array. Similarly, in the context of a function parameter declaration (but only in that context), declarations like T a[] and T a[N] are interpreted as T *a; despite the array notation, the parameter a is a pointer, not an array type.
So given code like
int arr[10];
...
foo(arr);
The type of the expression arr in the call to foo is "10-element array of int". In this context, the type of the expression is converted to "pointer to int", so foo will receive a pointer value.
Similarly, given a function definition
void foo(int a[10])
{
...
}
the type of the parameter a is not "10-element array of int", but rather "pointer to int".
In short, you cannot pass an array argument to a function and have it received as an array type; all you get is a pointer value. As a result, you must either pass the array size as a separate parameter, or terminate the array with a sentinel value (such as using a 0 in an array of char to terminate strings).
1 The exceptions to this rule are when the array expression appears as an operand to either the sizeof or unary & operators, or if it is a string literal being used to initialize another array in a declaration.
A char array only ends with a \0 if it's being explicitly treated as a null-terminated string. There's no terminating character on arrays in general and no way to know the size of an array without passing the size around with it.

Resources