Why is char** argv same as char* argv[] - arrays

So I have read that behind the scenes when passing an array in a function the compiler turns
int myArray(int arr[])
into
int myArray(int *arr)
Also an array most of the times decays to a pointer, for example
arr[0]
is the same as
(arr + 0)
(Correct me if I am wrong)
But when it comes to char *argv it gets confusing, char *argv[] translates to an array of strings.
For example:
argv[2] = "Hello"
argv[3] = "World"
But how is **argv the same as *argv[] since **argv is a pointer to a pointer, how can **argv contain 10 different values
since it is a pointer to a pointer? I think I have misunderstood something.

Also an array most of the times decays to a pointer for example arr[0]
is the same as (arr + 0)
arr[0] is evaluated like *( arr + 0 ) that is the same as *arr.
Function parameters having array types are adjusted by the compiler to pointers to the array element types.
On the other hand, an array used as an argument expression is implicitly converted to pointer to its first element.
So for example these function declarations
void f( char * s[100] );
void f( char * s[10] );
void f( char * s[] );
are equivalent and declare the same one function as
void f( char **s );
To make it clear just introduce a typedef name. For example
typedef char *T;
then you have
void f( T s[] );
So the function parameter is adjusted by the compiler to
void f( T *s );
Now change the typedef alias to its original type and you will get
void f( char * *s );
Pay attention to that the pointer s knows nothing how many elements the array has used as a function argument.
Thus for example the function main is declared like
int main( int argc, char *argv[] );
That is it has one more parameter argc that allows to determine the number of elements in the array of strings passed to the function. Though if to tell about main then in general the parameter argc is redundant because the array of strings always contains the sentinel value NULL. That is argv[argc] is equal to NULL.
But in general you have to pass also the number of elements in the array used as a function argument.

But how is **argv the same as *argv[] since **argv is a pointer to a pointer
Because, quoting n1570 6.7.6.3p7 (emphasis mine):
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.
Each element of char *argv[] has type char *, which is a pointer to a char.
So according to 6.7.6.3p7, an array of char * will be adjusted to a pointer to a char *; i.e. char *argv[] (an array of pointer to chars) will be adjusted to char **argv (pointer to a pointer to a char).
how can **argv contain 10 different values since it is a pointer to a pointer?
Because it doesn't. Just because it is a pointer to a pointer, doesn't make it different from any other pointer (Well, except that their size, representation, and alignment requirements may differ).
The diagram below will probably help you understand what is actually going on:
argv (points to the beginning of the array of pointer to chars.)
------------------+---------+---------+
\ | | |
\ argv[0] |argv[1] |argv[2] | argv[3]
\ | | |
\ | | |
V char * V char * V char * V char *
+---------+---------+---------+---------+
| 0xf00 | 0xf0C | 0xf13 | NULL | (0xf0C and 0xf13 are the addresses of the first element of the strings passed as parameters to your program.)
+---------+---------+---------+---------+
| | |
| | |
| | |
V V V
"my_ "hello" "world" ("hello" and "world" are the parameters passed to your program.)
program"

"**" simply refers to a pointer to a pointer. Array itself works as an pointer. Therefore we can say that int **argv == *argv[] = argv[][0].

char *argv[] translates to an array of strings
If you consider this snippet
char const *arr[] = {"one", "two", "three"};
arr is declared as an array of pointers to char and inititialized with an initializer list.
The C Standard (see e.g. the C11 draft at 6.7.6.2 Array declarators1) says (emphasis mine):
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.
[...]
4 If the size is not present, the array type is an incomplete type.
Later, at 6.7.9 Initialization2:
8 An initializer specifies the initial value stored in an object.
[...]
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.
The question is about argv, though, the second function parameter of the main function.
As noted in JASLP's answer, we need to look at 6.7.6.3 Function declarators3
6 A parameter type list specifies the types of, and may declare identifiers for, the parameters of the function.
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.
1) http://port70.net/%7Ensz/c/c11/n1570.html#6.7.6.2
2) http://port70.net/%7Ensz/c/c11/n1570.html#6.7.9
3)http://port70.net/%7Ensz/c/c11/n1570.html#6.7.6.3

