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

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.

Related

Functions with multi-dimensional arrays as formal parameters

I am trying to figure out why it is that the signature of functions with multi-dimensional arrays as formal parameters have the first dimension as unsized, while the others are not. Actually the answer to the second part of the aforementioned statement is clear: without passing the dimension information, the compiler will not know how the multi-dimensional array will be organized in memory. What bothers me is the inconsistency.
Why not require all dimensions to be explicitly specified (including the first dimension)?
I have come up with a theory and I want to see if that is correct or not.
The most common usage of an array is a 1D array. Given that the array name is a pointer to the first element of the array, Dennis Ritchie wanted to have the exact same signature for 1D arrays, whether the array syntax was used, or the pointer syntax was used.
With the pointer syntax it was impossible to know how many elements to process in the function, without specifying the size information as well. So Dennis Ritchie forced the same signature for array syntax also by having the array be unsized (and even have the compiler completely ignore the size information for the first dimension, if it is provided). In other words you will have one formal parameter be a pointer, or an unsized array, and the second formal parameter the array size.
When you pass an array it decays to a pointer to the first element. This is a for convenience. Without it, passing a string literal to a function like this:
void foo(const char* str) {}
would be cumbersome:
foo("Hello world"); // the const char[12] decays into ...
foo(&"Hello world"[0]); // what would have to be written like this without the decay
If you want all dimensions to be specified, just take the address of the array and you'll get a pointer to a single element - with all dimensions specified.
Example. This function takes a pointer to a int[2][10]:
void f2d2(int (*)[2][10]) {}
int a2d[2][10];
f2d2(&a2d); // and you call it like this
In C++ you can prevent the array decaying into a pointer to the first element by taking the array by reference.
void f2d2(int (&)[2][10]) {}
int a2d[2][10];
f2d2(a2d); // no decay
There are two quotes from the C Standard that makes using arrays more clear.
The first one is (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.
And the second one is (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.
What does this mean relative to function declaration?
If you declared a function like for example
void f( int a[100] );
then the compiler will adjust the function parameter the following way
void f( int *a );
So for example these function declarations are equivalent
void f( int a[100] );
void f( int a[10] );
void f( int a[1] );
void f( int a[] );
void f( int *a );
and declare the same one function. You nay even include all these declarations in your program though the compiler can issue a message that there are redundant declarations.
Within the function the variable a has the the pointer type int *.
On the other hand, you may call the function passing arrays of different sizes. Arrays designators will be implicitly converted by the compiler to pointers to their first element. You even may pass a scalar object through a pointer to it.
So these calls of the function are all correct
int a[100];
f( a );
int a[10];
f( a );
int a[1];
f( a );
int a;
f( &a );
As a result the function has no information what array was used as an argument. So you need to declare the second function parameter that will specify the size of the passed array (if the function does not rely on a sentinel value present in the array)
For example
void f( int a[], size_t n );
If you have a multidimensional array like this
T a[N1][N2][N3]...[Nn];
then a pointer to its first element will have the type
T ( *a )[N2][N3]...[Nn];
So a function declared like
void f( T a[N1][N2][N3]...[Nn] );
is equivalent to
void f( T ( *a )[N2][N3]...[Nn] );
If there N2, N3,..Nn are integer constant expressions then the array is not a variable length array. Otherwise it is a variable length array and the function may be declared like (when the declaration os not a part of the function definition)
void f( T ( *a )[*][*]...[*] );
For such a function there is also a problem of determining of sizes of sub-arrays. So you need to declare parameters that will specify the sizes.
For example
void f( size_t n1, size_t n2, size_t n3, ..., size_t nn, T a[][n2][n3]...[nn] );
As for C++ then variable length arrays is not a standard C++ feature. Also you can declare a function parameter as having a referenced type.
For example
void f( int ( &a )[10] );
within the function in this case a is not a pointer. It denotes an array and the expression sizeof( a ) will yield the size of the whole array instead of the size of pointer.
It's not possible to pass arrays by value in C. You can only pass a pointer, and then in the function, index from the pointer to access the contents of the array.
Accordingly; in a function declaration, if you specify parameter with array type, that parameter is adjusted to pointer type before any further analysis. For example:
You write: void f( int x[][5] );
The compiler sees: void f( int (*x)[5] );
The language would be equally functional if the first form were outlawed, i.e. you had to define the parameter with the pointer version. It is just syntactic sugar -- which as it turns out, causes a lot of confusion to newbies but that's another story.
So to answer your question -- all array dimensions after adjustment must be specified , because otherwise the compiler doesn't know how to index the memory when accessing the array via the pointer argument passed. And it should now be clear that the faux first "dimension" is irrelevant because it is syntactic sugar that is immediately discarded.

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.

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.

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.

Correct C syntax for "address of" character pointer array?

I need to pass into a function the address of a character pointer array, what would be the correct function declaration/prototype syntax for that?
I tried:
void myFunc(char &*a[]);
But get an expected ; , or ) before & error.
I also tried:
void myFunc(char **a);
Since the pointer I would be passing in is indeed a pointer to a pointer, but I get yet another error. This time the error has to do with: expected char **, but got char *[]
Or something like that. I have since attempted other solutions so I not remember exactly the error.
Any suggestions?
Assuming you have an array declared as
char *a[N];
and you want to pass a pointer to the array, as in
foo( &a );
then the prototype for foo needs to be
void foo( char *(*aptr)[N] );
Note that in this case, the size of the array must be declared; a pointer to an N-element array is a different type from a pointer to an M-element array.
Normally, you don't want to do this; instead, you would normally just pass the array expression like so:
foo ( a );
and the corresponding prototype would be:
void foo ( char **aptr );
Except when it is the operand of thesizeof or unary & operators, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer toT", and the value of the expression will be the address of the first element of the array.
Updated Answer For Modified Problem Statement
Given what you have said in comments, there is no need to pass a pointer to an array. You can simply pass a pointer to the first element of the array. Such a pointer suffices because the remaining elements of the array are obviously located after the first element.
To write a function that sets pointers in an array of pointers to char, do this:
void MyFunction(int NumberToSet, char *Pointers[])
{
for (int i = 0; i < NumberToSet; ++i)
{
Pointers[i] = SomeString;
}
}
In the above, SomeString must have type “pointer to char”. This could be a string, such as "Hello", or an array of char (which is automatically converted to a pointer to char), or some identifier x that has been declared as char *x (and has been initialized or assigned), for example.
To use this function, call it like this:
char *MyArrayOfPointers[SomeNumber];
MyFunction(NumberOfPointersIWantToSet, MyArrayOfPointers);
Original Answer
In most cases, to pass an array of pointers to char to a function, it suffices to pass the address of the first element. In this case, you would use either of these (they are equivalent):
void myFunc(char **a)
void myFunc(char *a[])
If you truly want to pass the address of the array, you would use:
void myFunc(char *(*a)[])
In this case, the type of a is incomplete, since the dimension is missing. Depending on what you intend to do with a, you may need to provide the dimension in the declaration.
When calling myFunc and passing it some array declared as char *array[N];, you would pass it, in the former case, as myFunc(array) and, in the latter case, as myFunc(&array).
try this as a function definition void myFunc(char *a[]) or void myFunc(char **a) then use it this way :
char *arr[20];
myFunc(arr);
Ok you are almost on the right path. void myFunc(char *a[]);
Example
void fun(char *a[]){
printf("%s",*a); //for accessing the next element do a+1
}
int main(void) {
char *x[3];
x[0]="abcd";
fun(x); // here you are passing the address first array element
return 0;
DEMO
Declaration
void myFunc(char &*a[]);
is not a valid C syntax.
To pass the address of character pointer arrays, use this instead
void myFunc(char *(*a)[]);
*(*a)[] in the above function declares a as pointer to array of pointers to chars. Must note that a has an incompatible type. A suffix is needed in [] to make it complete.
First of all, C is in general a "pass by reference" language. Some data items such as integers, floats, and single characters can be passed by value. But, arrays of those same data types are ALWAYS passed by reference.
Thus, when you say "I need to pass into a function the address of a character pointer array" then simply declare an array of character pointers in your function prototype as:
void myFunc(char *a[]);
Thus, char * declares a char pointer and a[] defines an array of them. To check this declaration refer to: http://www.cdecl.org/ which parses this expression as a "declare a as array of pointer to char".
The technical point is that the * binds with char rather than with a[].
So, C will pass a pointer to the data structure that you have declared. A discussion on this topic could delve into double pointers but for this question such a discussion is probably off topic.

Resources