What's char* const argv[]? - c++

I'm studying linux C++ programing when I see
int execve(const char *path,
char *const argv[],
char *const envp[]);
I don't understand what is char *const argv[] . I know char *const foo is a const pointer to char. And const char *foo is a pointer to a const char. But what's char *const argv[]?
Is it an array of const pointers to char or an array of pointers to const char?
And I have a vector<string> now, how to convert it to char *const argv[]?

Is it an array of const pointers to char or an array of pointers to const char?
Read types right to left:
const always binds to the left (unless it is on the very left)
char *const argv[]
^^ Array of
^^^^^ const
^ pointer to
^^^^ char
An array of "const pointer" to char
So the pointers in the array can not be modified.
But what they point at can be modified.
Note: As pointed out by Steve below. Because this is being used as a parameter. It actually decays to a "A pointer to "const pointer" to char" or "char* const *".
And I have a vector<string> now, how to convert it to char *const argv[]?
int execve(const char *path,
char *const argv[],
char *const envp[]);
int main()
{
std::vector<std::string> data; /// fill
std::vector<char*> argv;
std::vector<char*> envp;
for(std::vector<std::string>::iterator loop = data.begin(); loop != data.end(); ++loop)
{
argv.push_back(&(*loop)[0]);
}
// I also bet that argv and envp need to be NULL terminated
argv.push_back(NULL);
envp.push_back(NULL);
execve("MyAppName", &argv[0], &envp[0]);
}

cdecl.org says:
char *const argv[]
declare argv as array of const pointer to char

