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

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.

Related

Size of an unknown array in C [duplicate]

The following C program:
int doStuff(int afm[]);
int main(){
int afm1[9] = {1,2,3,4,5,6,7,8,9}; //size=9
int afmLength = sizeof(afm1)/sizeof(int);
printf("main: Length Of Array=%d\n", afmLength); //9 OK
int k = doStuff(afm1);
system("PAUSE");
return 0;
}
int doStuff(int afm[]){
int afmLength = sizeof(afm)/sizeof(int);
printf("doStuff: Length Of Array=%d\n", afmLength); //1 WRONG
return 1;
}
produces the following output:
main: Length Of Array=9
doStuff: Length Of Array=1
Why is the array size calculated correctly in main, but is wrong inside the function?
Because in main you have an array and in the function you have a pointer to that array.
int doStuff(int afm[])
is equivalent to
int doStuff(int *afm)
Adding to David Heffernan's answer (which is correct), you should have another parameter which would be the array length passed onto your doStuff method.
From the 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.
Memorize that paragraph, since one of the biggest sources of heartburn in C programming is how C treats array expressions.
When you call doStuff(afm1);, the expression afm1 is implicitly converted from type "9-element array of int" to "pointer to int", and the expression's value is the same as &afm1[0]. So what doStuff receives is a pointer value, not an array.
In the context of a function parameter declaration, T a[] and T a[N] are both interpreted as T *a:
6.7.5.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.
Since doStuff receives a pointer value and not an array, the sizeof trick doesn't work. In general, you have to explicitly tell a function how large of an array you're passing to it; you can't determine that from the pointer value itself.
So, when you call doStuff from main, you'll need to do something like
doStuff(afm1, sizeof afm1/sizeof *afm1);
...
int doStuff(int *afm, size_t afmsize)
{
...
}

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

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/

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.

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.

Array name as variable

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.

Resources