Array and Pointers in C Language hold a very strong relationship. Generally, pointers are the variables which contain the addresses of some other variables and with arrays a pointer stores the starting address of the array. Array name itself acts as a pointer to the first element of the array and also if a pointer variable stores the base address of an array then we can manipulate all the array elements using the pointer variable only.
Difference between pointer and array in C: https://www.geeksforgeeks.org/difference-pointer-array-c/

Related

char argv[ ][ ] as main function argument, why not?

I have learned from an online class that, both char **argv and char *argv[ ] can be used as an argument to main.
I also understand that they are effectively the same, they both declare argv as a "pointer (*) to a char pointer (char *)".
What makes char argv[ ][ ] invalid as an argument to main?
edit: Actually, is char *argv[ ] declaring an array instead of a pointer? So argv only decays to a pointer but is not one itself?
This declaration of a parameter
char argv[ ][ ]
is invalid because the compiler needs to know the element type of the array that in this case is incomplete type char[].
This declaration
char *argv[ ]
declares an incomplete array type with the element type char *.
If such a declaration is used as a function parameter declaration then it is implicitly adjusted by the compiler to pointer to the element type that is to the declaration
char **argv
Getting into the weeds a bit:
6.7.6.2 Array declarators
...
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.
...
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.)
...
6.7.6.3 Function declarators (including prototypes)
...
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.
...
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.
C 2011 Online Draft
So, to unpack all of this:
char argv[];
declares argv as an array of unknown size of char. This type is incomplete, so we cannot create an array of it with something like
char argv[][N];
and by extension, we can't create an array of unknown size of that element type:
char argv[][];
"But," I hear you say, "a parameter of type char [] is adjusted to type char *, so why doesn't char [][] equate to char **?"
Because that's not how array decay works. Let's start with a generic array declaration
T a[N];
The type of a is "N-element array of T", and in most contexts (including as a function parameter), its type is converted ("decays") or "adjusted" to "pointer to T", which gives us T *a;.
Now let's replace the type T with an array type R [M]. Substituting that in for T gives us
R a[N][M];
The type of a is adjusted from "N-array of M-element array of R" to "pointer to M-element array of R", which gives us:
R (*a)[M];
not
R **a;
So char argv[][] will never decay to char **, it will decay to char (*)[] (which is an incomplete type and not allowed as a function parameter). But we don't get that far, because char argv[][] is invalid all on its own.
If you use char argv[ ][ ], it is thought every word to have the same amount of letters. Every argv[i], points to another char *, however, this all new char * do not need all to be the same size.
Moreover, if you run your code with char argv[ ][ ], one error your compilor might show is: "declaration of 'argv' as multidimensional array must have bounds for all dimensions except the first". In fact, the int argc argument contains the amount of arguments passed, that is how many char * will be allocated. However, there is not any information for the size of the char * every char * previusly allocated point to.

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

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.

Why are arrays of undefined size only allowed in main()?