// says: argv is a constant pointer pointing to a char*
int main(int c, char *const argv[]);
You won't be able to change "argv" within the function.
As you are just learning C, i recommend you to really try to understand the differences between arrays and pointers first instead of the common things.
In the area of parameters and arrays, there are a few confusing rules that should be clear before going on. First, what you declare in a parameter list is treated special. There are such situations where things don't make sense as a function parameter in C. These are
Functions as parameters
Arrays as parameters
Arrays as parameters
The second maybe is not immediately clear. But it becomes clear when you consider that the size of an array dimension is part of the type in C (and an array whose dimension size isn't given has an incomplete type). So, if you would create a function that takes an array by-value (receives a copy), then it could do so only for one size! In addition, arrays can become large, and C tries to be as fast as possible.
In C, for these reasons, array-values are not existent. If you want to get the value of an array, what you get instead is a pointer to the first element of that array. And herein actually already lies the solution. Instead of drawing an array parameter invalid up-front, a C compiler will transform the type of the respective parameter to be a pointer. Remember this, it's very important. The parameter won't be an array, but instead it will be a pointer to the respective element type.
Now, if you try to pass an array, what is passed instead is a pointer to the arrays' first element.
Excursion: Functions as parameters
For completion, and because i think this will help you better understand the matter, let's look what the state of affairs is when you try to have a function as a parameter. Indeed, first it won't make any sense. How can a parameter be a function? Huh, we want a variable at that place, of course! So what the compiler does when that happens is, again, to transform the function into a function pointer. Trying to pass a function will pass a pointer to that respective function instead. So, the following are the same (analogous to the array example):
void f(void g(void));
void f(void (*g)(void));
Note that parentheses around *g is needed. Otherwise, it would specify a function returning void*, instead of a pointer to a function returning void.
Back to arrays
Now, i said at the beginning that arrays can have an incomplete type - which happens if you don't give a size yet. Since we already figured that an array parameter is not existent but instead any array parameter is a pointer, the array's size doesn't matter. That means, the compiler will translate all of the following, and all are the same thing:
int main(int c, char **argv);
int main(int c, char *argv[]);
int main(int c, char *argv[1]);
int main(int c, char *argv[42]);
Of course, it doesn't make much sense to be able to put any size in it, and it's just thrown away. For that reason, C99 came up with a new meaning for those numbers, and allows other things to appear between the brackets:
// says: argv is a non-null pointer pointing to at least 5 char*'s
// allows CPU to pre-load some memory.
int main(int c, char *argv[static 5]);
// says: argv is a constant pointer pointing to a char*
int main(int c, char *argv[const]);
// says the same as the previous one
int main(int c, char ** const argv);
The last two lines say that you won't be able to change "argv" within the function - it has become a const pointer. Only few C compilers support those C99 features though. But these features make it clear that the "array" isn't actually one. It's a pointer.
A word of Warning
Note that all i said above is true only when you have got an array as a parameter of a function. If you work with local arrays, an array won't be a pointer. It will behave as a pointer, because as explained earlier an array will be converted to a pointer when its value is read. But it should not be confused with pointers.
One classic example is the following:
char c[10];
char **c = &c; // does not work.
typedef char array[10];
array *pc = &c; // *does* work.
// same without typedef. Parens needed, because [...] has
// higher precedence than '*'. Analogous to the function example above.
char (*array)[10] = &c;

Related

How can I make my strchr function take both 'const char' and 'char' arrays as first parameter?

I am expecting my function to work just the same as the function "strchr" I am using from the cstring / string.h library.
I understand that I cannot cast a " const char* " variable / array to " char* ". Yet, how is the predefined "strchr" function able to be passed both "const char" and "char" data type arrays and work just fine ? How could I change mine so that it works, too ?
char* my_strchr(char *place_to_find, char what_to_find)
{
for(int i=0;place_to_find[i];i++)
if(what_to_find==place_to_find[i]) return place_to_find+i;
return NULL;
}
...
int main()
{
const char vocals1[]="AEIOUaeiou";
char vocals2[]="AEIOUaeiou";
cout<<strchr(vocals1,'e');
cout<<strchr(vocals2,'e');
cout<<my_strchr(vocals1,'e');
cout<<my_strchr(vocals2,'e');
return 0;
}
As you could probably already tell, my third cout<< does not work.
I am looking for a way to change my function ( I am guessing the first parameter should somehow be typecast ).
How can I make my strchr function take both 'const char' and 'char' arrays as first parameter?
You could change the argument to be const char*. That would allow passing both pointers to char as well as const char.
However, you return a non-const pointer to that array, which you shouldn't do if the pointer is to a const array. So, when passing a const char*, your function should also return const char*
Yet, how is the predefined "strchr" function able to be passed both "const char" and "char" data type arrays and work just fine ?
There are two predefined std::strchr functions. One that accepts and returns char* and another that accepts and returns const char*:
const char* strchr(const char* str, int ch);
char* strchr( char* str, int ch);
If you would wish to return char* in case of char* argument, you need to have a different function for each case, like the standard library has. You can use an overload like the standard library does, or you can use a function template to generate both variations without repetition:
template<class Char>
Char* my_strchr(Char *place_to_find, char what_to_find)
Note that the C version of the function is declared char *strchr(const char *str, int ch). This makes the single function usable in both cases, but is unsafe, since the type system won't be able to prevent the user of the function from modifying though the returned non-const pointer even when a const array was passed as an argument.
Make two overloads, the way it is done in the C++ standard library:
char* my_strchr( char *place_to_find, char what_to_find)
const char* my_strchr(const char *place_to_find, char what_to_find)
Even though in your case only the second overload would be sufficient (demo) you would not be able to support an important use case, when you need to find a character and then replace it:
// This would not work with only one overload:
char *ptr = my_strchr(vocals2,'e');
if (ptr) {
*ptr = 'E';
}
That is why the non-const overload is necessary.
Note: I assume that you are doing this as a learning exercise, because C-style string functions are no longer necessary for new development, having been replaced with std::string functionality.
Short answer: make the type of the place_to_find const char*
The reason for your error is that you cannot implicitly convert a pointer to a const char to a pointer to a non-const char. If you could, then you could change the char that the pointer points to and it would defeat the purpose of having it a const char type in the first place.
You can implicitly convert a pointer to a non-const char to a pointer to a const char because it does not remove any restrictions.
L.E.: Also, the return value should be a const char*, because again, if you don't, it would remove the const restriction which is not allowed. The only problem with that is that you would not be able to modify the array through the pointer returned. If you also want that, then you would have to overload the method on both char and const char.

Meaning of the syntax `const char (&x)[]`

What is the meaning of the syntax const char (&x)[] in C++, is it something like pass pointer by reference to a function call?
Is it the same like const char x[], which defines x as const char*?
And if both are one and the same where should I use const char (&x)[] instead of const char x[]?
const char (&)[] is a reference to a const char array. The expression you showed is declaring x as a one of those.
Is it the same like const char x[]
it's a reference to one of those
... which defines x as const char*
Um, no, let's take a step back.
const char array[5]
declares array as a an array of 5 const chars. It doesn't declare a pointer. However, arrays easily decay to pointers in C++, so for example
void foo(const char *);
// ...
foo(array);
is legal. In fact, arrays decay to pointers so easily, it takes extra care to pass them somewhere without decay:
template <size_t N>
void bar(const char (&x)[N]);
bar(array);
will actually get a reference to the array, and as a bonus, allow bar to deduce the array size.
Note that the only useful difference between a pointer and an array is the number of elements - when I quoted you saying const char x[], I assume there will really be a number between the square brackets. If you omit that, it doesn't have any benefit over a pointer, unless you initialize it:
const char x[] = { 'h', 'e', 'l', 'l', 'o' };
will still allow that call to bar to deduce N=5, even though you never wrote a literal 5 in your code.
It can also be used when you want your function to accept a fixed-length array:
void fun(const char (&x)[50]);
It's also usable with multi-dimensional arrays (but prefer std::vector or std::array when possible).
What is the meaning of the syntax const char (&x)[] in C++
This syntax mean that you want to use a reference to an array of const char.
Is it the same like const char x[], which defines x as const char*?
No, it isn't the same, the main difference with const char* is that the size of the array became part of the type, so you can't pass an array with a different number of elements.
The main use of reference to array is in template where the number of elements is than deduced

what the differences between char** and char*[]

Recently, I need to declare a string array, so I wrote down the following statement:
const char** directories = {"cricket_batting", "cricket_bowling", "croquet", "tennis_forehand", "tennis_serve", "volleyball_smash"};
However, g++ showed me the error:
error: scalar object ‘directories’ requires one element in initializer
So I changed the statement to this:
const char* directories[] = {"cricket_batting", "cricket_bowling", "croquet", "tennis_forehand", "tennis_serve", "volleyball_smash"};
This time, it was right. But I can't exactly know the difference between char** and char[].
= {...};
Initialisation of this form is known as list-initialization.
const char**
This type is a "pointer to pointer to const char".
const char*[]
This type is an "array of pointer to const char".
Simply put, you cannot initialise a pointer with list-initialization. You can initialise an array with list-initialization; it initializes each of the elements in the array with the items in the braced list.
The reason comes down to what exactly you get from a declaration. When you declare a const char**, all you get is a single pointer object. It's a const char**, which is a pointer promising to point at a pointer to const char. Nonetheless, all you actually have is the outer pointer. You can't then initialise this as though it's an array, since you only have one pointer. Where exactly are you going to store the elements of the initialization list? There is no array of pointers in which you can store them.
However, when you declare a const char*[], what you get is an array of pointers. The size of the array is determined by the size of the list because you have omitted it.
The former is a pointer to a pointer to const char while the latter is an array to pointer to const char. The latter is the correct way to initialize an array of strings.
You would need to allocate memory using new to set up char** , for you can't simply initialize pointers with { }.
When you write int a[] we are making an array of integers. Similarly when you write const char* directories[] you are creating an array of char* pointers. Now each char* can point to a char or a string as in your case. This creates individual string constants and assigns the base address of each to the corresponding pointer in the array
char **directories is pointer to a pointer and you can't assign value to a pointer using { .. }

Can't convert from char*[] to char**

When I try a test program with just these two lines
char array[256];
char** arrayPointer=&array;
I get the error
cannot convert from char*[256] to char**.
Yet if I do this:
char array[256];
char* temp=array;
char** arrayPointer=&temp;
I get no such complaint.
I figured that it was eclipse acting buggy (which my eclipase is acting funny right now) but when I tried to do a cast of the &array to char** for the function I ended up with unusual behavior and my debugger implying that the array isn't being modified as it should.
PS. all of this was written by hand, forgive typos.
In C++, arrays and pointers are not the same thing. Arrays in many cases can implicitly be converted to a pointer, but array types and pointer types are different.
In your case, the variable
char array[256];
has type char[256]. If you take its address by writing &array, you get a pointer to an array of 256 chars, which has type char (*)[256]. This is not the same a char**. This is actually a good thing. If you could do the conversion, what would happen if you did this?
char array[256];
char** ptrPtr = &array; // Not legal!
*ptrPtr = new char[256];
In this case, the third line would "reassign" array to point to a new array of 256 elements. However, array is not a pointer! This operation is meaningless.
The reason you got a weird debugger error when writing
char array[256];
char** ptrPtr = (char**) &array; // Legal, but Bad Times!
*ptrPtr = new char[256];
is that the cast you've put in results in undefined behavior. You're pretending that a pointer to an array of 256 actual char objects is really a pointer to a pointer to a char. This is a meaningless cast, so all bets are off when you do it.
On the other hand, if you explicitly introduce a char* variable like this:
char array[256];
char* ptr = array;
char** ptrPtr = &ptr;
Then everything is fine. In the second line, you create a pointer (actual type char*) that points to the first element of array. In the third line, you create a pointer to that new pointer. If you then write
*ptrPtr = new char[137];
Then nothing bad happens; you've just changed where ptr was pointing, and didn't destroy array.
Hope this helps!

Cannot cast array to pointer

I have the following source:
#include <iostream>
using namespace std;
void main(int j)
{
char arr[10][10];
char** ptr;
ptr = arr;
}
when I compile it using VS2010 I get this error:
error : a value of type "char (*)[10]" cannot be assigned to an entity of type "char **"
I thought arrays in c++ were just pointers. So a char[][] could also be char**. What am I doing wrong?
Arrays aren't pointers.
An array decays to a pointer in most circumstances, but this isn't recursive. So a T[] decays to a T *, but a T[][] doesn't decay to a T**.
I suggest reading the whole of the C FAQ chapter on arrays and pointers; in particular, the section on 2D arrays and pointers-to-pointers.
The existing answers, though correct, don't make it very clear that there is a fundamental reason (apart from the language rules) why you cannot cast char [10][10] to char **. Even if you force the cast by saying something like
char arr[2][2];
char ** ptr = (char **)arr;
it won't actually work.
The reason is that in C and C++ a two-dimensional array is laid out in memory as an array of arrays. That is, in C a two-dimensional array is laid out in memory as a single allocation,
arr -> arr[0][0]
arr[0][1]
arr[1][0]
arr[1][1]
You'll notice that arr doesn't point to a char * but to arr[0][0] which is a char; therefore, while arr can be cast to a char *, it cannot be cast to a char **.
The correct forced cast would be
char arr[2][2];
char * ptr = (char *)arr;
If you don't want to force the cast (always a good idea if possible!) you would say
char arr[2][2];
char * ptr = arr[0];
or, to make the outcome clearer,
char arr[2][2];
char * ptr = &arr[0][0];
And you now have (in effect) a pointer to an array of 4 characters. [Proviso: I can't find anything in the C standard that prohibits an implementation from adding padding between two rows of an array, but I don't believe that any real-world implementations do so, and common coding practice depends on the assumption that there will be no such padding.]
If you really needed to convert arr to a char ** you would have to explicitly create an array of pointers:
char arr[2][2]
char * arr_ptrs[2];
char ** ptr;
arr_ptrs[0] = arr[0];
arr_ptrs[1] = arr[1];
ptr = arr_ptrs;
The C language could in principle do this for you automatically if you tried to cast a two-dimensional array to a pointer-to-pointer but that would violate the programmer's expectation that a cast does not have side-effects such as allocating memory.
In Java, by way of comparison, a two-dimensional array is always an array of pointers to arrays, so that the array
char arr[][] = { {'a', 'b'}, {'c', 'd'} };
is laid out in memory as three separate allocations, in arbitrary order and not necessarily adjacent,
arr -> arr[0]
arr[1]
arr[0] -> arr[0][0]
arr[0][1]
arr[1] -> arr[1][0]
arr[1][1]
You will immediately notice that this requires more memory than the equivalent C array, and is slower to evaluate at runtime. On the other hand, it does allow the rows of an array to have different lengths.
The types char[10][10] and char** and char (*)[10] are all different types. However, the first one cannot convert into the second one, it can convert into the third one.
So try this:
char arr[10][10];
char (*ptr)[10];
ptr = arr; //ok
It will work, because as I said object of type char[10][10] can convert into an object of type char (*)[10]. They're compatible types.
I thought arrays in c++ were just pointers.
No, an array is a set of objects laid out contiguously in memory. In some circumstances, they are convertible to a pointer to the first element.
So a char[][] could also be char**
No. It is convertible to a pointer to the first one-dimensional array (which is the type char (*)[10] mentioned in the error message); but that array is not a pointer, so it is not convertible to a pointer-to-pointer.
The error exactly tells you whats wrong a double dimensional array can be assigned to an pointer to array not an double pointer. So what you need is:
char (*ptr)[10] = arr;
What am I doing wrong?
First things first
Arrays are not pointers!! but they act sometimes like pointers.
The rule is:
An expression with array type (which could be an array name) converts to a pointer anytime an array type is not legal, but a pointer type is.
So if you have a single dimensional array:
char arr[10];
Then arr decays to address of the zeroth element it has the type char *.
Hence:
char *ptr = arr;
But if you have an 2 dimensional array which is essentially an array of arrays.
char arr[10][10];
Then arr decays to the pointer to an array of 10 characters.
So, In order to assign arr to something, you will need that something to match the type, which is pointer to an array of characters.
Hence:
char (*ptr)[10] = arr;
Arrays are NOT just pointers -- arrays are arrays. Arrays are not first-class types, however, so you can't use them in many places. The thing that causes your confusion is that array names CAN be implicitly converted into pointers to the array's first element, which means that you can use an array in many places where you need a pointer and it 'just works'. This, however, is not one of those places.
Arrays are not pointers (I notice a lot of books tend to make you think this, though). They are something completely different. A pointer is a memory address while an array is a contiguous set of some data.
In some cases, an array can decay to a pointer to its first element. You can then use pointer arithmetic to iterate through the contiguous memory. An example of this case would be when passing an array to a function as a parameter.
What you probably want to do here is something like:
char arr[10];
char * i = &arr[0];
Obviously you'll need to use 2D arrays and char** in your case. I'll leave that to you to figure out :)
When you cast ar[10][10] to pointer you will get a array of pointer as said above *ar[10] and not **ar.