What is an array of constant pointers in C? - c

Isn't the address of an array and thus of all its elements as well constant anyway?
And if so, in a declaration like:
char *const argv[]
isn't the const qualifier redundant?

No, the const in char *const argv[] is not redundant.
First, const and "constant" are actually two different things in C, even though the const keyword is obviously derived from the word "constant". A constant expression is one that can be evaluated at compile time. const really means "read-only". For example:
const int r = rand();
is perfectly legal.
Yes, the address of an array -- like the address of any object -- is read-only. But that doesn't mean that the value of the array (which consists of the values of its elements) is read-only, any more than any other object is necessarily read-only.
Consider these three declarations:
char *arr1[10];
char *const arr2[10];
const char *arr3[10];
arr1 is a 10-element array of pointers to char. You can modify the char* elements and you can modify the objects that those elements point to.
arr2 is an array of const (read-only) pointers to char. That means that you can't modify the char* elements of the array (once they're initialized) -- but you can still modify the char objects or arrays that those elements point to.
And arr3 is an array of pointers to const char; you can modify the array elements, but you can't modify what they point to.
Now the fact that you used the name argv suggests that you're talking about the second parameter to main, which has some huge effects on this. The language specifies that main's second parameter is
char *argv[]
or, equivalently,
char **argv
There is no const. You can probably get away with adding one, but it's best to follow the form specified by the standard. (Update: I see from your comment that you're asking about the argv parameter of getopt(), which is defined as char * const argv[].)
And since it's a parameter defined as an array, another rule comes into play: a parameter defined as an array of some type is "adjusted" to a pointer to that type. (This rule applies only to parameters.) This isn't a run-time conversion. A function cannot have a parameter of array type.
The relationship between arrays and pointers in C can be confusing -- and there's a lot of misinformation out there. The most important thing to remember is that arrays are not pointers.
Section 6 of the comp.lang.c FAQ is an excellent explanation of the details.

Isn't the address of an array and thus of all its elements as well
constant anyway?
Yes, and it is true for any object in C. Recall that by object here, we mean a location in memory having a value and referenced by an identifier. The identifier is bound to a fixed memory location throughout its scope and you cannot change it. You can change the value of the object though.
int a = 4;
a = 6; // legal. you can change the value of the object
&a = 23456; // illegal. you cannot change the address of the object
Similarly, an array is also an object and each of its elements will have a fixed memory address. However, the value held by an element of the array has nothing to do with the address of the element.
Note that if the declaration appears in a function parameter list, then the following are equivalent
char *const argv[]
char *const *argv
which means that argv is a pointer to an object which is of type char *const, i.e., a constant pointer to a character. It's obvious that char *const *argv and char **argv are different. So let's take another example.
char *const argv[10];
The above statement defines argv to be an array of 10 constant pointers to a character. This means that you have to initialize the array and cannot later change the pointers to point to a different character. However, this has nothing to do with the address of the array elements.
char c = 'A';
char d = 'B';
char *const argv[2] = {&c, &d};
argv = &c; // illegal. you cannot the change the address of an object
argv[0] = &d; // illegal. you cannot change the value of the array element
*argv[0] = 'C'; // legal. you change the value pointed to by the element
Without the const qualifier, char *argv[2] means an array of 2 pointers to characters.
This is clearly different from the case when we have the const qualifier as explained above. Therefore, to answer your second question, no, the const qualifier is not redundant. That's because the const qualifier qualifies the type of the array elements.

No, it isn't. char *const argv[] is an array of constant pointers to char. So the const makes the pointers in the array constant (you cannot change them to point to other strings in memory).

Related

Different Types Of Initializing Character Arrays in C

