Context: C++
Consider the example below
class TestClass
{
private:
int A[];
int *B;
public:
TestClass();
};
TestClass::TestClass()
{
A = 0; // Fails due to error: incompatible types in assignment of `int' to `int[0u]'
B = 0; // Passes
}
A = 0 fails but B = 0 succeeds. What's the catch? What exactly is A? A constant pointer? How do I initialize it then?
The question "what is the difference between int* and int[]?" is a less trivial question than most people will think of: it depends on where it is used.
In a declaration, like extern int a[]; it means that somewhere there is an array called a, for which the size is unknown here. In a definition with aggregate initialization, like int a[] = { 1, 2, 3 }; it means an array of a size I, as programmer, don't want to calculate and you, compiler, have to interpret from the initialization. In a definition without initialization, it is an error, as you cannot define an array of an unknown size. In a function declaration (and/or) definition, it is exactly equivalent to int*, the language specifies that when processing the types of the arguments for functions, arrays are converted into pointers to the contained type.
In your particular case, as declaration of members of a class, int a[]; is an error, as you are declaring a member of an incomplete type. If you add a size there, as in int a[10] then it becomes the declaration of an array of type int and size 10, and that will reserve space for 10 int inside each object of the class. While on the other hand, int *b will only reserve space for a pointer to integers in the class.
The only difference between them is that int A[] in a class would not compile, and should not compile!
Comeau C++ compiler gives this error:
"ComeauTest.c", line 4: error:
incomplete type is not allowed
int A[];
^
Wikipedia says,
Comeau C/C++ has been regarded as the
most standards-conformant C++
compiler.
I therefore would suggest : Don't write such code even if your compiler compiles it.
A is an array, and in C++ you need to specify the array size when you define the variable itself i.e. you need to do something like int A[10]. Then you can access individual elements using A[0], A[1] etc. B is a pointer, so doing B=0; sets the pointer to NULL. If you don't want to specify the size at compile time, but still want array like syntax you can use std::vector<int>.
int A[] is an memory range inside the class instance, where int *B is a pointer that you may or may not initialize later.
so
A=0 means that you want to change a pointer that cannot be changed,
where
B=0 means that you are changing a pointer to point to 0x00000000
A[] is an array with its size undefined. You need to declare it like this:
int A[SIZE];
then initialize it like this:
A[0] = 0,
A[1] = 5,
etc
long answer short, A is of type array
and B is of type pointer to int.
A[] initializes an array of zero size, which you cannot assign a value to.
Btw, you can ask C++ yourself with:
#include <iostream>
#include <typeinfo>
using namespace std;
class TestClass{
private:
int A[];
int* B;
public:
TestClass();
};
TestClass::TestClass(){
cout<<"Type A: "<<typeid(A).name()<<endl;
cout<<"Type B: "<<typeid(B).name()<<endl;
}
int main(int argc, char** argv){
TestClass A;
return 0;
}
The code snapshot you have there should not compile since A[] is effectively a zero sized array, thus an incomplete type, which is not allowed in class specifications.
For e.g. Visual Studio fails to compile this with these errors:
1>...\test.h(4) : warning C4200:
nonstandard extension used :
zero-sized array in struct/union 1>
Cannot generate copy-ctor or
copy-assignment operator when UDT
contains a zero-sized array
1>...\test.h(5) : error C2229: class
'TestClass' has an illegal zero-sized
array
To initialize and use A[] you must declare it as static and initialize it with file scope like this:
class TestClass
{
private:
static int A[];
int *B;
public:
TestClass();
};
TestClass::TestClass()
{
//A = 0; Yes this is wrong, see below.
B = 0; // Passes
}
int TestClass::A[] = {1,2,3};
As for A = 0 this is wrong too and Visual Studio will complain with this error, which is self explanatory:
1>..\test.h(13) : error C2440: '=' : cannot convert from 'int' to 'int []'
1> There are no conversions to array types, although there are conversions to references or pointers to arrays.
LE: Also see David Rodríguez - dribeas answer for a complete interpretation of zero sized arrays.
Related
I have a class board:
class board{
private:
int data[9][3][3] = {};
public:
board(int dataa[9][3][3]);
};
board::board(int dataa[9][3][3]){
this->data = dataa; // error from title here!
I think the error might have something to do with the fact that an array is itself a pointer so it may interfere with types, but the code looks like it should work given that the constructor's argument type dataa is identical to data's type. Any help is appreciated.
An array parameter decays to a pointer so your array 9 of array 3 of array 3 of int (= int data[9][3][3]) in a parameter becomes a pointer to array 3 of array 3 of int (= int (*data)[3][3]).
But even if it didn't, C and C++ don't let you assign an array to an array—you need to use memcpy (or copy the array member by member, but memcpy is frequently more optimal).
//this->data = dataa
std::memcpy(this->data, dataa, sizeof(*dataa) * 9);
//^the innermost dimension got lost in the decay so specify it explicitly (9)
For example a declaration such as that:
int (x) = 0;
Or even that:
int (((x))) = 0;
I stumbled upon this because in my code I happened to have a fragment similar to the following one:
struct B
{
};
struct C
{
C (B *) {}
void f () {};
};
int main()
{
B *y;
C (y);
}
Obviously I wanted to construct object C which then would do something useful in its destructor. However as it happens compiler treats C (y); as a declaration of variable y with type C and thus it prints an error about y redefinition. Interesting thing is that if I write it as C (y).f () or as something like C (static_cast<B*> (y)) it will compile as intended. The best modern workaround is to use {} in constructor call, of course.
So as I figured out after that, it's possible to declare variables like int (x) = 0; or even int (((x))) = 0; but I've never seen anyone actually using declarations like this. So I'm interested -what's the purpose of such possibility because for now I see that it only creates the case similar to the notorious "most vexing parse" and doesn't add anything useful?
Grouping.
As a particular example, consider that you can declare a variable of function type such as
int f(int);
Now, how would you declare a pointer to such a thing?
int *f(int);
Nope, doesn't work! This is interpreted as a function returning int*. You need to add in the parentheses to make it parse the right way:
int (*f)(int);
The same deal with arrays:
int *x[5]; // array of five int*
int (*x)[5]; // pointer to array of five int
There's generally allowed to use parentheses in such declarations because the declaration, from the syntactical point of view looks always like this:
<front type> <specification>;
For example, in the following declaration:
int* p[2];
The "front type" is int (not int*) and the "specification" is * p[2].
The rule is that you can use any number of parentheses as needed in the "specification" part because they are sometimes inevitable to disambiguate. For example:
int* p[2]; // array of 2 pointers to int; same as int (*p[2]);
int (*p)[2]; // pointer to an array of 2 ints
The pointer to an array is a rare case, however the same situation you have with a pointer to function:
int (*func(int)); // declares a function returning int*
int (*func)(int); // declares a pointer to function returning int
This is the direct answer to your question. If your question is about the statement like C(y), then:
Put parentheses around the whole expression - (C(y)) and you'll get what you wanted
This statement does nothing but creating a temporary object, which ceases to living after this instruction ends (I hope this is what you intended to do).
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";
For example a declaration such as that:
int (x) = 0;
Or even that:
int (((x))) = 0;
I stumbled upon this because in my code I happened to have a fragment similar to the following one:
struct B
{
};
struct C
{
C (B *) {}
void f () {};
};
int main()
{
B *y;
C (y);
}
Obviously I wanted to construct object C which then would do something useful in its destructor. However as it happens compiler treats C (y); as a declaration of variable y with type C and thus it prints an error about y redefinition. Interesting thing is that if I write it as C (y).f () or as something like C (static_cast<B*> (y)) it will compile as intended. The best modern workaround is to use {} in constructor call, of course.
So as I figured out after that, it's possible to declare variables like int (x) = 0; or even int (((x))) = 0; but I've never seen anyone actually using declarations like this. So I'm interested -what's the purpose of such possibility because for now I see that it only creates the case similar to the notorious "most vexing parse" and doesn't add anything useful?
Grouping.
As a particular example, consider that you can declare a variable of function type such as
int f(int);
Now, how would you declare a pointer to such a thing?
int *f(int);
Nope, doesn't work! This is interpreted as a function returning int*. You need to add in the parentheses to make it parse the right way:
int (*f)(int);
The same deal with arrays:
int *x[5]; // array of five int*
int (*x)[5]; // pointer to array of five int
There's generally allowed to use parentheses in such declarations because the declaration, from the syntactical point of view looks always like this:
<front type> <specification>;
For example, in the following declaration:
int* p[2];
The "front type" is int (not int*) and the "specification" is * p[2].
The rule is that you can use any number of parentheses as needed in the "specification" part because they are sometimes inevitable to disambiguate. For example:
int* p[2]; // array of 2 pointers to int; same as int (*p[2]);
int (*p)[2]; // pointer to an array of 2 ints
The pointer to an array is a rare case, however the same situation you have with a pointer to function:
int (*func(int)); // declares a function returning int*
int (*func)(int); // declares a pointer to function returning int
This is the direct answer to your question. If your question is about the statement like C(y), then:
Put parentheses around the whole expression - (C(y)) and you'll get what you wanted
This statement does nothing but creating a temporary object, which ceases to living after this instruction ends (I hope this is what you intended to do).
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];.