String assignment has incomplete type - c

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.

Related

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/

How do I force a warning from using an array of wrong size when passed to function?

Let's say you have a function taking a string as an argument:
void foo(char *arg);
If we know for certain that the array (not to be confused with string length, thanks chux) will always have a certain size, let's say 8, then we can instead do:
void bar(char (*arg)[8]);
and then call it like this:
char str[8] = "Hello";
bar(&str);
We need to add the & for this to work properly, but the above code will emit a warning if you pass an array of wrong size or type, which is exactly what I want to achieve. But we will obviously need to modify the body a bit. So my question is simply if this wrapper technique would work:
void bar(char (*arg)[8]) {
char *tmp = (char*) arg;
foo(tmp);
}
What I'm trying to achieve here is that warnings should be emitted if called with an array of wrong size. Is the above solution safe? Is it safe to cast pointer to array of char to pointer to char?
I tried it, and it works, and emits no warnings with -Wall -Wextra -pedantic. And as soon as I change the size of str I get:
<source>: In function 'main':
<source>:18:9: warning: passing argument 1 of 'bar' from incompatible pointer type [-Wincompatible-pointer-types]
18 | bar(&str);
| ^~~~
| |
| char (*)[9]
<source>:9:17: note: expected 'char (*)[8]' but argument is of type 'char (*)[9]'
9 | void bar(char (*arg)[8]) {
| ~~~~~~~^~~~~~~
which is exactly what I want. But is it safe, or is it UB? I would like to do this, not only via a wrapper, but also by rewriting the original function, like
void foo(char (*argaux)[8]) {
char *arg = *argaux;
// Copy body of original foo
I know that I can achieve basically the same thing using structs, but I wanted to avoid that.
Runnable code: https://godbolt.org/z/GnaP5ceMr
char *tmp = (char*) arg; is wrong, these are not compatible pointer types. You can fix this easily though:
char *tmp = *arg;
*arg gives a char[8] which then decays into a pointer to its first element. This is safe and well-defined. And yes, pointers have much stronger "typing" in C than pass-by-value, so the compiler will recognize if an array of wrong size is passed.
Please note however that this leads to other problems: you can no longer have const correctness.
See Const correctness for array pointers?
This is not safe:
char *tmp = (char*) arg;
Because you're attempting to convert a char (*)[8] to a char *. While you might get away with it since a pointer to an array will (at least on x86-64) have the same numeric value as a pointer to the first member of an array, the standard doesn't guarantee that it will work. You would first need to dereference the parameter:
char *tmp = *arg;
In theory you should be able to do this:
void foo(char arg[static 8]);
This means that arg must be an array of at least that size.
The description of this syntax is in section 6.7.6.3p7 of the C standard:
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.
However, most implementations don't enforce this restriction and it doesn't prevent you from passing an array larger than expected.

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.

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.

how to pass "pointer to array" in function

This is my function
int mkvCopyWord(uint8_t *pBuffer, UINT8 temp):
main()
{
uint8_t a[10];
mkvCopyWord(&a,10);
}
its says warning : note: expected ‘uint8_t *’ but argument is of type ‘uint8_t (*)[10]’
how to remove this warning..?
Your syntax for passing a pointer-to-array is fine. But you are trying to pass it to something that doesn't want a pointer-to-array. It just wants a pointer to the beginning element. The simplest way to get that is to allow the array name to decay to a pointer, thus mkvCopyWord(a, 10). The function will assume that the pointer you give it is pointing to some sequence of uint8_ts somewhere - that is why you have to pass the array size as another parameter, because it does not know about the array, it only has some address in memory where the array is.
You don't need to take the address of the array as it implicitly converts to a pointer:
int mkvCopyWord(uint8_t *pBuffer, UINT8 temp):
main()
{
uint8_t a[10];
mkvCopyWord(a,10);
}
int main(void) //main() without return type isn't a good practice
{
uint8_t a[10];
mkvCopyWord(&a,10);
return 0;
}
Here you pass a parameter of type "pointer to array of 10 uint8_t". Your function, however, is declared to take pointer to int. In order to make this syntax work, you'd have to change the declaration of your function to take a pointer to an array of 10 uint8_t's
void mkvCopyWord(uint8_t (*parr)[10], int n)
This is all about syntax and type system. As for your actual problem, you need to pass a pointer to the first element of your array.
mkvCopyWord(&a[0],10);
However, an array is implicitly converted to that anyway, so you don't need to bother. You use the following:
mkvCopyWord(a,10);
HTH
Remove the ampersand in mkvCopyWord(&a,10):
int mkvCopyWord(uint8_t *pBuffer, UINT8 temp):
main()
{
uint8_t a[10];
mkvCopyWord(a,10); /* <<< removed the ampersand */
}
There is quite a lot of information on the equivalence of pointers and arrays in the C FAQ.

Resources