I was learning about pointers and strings.
I understood that,
Pointers and Arrays/Strings have similar behaviours.
array[] , *array , &array[0]. They all are one and the same.
Why does the three statements in this code work, and char * help one does not ?
#include <stdio.h>
void display(char*help){
for(int i=0; help[i]!='\0'; i++){
printf("%c", help[i]);
}
}
int main(){
// char help[] = "Help_Me"; //Works
// char help[] = {'H','e','l','p','_','M','e','\0'}; //Works
// char *help = "Help_Me"; //Works
char *help = {'H','e','l','p','_','M','e','\0'}; //Error
display(help);
}
Error Messages :
warning: initialization of 'char *' from 'int' makes pointer from integer without a cast
warning: excess elements in scalar initializer
Pointers and Arrays/Strings have similar behaviours.
Actually, no, I wouldn't agree with that. It is an oversimplification that hides important details. The true situation is that arrays have almost no behaviors of their own, but in most contexts, an lvalue designating an array is automatically converted to pointer to the first array element. The resulting pointer behaves like a pointer, of course, which is what may present the appearance that pointers and arrays have similar behaviors.
Additionally, arrays are objects, whereas strings are certain configurations of data that char arrays can contain. Although people sometimes conflate strings with the arrays containing them or with pointers to their first elements, that is not formally correct.
array[] , *array , &array[0]. They all are one and the same.
No, not at all, though the differences depend on the context in which those appear:
In a declaration of array (other than in a function prototype),
type array[] declares array as an array of type whose size will be determined from its initializer;
type *array declares array as a pointer to type; and
&array[0] is not part of any valid declaration of array.
In a function prototype,
type array[] is "adjusted" automatically as if it were type *array, and it therefore declares array as a pointer to type;
type *array declares array as a pointer to type; and
&array[0] is not part of any valid declaration of array.
In an expression,
array[] is invalid;
*array is equivalent to array[0], which designates the first element of array; and
&array[0] is a pointer to array[0].
Now, you ask,
Why does the three statements in this code work, and char * help one does not ?
"Help_Me" is a string literal. It designates a statically-allocated array just large enough to contain the specified characters plus a string terminator. As an array-valued expression, in most contexts it is converted to a pointer to its first element, and such a pointer is of the correct type for use in ...
// char *help = "Help_Me"; //Works
But the appearance of a string literal as the initializer of a char array ...
// char help[] = "Help_Me"; //Works
... is one of the few contexts where an array value is not automatically converted to a pointer. In that context, the elements of the array designated by the string literal are used to initialize the the array being declared, very much like ...
// char help[] = {'H','e','l','p','_','M','e','\0'}; //Works
. There, {'H','e','l','p','_','M','e','\0'} is an array initializer specifying values for 8 array elements. Note well that taken as a whole, it is not itself a value, just a syntactic container for eight values of type int (in C) or char (in C++).
And that's why this ...
char *help = {'H','e','l','p','_','M','e','\0'}; //Error
... does not make sense. There, help is a scalar object, not an array or a structure, so it takes only one value. And that value is of type char *. The warnings delivered by your compiler are telling you that eight values have been presented instead of one, and they have, or at least the one used for the initialization has, type int instead of type char *.
array[] , *array , &array[0]. They all are one and the same.
No. Presuming array names some array, array[] cannot be used in an expression (except where it might appear in some type description, such as a cast).
array by itself in an expression is automatically converted to a pointer to its first element except when it is the operand of sizeof or the operand of unary &. (Also, a string literal, such as "abc", denotes an array, and this array has another exception to when it is converted: When it is used to initialize an array.)
In *array, array will be automatically converted to a pointer, and then * refers to the element it points to. Thus *array refers to an element in an array; it is not a pointer to the array or its elements.
In &array[0], array[0] refers to the first element of the array, and then & takes its address, so &array[0] is a pointer to the first element of the array. This makes it equivalent to array in expressions, with the exceptions noted above. For example, void *p = array; and void *p = &array[0]; will initialize p to the same thing, a pointer to the first element of the array, because of the automatic conversion. However, size_t s = sizeof array; and size_t s = sizeof &array[0]; may initialize s to different values—the first to the size of the entire array and the second to the size of a pointer.
// char help[] = "Help_Me"; //Works
help is an array of char, and character arrays can be initialized with a string literal. This is a special rule for initializations.
// char help[] = {'H','e','l','p','_','M','e','\0'}; //Works
help is an array, and the initializer is a list of values for the elements of the array.
// char *help = "Help_Me"; //Works
help is a pointer, and "Help_Me" is a string literal. Because it is not in one of the exceptions—operand of sizeof, operand of unary &, or used to initialize an array—it is automatically converted to a pointer to its first element. Then help is initialized with that pointer value.
char *help = {'H','e','l','p','_','M','e','\0'}; //Error
help is a pointer, but the initializer is a list of values. There is only one thing to be initialized, a pointer, but there are multiple values listed for it, so that is an error. Also, a pointer should be initialized with a pointer value (an address or a null pointer constant), but the items in that list are integers. (Character literals are integers; their values are the codes for the characters.)
{'H','e','l','p','_','M','e','\0'} is not a syntax that creates a string or an array. It is a syntax that can be used to provide a list of values when initializing an object. So the compiler does not recognize it as a string or array and does not use it to initialize the pointer help.
Pointer is not the array and it cant be initialized like an array. You need to create an object, then you can assign its reference to the pointer.
char *help = (char[]){'H','e','l','p','_','M','e','\0'};