The main method argument uses an undefined array argv[]
int main(int argc, char *argv[])
{
.... Do stuff....
}
Why is it undefined arrays are only allowed in the main() method?
In fact this declaration
int main(int argc, char *argv[])
is equivalent to
int main(int argc, char **argv)
It is the host environment that supplies an array of strings and passes the pointer to the first string in the array as an argument for the parameter argv.
From the C Standard (5.1.2.2.1 Program startup)
— If the value of argc is greater than zero, the array members argv[0]
through argv[argc-1] inclusive shall contain pointers to strings,
which are given implementation-defined values by the host environment
prior to program startup. The intent is to supply to the program
information determined prior to program startup from elsewhere in the
hosted environment. If the host environment is not capable of
supplying strings with letters in both uppercase and lowercase, the
implementation shall ensure that the strings are received in lowercase
As for the comment
The argv array has no size, how is this possible?
then a function parameter declared as an array of type T is adjusted to pointer to T.
So for example these function declarations
void f( int a[100] );
void f( int a[10] );
void f( int a[] );
declare the same one function and the all declarations are adjusted to the declaration
void f( int *a );
The same way when an argument of an array type is passed to a function then the array is implicitly converted to pointer to its first element.
So for example the function above can be called like
int a[100];
f( a );
or
int a[10];
f( a );
or
int *a;
f( a );
Nothing is special about main here. An array declaration such as char *argv[] or int example[] has incomplete type, which is valid in certain contexts but not others. However, in the declaration of arguments to a function, the final type cannot be an array type; the array notation acts as a stand-in for a pointer to the first member of an array of that type. The actual type of argv in main is char **.
Why is it undefined arrays are only allowed in the main() method?
By "undefined arrays" you appear to mean arrays with unspecified size. There are several different cases to consider.
Function parameters declared with array syntax in fact do not have array type after all, whether a bound is specified or not. Rather, such a parameter has a pointer type. It is not necessary to specify a bound (for the first dimension), and if one is specified then it is insignificant. This matches up with the automatic conversion from array to pointer ("decay") that would happen in the caller if the corresponding argument were an array, but in the called function the parameter is a pointer, a whole pointer, and nothing but a pointer.
This applies to any function, not just main().
Any variable can be declared at file or block scope as an array without an explicit bound but with an initializer; in this case, the array bound is established implicitly as the minimum necessary to accommodate all elements initialized by the initializer.
Example:
int some_primes[] = { 2, 3, 5, 7, 11 }; // dimension 5
A file-scope (outside any function) variable or one that otherwise has linkage can be declared as an array without explicit bounds and without an intializer to establish the bound implicitly. The type of such a variable is "incomplete" until and unless another, compatible declaration of the same variable completes it by providing the bound, but many array operations can be performed on it at points in the source where it remains incomplete.
Only local variables of array type and without initializers must be declared with an explicit bound. ("Local variables" are those declared at block scope and with no linkage.) These are a fairly common case, though, so I suppose the need to declare a size for them was part of the inspiration for the question.
I'm a little confused about this question. argc is the number of commandline arguments passed into the executable. argv is an array of those arguments. Neither are undefined.
Both argc and argv are simply parameter names, and you can change them to whatever you'd like. They are simply called argc and argv by convention.

String assignment has incomplete type

I have a shared library that takes a new prompt (for the shell) as a parameter argv[1] I also have a global variable prompt type string as such
char *prompt[];
int setprompt(int argc, char *argv[]) {
prompt = argv[1];
return 0;
}
and I'm getting the following error
setprompt.c:14:2: error: ‘prompt’ has an incomplete type
prompt = argv[1];
^
In your code,
char *prompt[];
you did not supply a size for the array.
Quoting C11,chapter §6.7.6.2
If the size is not present, the array type is an incomplete type. [...]
Only time you're allowed to do that when you supply an initializer list, otherwise, you have to specify the size explicitly.
As per your requirement, making prompt a char * will suffice.
You can't declare a global or local with that syntax, since it denotes an array of unspecified size. Such a declaration is fine as a function parameter, since the array decays into a pointer.
In your case, you want a pointer, not an array of pointers:
char *prompt;
It should be declared as a pointer:
char *prompt;
This argument passed to the function char *argv[] is an array of pointers, and you are assigning to your global pointer just one element from that array.

Multi-dimensional array and pointer to pointers

When you create the multi-dimensional array char a[10][10], according to my book it says you must use a parameter similar to char a[][10] to pass the array to a function.
Why must you specify the length as such? Aren't you just passing a double pointer to being with, and doesn't that double pointer already point to allocated memory? So why couldn't the parameter be char **a? Are you reallocating any new memory by supplying the second 10.
Pointers are not arrays
A dereferenced char ** is an object of type char *.
A dereferenced char (*)[10] is an object of type char [10].
Arrays are not pointers
See the c-faq entry about this very subject.
Assume you have
char **pp;
char (*pa)[10];
and, for the sake of argument, both point to the same place: 0x420000.
pp == 0x420000; /* true */
(pp + 1) == 0x420000 + sizeof(char*); /* true */
pa == 0x420000; /* true */
(pa + 1) == 0x420000 + sizeof(char[10]); /* true */
(pp + 1) != (pa + 1) /* true (very very likely true) */
and this is why the argument cannot be of type char**. Also char** and char (*)[10] are not compatible types, so the types of arguments (the decayed array) must match the parameters (the type in the function prototype)
C language standard, draft n1256:
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.
Given a declaration of
char a[10][10];
the type of the array expression a is "10-element array of 10-element array of char". Per the rule above, that gets coverted to type "pointer to 10-element array of char", or char (*)[10].
Remember that in the context of a function parameter declaration, T a[N] and T a[] are identical to T *a; thus, T a[][10] is identical to T (*a)[10].

Resources