So, I see that the practice for dynamic allocating of an array of pointers looks like this:
int **array = new int *[10];
And indeed, using syntax:
int *array[] = new int *[10];
results in error:
/Users/Malachi/Projects/playground/playground.gcc/src/pgccc-5/main.cpp:8: error: definition of variable with array type needs an explicit size or an initializer
const char* test_array[];
^
I'm always more comfortable using pure pointer syntax anyway. However, what bothers me is lines like this:
int main(int argc, char *argv[])
are valid. I'm accustom to empty array brackets [] more or less aliasing out to a pointer type. It seems to me char *argv[] is subject to almost exactly the same constraints as my int *array[], so why is the syntax permitted in one scenario but not the other?
EDIT: It appears the simpler case of int array[] = new int[10] exhibits the same behavior
This one:
int *array[] = new int *[10];
is not a valid syntax. The reason the left side has a type of an array of pointers to int, and the right side has a type of a pointer to a pointer to int. So the assignment is not legal due to the different types of left and right sides.
On the other hand, arrays decay into pointers. It means, that when you declare a function in the form of:
void foo(int* arr[])
the compiler sees it as:
void foo(int** arr)
The rule above applies only for functions, but not for assignments like in the first example.
It's because function parameter declaration is something different than variable declaration.
An array can decay into a pointer for the first dimension.
You can explicitly express that function expects an array rather than a pointer through the declaration using [] notation in e.g int main(int argc, char *argv[]). They type doesn't matter:
void f(int* i[]) {}
is legal as well. This says "I want an array of pointers to ints". This is more expressive than:
void f(int** i) {}
I'm accustom to empty array brackets [] more or less aliasing out to a pointer type.
That's valid only in the declaration of a function argument.
void foo(int a[]);
is the same as:
void foo(int* a);
However, when declaring or defining variables, they are not the same.
int a[] = {1, 2, 3}; // Valid. Array of 3 ints
is not the same as
int* a = {1, 2, 3}; // Invalid syntax.
Exception
You can use a string literal to intialize a char array or char const*.
char s1[] = "string 1";
char const* s2 = "string 2";
However, you can't use (not in C++ anyway):
char* s2 = "string 2";
Related
Why does int* ptr_arr_int = {1,2}; gives a compiler error, but whereas char* ptr_arr_char = "amruth"; compiles fine?
int* ptr_arr_int = {1,2}; // ->ERROR
char* ptr_arr_char = "amruth"; // ->OK
"amruth" is a const char[7] type in C++, and a char[7] type in C (although the behaviour on attempting to modify the string is undefined).
This can decay to a const char* or char* type respectively in some circumstances, such as yours.
Although an int[2] will similarly decay to an int* in some circumstances, {1, 2} is neither an int[2] nor a const int[2] type; rather it is a brace-initialiser.
As said, the string is an const char[7] array and while that can decay to a char*, {1, 2} cannot decay to int* (it's an brace initializer list, similarly: std::initializer_list). But note there is also the array [] declaration which allows you to automatically declare arrays. Combine that with the list-initialization since C++11 and you are able to initialize both via []:
int ptr_arr_int[] = { 1,2 }; //OK
char ptr_arr_char[] = "amruth"; //OK
A string by itself already implies a storage class -- it's a static (+effectively const) char array declared using special syntax.
In contrast to that, it's not clear how {1, 2} in int *ptr_arr_int = {1, 2} should be stored. Should it be static or auto?
If you want it auto when in a local scope or static when in file scope, then in C>=99 you can explicitly do int *ptr_arr_int = &(int[]){1,2}[0]; (the &[0] is optional).
You could conceivably make this implicit, but how far would you take this?int ****x = {1,2}; initialized by creating an array, a pointer to it, a pointer to that etc., could result int quite a bit of code/data and hiding a lot of code/data under a simple construct isn't quite the C way. It would also not be clear when the brace syntax should be initializing the ultimate target basic type or some of the intermediate pointer(s).
It is simply because the language syntax says so. Regarding arrays:
We must initialize an array with a brace-enclosed list {...}, containing no more items that what will fit inside the array.
As a special rule, char arrays can be initialized with a string literal, "...".
However, you have no arrays in your code, you only have pointers.
A char* can always be set to point at a string literal (in C) so char* ptr_arr_char = "amruth"; is valid. However, it is fishy style, because the string literal cannot be modified. Correct style is to use const char*.
C++ is more strict and string literals in C++ have type const char[]. and a C++ compiler will likely give a warning along the lines of "deprecated conversion" if you don't use const char*.
As for int* ptr_arr_int = {1,2};, it has two problems.
{1,2} is an initializer list, but ptr_arr_int is a single item, so it doesn't make any sense. There is a special, odd rule that actually allows us to use {} when initializing a single variable, but then you must only have one single initializer inside.
You cannot initialize a pointer with an integer. See "Pointer from integer/integer from pointer without a cast" issues.
If you meant to have the pointer point at a temporary array, only existing in the local scope, then you could have used the C feature called compound literal:
int* ptr_arr_int = (int[]){1,2};
This is AFAIK not possible to do in C++ though, unless you rely on non-standard extensions. But in either language you could of course simply do this instead:
int arr[2] = {1,2};
int* ptr_arr_int = arr;
You are declaring an array of pointers to int (int*). But the values you initialize the array with (1 and 2) are not pointers to integers, they are plain integers.
You cannot, because int * is not a structure that could be initialized with two values like you did with = {1,2};.
You could have initialized it this way:
int * ptr_arr_int = nullptr;
The other way could have been using an object that expects two values for construction:
struct Point
{
int a;
int b;
};
then you could have written:
Point p = {1,2};
No... that's not correct... that initialiser is only valid for arrays. Had you written:
int array[] = {1, 2};
it would be valid.... but you are trying to initialise a pointer variable, so this would be valid (despite of the warning, because 0xf45 is not an address):
int *pointer = { 0xf45 };
(because you put only a single entry, and it is considered a single value) but:
int *pointer = { 0xf45, 0 };
(You will get another warning about excess elements in scalar initializer) This means that for a single variable you tried to use two initializers. The braces notation was born to be able to initialise arrays, but also is permitted to be used with scalar types. This means that the two notations:
{ 0 }; /* a single value between braces */
and
0;
are equivalent.
The solution to your problem is to do an extra step, declaring an array and then pointing to it from the pointer:
int array[] = {1, 2};
int *pointer = array;
but think that you can make pointer to point to a different place than the beginning of array, and you cannot array to be magically moved elsewhere. And &array means the address of array array while &pointer means the address of the pointer variable pointer. pointer points to the address of array, but array is a constant pointer pointing to the address of array[0].
Pointers and arrays are somewhat confusing, but never think they are the same thing. Just compare sizeof(char[120]) with sizeof(char *) and you'll see.
I'm reading C++ Primer and in section 6.2 it says:
"Parameter initialization works the same way as variable
initialization."
Yet when I do:
void foo(char* args[]) {return;}
int main() {
char* args[]={"asd","dsa"}; // ok.
foo({"asd","dsa"}); // error.
}
Why is that?
As #T.C. pointed out in the comments, the args in the function argument is converted to a char** because functions can't take arrays as an argument. Since you can't do
char **asd={"asd","dsa"};
the code is illegal. My confusion came from the fact that
char* args[]={"asd","dsa"};
char **asd=args;
is legal.
It is generally possible to take advantage of the new initialization syntax and semantics to use anonymous arrays as arguments, but you will have to jump through a few hoops. For example
typedef const char *CC2[2];
void foo(const CC2 &a) {}
int main() {
foo({ "asd", "dsa" });
}
However, in your case this technique will not help because you are requesting an array-to-pointer conversion on a temporary array. This is illegal in C++.
typedef int A[2];
const A &r = A{ 1, 2 }; // reference binding is OK
int *p = A{ 1, 2 }; // ERROR: taking address is not OK
So, if you really want to do something like this, you can do the following
template <size_t N> void foo(const char *const (&args)[N]) {}
int main() {
foo({ "asd", "dsa" });
}
but that is not exactly what you had in mind originally.
"Parameter initialization works the same way as variable initialization."
This is true as far as calling constructors, I think they mean. However, initializers are special, and different from ordinary value expressions you can assign to a variable or pass to a function call.
C/C++ doesn't have a way to write a literal anonymous array. You can only do it as an initializer as you declare a variable.
Why is that?
First of all in both cases, you need char const* because you are working with string literals.
Secondly, {...} could work if the parameter type was an array, but char*[] is adjusted to char** (due to decayment) which cannot be initialised with a braced-init-list.
Alternatively use std::string and std::vector, as you are already supposed to do:
void foo(std::vector<std::string> args) {return;}
and:
foo({"asd","dsa"});
will work just fine.
I have a array int b[MAXN][MAXN];
When I use int a[][MAXN] = b; (I know that int (*a)[MAXN] is OK)
It gives me an error.
But If I have a function void f(int a[][MAXN])
When I call f(b);
It works!
Does someone can tell me why?
When declaring function parameters, T[] is treated the same as T*, i.e. it declares the parameter as a pointer. So void f(int a[][MAXN]) simply declares the parameter a to be a pointer to int arrays of size MAXN.
For other kinds of variables, it is not the same though. That is when you write int a[][MAXN] = b, this declares a as an array or arrays, not a pointer to arrays. And that's why that doesn't work (because you can't make arrays "point" to other arrays like that).
Note that this isn't specific to arrays of arrays. It's the same if you compare void f(int a[]) to int a[] = b. In the former case the parameter a would simply be an int pointer (just as if you had written int *a) and in the latter case a would be an array and you'd get an error for the same reason as above.
You cannot initialize [in declaration] any array variable with another like this:
int a[1] = {};
int b[] = a;
That's not a decaying to pointer situation. You seem to want to declare a new array, which would be a copy of the first one. When passing arrays as function parameters like you have shown, they'll decay to pointers, and you won't be creating a new array.
If, instead, you want a reference, you could do:
int a[1] = {};
int (&b)[1] = a; // first reference
auto &c = a; // second rerefence
If you want to make a copy, then make a copy:
int a[1] = { 42 };
int b[1];
decltype(a) c;
std::copy(std::begin(a), std::end(a), b);
std::copy(std::begin(a), std::end(a), c);
What is the following code doing?
int g[] = {9,8};
int (*j) = g;
From my understanding its creating a pointer to an array of 2 ints.
But then why does this work:
int x = j[0];
and this not work:
int x = (*j)[0];
The parenthesis are superfluous in your example. The pointer doesn't care whether there's an array involved - it only knows that its pointing to an int
int g[] = {9,8};
int (*j) = g;
could also be rewritten as
int g[] = {9,8};
int *j = g;
which could also be rewritten as
int g[] = {9,8};
int *j = &g[0];
a pointer-to-an-array would look like
int g[] = {9,8};
int (*j)[2] = &g;
//Dereference 'j' and access array element zero
int n = (*j)[0];
There's a good read on pointer declarations (and how to grok them) at this link here: http://www.codeproject.com/Articles/7042/How-to-interpret-complex-C-C-declarations
int g[] = {9,8};
This declares an object of type int[2], and initializes its elements to {9,8}
int (*j) = g;
This declares an object of type int *, and initializes it with a pointer to the first element of g.
The fact that the second declaration initializes j with something other than g is pretty strange. C and C++ just have these weird rules about arrays, and this is one of them. Here the expression g is implicitly converted from an lvalue referring to the object g into an rvalue of type int* that points at the first element of g.
This conversion happens in several places. In fact it occurs when you do g[0]. The array index operator doesn't actually work on arrays, only on pointers. So the statement int x = j[0]; works because g[0] happens to do that same implicit conversion that was done when j was initialized.
A pointer to an array is declared like this
int (*k)[2];
and you're exactly right about how this would be used
int x = (*k)[0];
(note how "declaration follows use", i.e. the syntax for declaring a variable of a type mimics the syntax for using a variable of that type.)
However one doesn't typically use a pointer to an array. The whole purpose of the special rules around arrays is so that you can use a pointer to an array element as though it were an array. So idiomatic C generally doesn't care that arrays and pointers aren't the same thing, and the rules prevent you from doing much of anything useful directly with arrays. (for example you can't copy an array like: int g[2] = {1,2}; int h[2]; h = g;)
Examples:
void foo(int c[10]); // looks like we're taking an array by value.
// Wrong, the parameter type is 'adjusted' to be int*
int bar[3] = {1,2};
foo(bar); // compile error due to wrong types (int[3] vs. int[10])?
// No, compiles fine but you'll probably get undefined behavior at runtime
// if you want type checking, you can pass arrays by reference (or just use std::array):
void foo2(int (&c)[10]); // paramater type isn't 'adjusted'
foo2(bar); // compiler error, cannot convert int[3] to int (&)[10]
int baz()[10]; // returning an array by value?
// No, return types are prohibited from being an array.
int g[2] = {1,2};
int h[2] = g; // initializing the array? No, initializing an array requires {} syntax
h = g; // copying an array? No, assigning to arrays is prohibited
Because arrays are so inconsistent with the other types in C and C++ you should just avoid them. C++ has std::array that is much more consistent and you should use it when you need statically sized arrays. If you need dynamically sized arrays your first option is std::vector.
j[0]; dereferences a pointer to int, so its type is int.
(*j)[0] has no type. *j dereferences a pointer to an int, so it returns an int, and (*j)[0] attempts to dereference an int. It's like attempting int x = 8; x[0];.
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;