char **s vs char *s[] initialization

I am trying hard to understand the difference between char *s[] and char s** initialization.
My char *s[] works fine, whereas my char s1** throws an error [Error] scalar object 's1' requires one element in initializer. I don't get the meaning of that error.
How can we initialize char s1** properly?
#include<stdio.h>
int main(void)
{
char *s[]={"APPLE","ORANGE"};
char **s1={"APPLE","ORANGE"};
return 0;
}
TLDR: char **s1=(char*[]){"apple","orange"};.
You can, of course, initialize pointers with the address of an element in an array. This is more common with simpler data types: Given int arr[] = {1,2};you can say int *p = &arr[0];; a notation which I hate and have only spelled out here in order to make clear what we are doing. Since arrays decay to pointers to their first elements anyway, you can simpler write int *p = arr;. Note how the pointer is of the type "pointer to element type".
Now your array s contains elements of type "pointer to char". You can do exactly the same as before. The element type is pointer to char, so the pointer type must be a pointer to that, a pointer to pointer to char, as you have correctly written:
char **s2= &s[0];, or simpler char **s2= s;.
Now that's a bit pointless because you have s already and don't really need a pointer any longer. What you want is an "array literal". C99 introduced just that with a notation which prefixes the element list with a kind of type cast. With a simple array of ints it would look like this: int *p = (int []){1, 2};. With your char pointers it looks like this:
char **s1=(char*[]){"apple","orange"};.
Caveat: While the string literals have static storage duration (i.e., pointers to them stay valid until the program ends), the array object created by the literal does not: Its lifetime ends with the enclosing block. That's probably OK if the enclosing block is the main function like here, but you cannot, for example, initialize a bunch of pointers in an "initialize" routine and use them later.
Caveat 2: It would be better to declare the arrays and pointers as pointing to const char, since the string literals typically are not writable on modern systems. Your code compiles only for historical reasons; forbidding char *s = "this is constant"; would break too much existing code. (C++ does forbid it, and such code cannot be compiled as C++. But in this special case C++ does not have the concept of compound literals in this way, and the program below is not valid C++.) I adjusted the types accordingly in the complete program below which demonstrates the use of a compound literal. You can even take its address, like that of any other array!
#include<stdio.h>
int main(void)
{
/// array of pointers to char.
const char *arrOfCharPtrs[2]={"APPLE","ORANGE"};
/// pointer to first element in array
const char **ptrToArrElem= &arrOfCharPtrs[0];
/// pointer to element in array literal
const char **ptrToArrLiteralElem=(const char*[]){"apple","orange"};
/// pointer to entire array.
/// Yes, you can take the address of the entire array!
const char *(*ptrToArr)[2] = &arrOfCharPtrs;
/// pointer to entire array literal. Note the parentheses around
/// (*ptrToArrLiteral)- Yes, you can take the address of an array literal!
const char *(*ptrToArrLiteral)[2] = &(const char *[]){"apples", "pears"};
printf("%s, %s\n", ptrToArrElem[0], ptrToArrElem[1]);
printf("%s, %s\n", ptrToArrLiteralElem[0], ptrToArrLiteralElem[1]);
printf("%s, %s\n", (*ptrToArr)[0], (*ptrToArr)[1]);
// In order to access elements in an array pointed to by ptrToArrLiteral,
// you have to dereference the pointer first, yielding the array object,
// which then can be indexed. Note the parentheses around (*ptrToArrLiteral)
// which force dereferencing *before* indexing, here and in the declaration.
printf("%s, %s\n", (*ptrToArrLiteral)[0], (*ptrToArrLiteral)[1]);
return 0;
}
Sample session:
$ gcc -Wall -pedantic -o array-literal array-literal.c && ./array-literal
APPLE, ORANGE
apple, orange
APPLE, ORANGE
apples, pears

Double pointer for argv

I am trying to create argv for a new process(trying to use execvp), and I checked the execvp manual page which says it needs char *const argv[].
I assume this is an pointer to array of char pointers. So is it possible to pass double pointer of char to this argument?
Basically, what I am trying to do is as following
(argvcounter is number of arguments. ex) cat a -> argvcount = 2)
int argvcount;
char **argv;
...
argv = malloc(sizeof(char*)*(argvcount+1));
for (int i = 0; i<argvcount; i++){
argv[i] = some char pointer;
}
argv[-1] = NULL;
I am not sure about the last line either. I am setting last element to NULL since the last element of array of arguments have to be NULL.
Is it possible to pass this argv to execvp?
Thank you.
According to the C Standard (5.1.2.2.1 Program startup, p.#2)
— argv[argc] shall be a null pointer
So you have to write
argv[argvcount] = NULL;
This statement
argv[-1] = NULL;
does not make sense and results in undefined behavior.
I assume this is an pointer to array of char pointers. So is it
possible to pass double pointer of char to this argument?
An array designater with rare exceptions is implicitly converted to pointer to its first element.
So if for example you have an array like this
char * argv[argvcount];
then passed to a function it is converted to pointer to its first element and has type char **.
On the other hand, these function declarations
void f( char *a[] );
and
void f( char **a );
are equivalent because the compiler adjusts the type of a parameter declared as an arrray to the type of pointer to an object of the array element type.
it needs char *const argv[]. I assume this is an pointer to array of char pointers.
No, it is an array of char* const pointers. It might help reading these declarations from right to left:
[] An array (of unknown size)...
argv ...named argv...
const ... of const...
* ...pointers...
char ...to char.
In plain English: An array (of unknown size) named argv, of read-only pointers to character.
So is it possible to pass double pointer of char to this argument?
Please note the subtle difference between arguments and parameters. Parameter referring to the variable in the function declaration, argument referring to the things you pass to the function on the caller side. It matters here.
Because a function taking a parameter of type char *const argv[], will have that parameter silently "adjusted" by the compiler into a pointer to the first element (sometimes called "array decay"). This is why we don't have to specify the array size - it will "decay" no matter the array size.
The first element is a char*const and a pointer to such an element is of type char*const*, so that's the type that the function will expect. A pointer to a const pointer to char - at the second level of indirection, the pointer itself cannot be modified.
As it happens, char** is a type that may be implicitly converted to char*const*, because the latter is a "qualified" version of the former - it is the same type but with "more const in the right places". Generally, any type* can be converted to type*const.
Had the parameter been const char* argv[], it wouldn't have been possible to use char**, because in that case the const belongs to the pointed-at type and not the pointer.
As already pointed out, note that argv[-1] = NULL; is nonsense, it should be argv[argvcount] = NULL;

Initialization different from assignment?

1.char str[] = "hello"; //legal
2.char str1[];
str1 = "hello"; // illegal
I understand that "hello" returns the address of the string literal from the string literal pool which cannot be directly assigned to an array variable. And in the first case the characters from the "hello" literal are copied one by one into the array with a '\0' added at the end.
Is this because the assignment operator "=" is overloaded here to support this?
I would also like to know other interesting cases wherein initialization is different from assignment.
You cannot think of it as overloading (which doesn't exist in C anyway), because the initialization of char arrays with string literals is a special case. The type of a string literal is const char[N], so if it were similar to overloading, you'd be able to initialize a char array with any expression whose type is const char[N]. But you cannot!
const char arr[3];
const char arr1[] = arr; //compiler error. Cannot initialize array with another array.
The language standard simply says that character arrays can be initialized with string literals. Since they say nothing about assignment, the general rules apply, in particular, that an array cannot be assigned to.
As for other cases when initialization is different from assignment: in C++, where there are references and classes, there would be zillions of examples. In C, with no full-fledged classes or references, the only other thing I can think of off the top of my head is const variables:
const int a = 4; //OK;
const int b; //Error;
b = 4; //Error;
Another example: array initialization with braces
int a[3] = {1,2,3}; //OK
int b[3];
b = {1,2,3}; //error
Same with structs
If you want to think of it as the operator being overloaded (even though C doesn't use the term), you can of course do that.
Do you also consider this to be overloading:
unsigned char x;
double y;
x = 2;
y = 1.243;
Those are assigning totally different types of data, after all, but using the "same operator", right?
It's just different, to be initializing or to be assigning.
Another big difference is that you used to be able to initialize structures, but there was no corresponding "struct literal" syntax for later assignments. This is no longer true as of C99, where we now have compound literals.
char str[] = "hello";
Is array initialization, using syntactic sugar defined in C because string initialization is so common. The compiler allocates some fixed memory in your program an initializes it. The name of the array (str) evaluates to the address of this memory, and it cannot be changed because there is no variable which holds that address.
Grijesh Chauhan explains more details of this.
Other cases depend on what you mean. Extending the current case, you can easily see that other initialized arrays have the same properties, for example
int a[] = { 1, 2, 3, 4 };
Array has non modifiable address. You need a pointer as a modifiable lvalue.
By assigning(trying) to a contant string literal, you are taking the address of it. Different address causes that illegality.
"hello" allocates some space in memory and gives and address. Then you take its address to initialize the array.

Is this typecast correct?

Basically my code crashes in NucleoProf_init, judging by gdb's stack-trace, and by the fact that is the only function that I call.
#include <HsFFI.h>
static char *argv[] = {"NucleoProf", "", "", 0};
static int argc = 1;
HsBool NucleoProf_init(void){
// Initialize Haskell runtime
hs_init(&argc, (char***)&argv );
return HS_BOOL_TRUE;
}
I suspect that it is the way I pass the argv argument, or perhaps, the typecast of argv, because the stack-trace contains the following:
#3 0x00007ffff5956282 in setFullProgArgv ()
from /usr/lib/ghc/libHSrts-ghc7.4.1.so
#4 0x00007ffff5956d04 in hs_init_ghc () from /usr/lib/ghc/libHSrts-ghc7.4.1.so
#5 0x00007ffff5b9ed4f in NucleoProf_init ()
Question: Is this the correct way of "synthesizing" a trivial command line?
You can try this:
char ** p = argv;
hs_init(&argc, &p);
It's unclear why you would need to pass the array by address, but I don't know the API you're using. Double-check the manual to see if those values can be changed by the function and if you need to process them afterwards.
It's wrong because, firstly, it's the wrong type. Taking the address of an array results in a pointer to an array. &argv has type char *(*)[4], i.e. "a pointer to an array of 4 pointers to char". This is not the char *** type that you want. You are forcibly casting it to hide the type incompatibility.
You want a char ***, which is a pointer to an actual char ** variable. But you do not have a char ** variable anywhere. You have argv, a char *[4] variable, which is completely different. An array variable is just that -- a collection of its elements in sequence in memory; there is no address of anything stored anywhere in memory.
Your fundamental confusion might be the fact that arrays are not pointers. Arrays have different sizes (the size of an array is its length times size of its component type), pointer and array types (pointer to an array is very different from from a pointer to a pointer; same with array of arrays and array of pointers), and semantics (arrays cannot be assigned or passed). An array expression can implicitly degrade to a pointer rvalue in some situations; but in this case, you are taking the address, which needs an lvalue, so it does not apply. Never confuse arrays with pointers.

Resources