why is array not getting formed? - c++

Why is it that when I define an array of floats like :
const int i[] = { 1, 2, 3, 4 };
float f[i[3]]; // Illegal
outside any function (i.e in global), it is illegal to do so.
while if I do the same thing inside any function (including main() ) like:
void f() {
const int i[] = { 1, 2, 3, 4 };
float f[i[3]];
cout << sizeof(f);
}
main()
{
f();
}
then everything works fine and it outputs the size as 16 successfully.
why is this difference ? Is it because of the change in the storage location from static (initially) to the stack ?
(PS: I know that in C++ an array can not be defined using a variable whose value is not known at the compile time, BUT still, then how is it working in the function ? )

By default, if you don't specify strict standard compliance, compilers often allow C features to be used in C++ (and vice versa). Both GCC and Clang allow such a C99 feature, namely VLAs, to be used by default. (Visual Studio, OTOH, does not support VLAs even in C mode.) Note, however, that VLAs can be defined only in block scope.
6.7.6.2 Array declarators
2 If an identifier is declared as having a variably modified type, it shall be an ordinary
identifier (as defined in 6.2.3), have no linkage, and have either block scope or function
prototype scope. If an identifier is declared to be an object with static or thread storage
duration, it shall not have a variable length array type.
So, a global VLA does not work in C++ (with lax compiler settings) whereas a function local VLA does.
Try compiling the following
$ cat float.cpp
int main() {
int i = 2;
const float f[i] = { 1, 2 };
}
with g++ -std=c++98 -Wall -ansi -pedantic float.cpp and you'd get something like:
float.cpp: In function 'int main()':
float.cpp:3:18: warning: ISO C++ forbids variable length array 'f' [-Wvla]
float.cpp:3:29: error: variable-sized object 'f' may not be initialized
float.cpp:3:15: warning: unused variable 'f' [-Wunused-variable]

C++11 allow this :
#include <iostream>
constexpr int i[] = {1,2,3,4};
float k[i[2]];
int main()
{
std::cout << sizeof(k) << "\n";
}
and it'll be well formed.
As for the initial question, this is probably a G++ extension.

Related

Why is pointer to array of unknown bounds sometimes illegal? [duplicate]

I just found out that this is illegal in C++ (but legal in C):
#include <stdio.h>
#include <stdlib.h>
#define ARRAY_LENGTH(A) (sizeof(A) / sizeof(A[0]))
int accumulate(int n, const int (*array)[])
{
int i;
int sum = 0;
for (i = 0; i < n; ++i) {
sum += (*array)[i];
}
return sum;
}
int main(void)
{
int a[] = {3, 4, 2, 4, 6, 1, -40, 23, 35};
printf("%d\n", accumulate(ARRAY_LENGTH(a), &a));
return 0;
}
It compiles without problems using gcc -std=c89 -pedantic but fails to compile using g++. When I try to compile it using g++ I get these error messages:
main.cpp:5:37: error: parameter 'array' includes pointer to array of unknown bound 'int []'
int accumulate(int n, int (*array)[])
^
main.cpp: In function 'int main()':
main.cpp:18:50: error: cannot convert 'int (*)[9]' to 'int (*)[]' for argument '2' to 'int accumulate(int, int (*)[])'
printf("%d\n", accumulate(ARRAY_LENGTH(a), &a));
I have been using this in my C code for a long time and I had no idea that it was illegal in C++. To me this seems like a useful way to document that a function takes an array whose size is not known before hand.
I want to know why this is legal C but invalid C++. I also wonder what it was that made the C++ committee decide to take it away (and breaking this compatibility with C).
So why is this legal C code but illegal C++ code?
Dan Saks wrote about this in 1995, during the lead up to C++ standardisation:
The committees decided that functions such as this, that accept a
pointer or reference to an array with unknown bound, complicate
declaration matching and overload resolution rules in C++. The
committees agreed that, since such functions have little utility and
are fairly uncommon, it would be simplest to just ban them. Hence, the
C++ draft now states:
If the type of a parameter includes a type of the form pointer to
array of unknown bound of T or reference to array of unknown bound of
T, the program is ill-formed.
C++ doesn't have C's notion of "compatible type". In C, this is a perfectly valid redeclaration of a variable:
extern int (*a)[];
extern int (*a)[3];
In C, this is a perfectly valid redeclaration of the same function:
extern void f();
extern void f(int);
In C, this is implementation-specific, but typically a valid redeclaration of the same variable:
enum E { A, B, C };
extern enum E a;
extern unsigned int a;
C++ doesn't have any of that. In C++, types are either the same, or are different, and if they are different, then there is very little concern in how different they are.
Similarly,
int main() {
const char array[] = "Hello";
const char (*pointer)[] = &array;
}
is valid in C, but invalid in C++: array, despite the [], is declared as an array of length 6. pointer is declared as a pointer to an array of unspecified length, which is a different type. There is no implicit conversion from const char (*)[6] to const char (*)[].
Because of that, functions taking pointers to arrays of unspecified length are pretty much useless in C++, and almost certainly a mistake on the part of the programmer. If you start from a concrete array instance, you almost always have the size already, so you cannot take its address in order to pass it to your function, because you would have a type mismatch.
And there is no need for pointers to arrays of unspecified length in your example either: the normal way to write that in C, which happens to also be valid in C++, is
int accumulate(int n, int *array)
{
int i;
int sum = 0;
for (i = 0; i < n; ++i) {
sum += array[i];
}
return sum;
}
to be called as accumulate(ARRAY_LENGTH(a), a).

Pointer to array of unspecified size "(*p)[]" illegal in C++ but legal in C

I just found out that this is illegal in C++ (but legal in C):
#include <stdio.h>
#include <stdlib.h>
#define ARRAY_LENGTH(A) (sizeof(A) / sizeof(A[0]))
int accumulate(int n, const int (*array)[])
{
int i;
int sum = 0;
for (i = 0; i < n; ++i) {
sum += (*array)[i];
}
return sum;
}
int main(void)
{
int a[] = {3, 4, 2, 4, 6, 1, -40, 23, 35};
printf("%d\n", accumulate(ARRAY_LENGTH(a), &a));
return 0;
}
It compiles without problems using gcc -std=c89 -pedantic but fails to compile using g++. When I try to compile it using g++ I get these error messages:
main.cpp:5:37: error: parameter 'array' includes pointer to array of unknown bound 'int []'
int accumulate(int n, int (*array)[])
^
main.cpp: In function 'int main()':
main.cpp:18:50: error: cannot convert 'int (*)[9]' to 'int (*)[]' for argument '2' to 'int accumulate(int, int (*)[])'
printf("%d\n", accumulate(ARRAY_LENGTH(a), &a));
I have been using this in my C code for a long time and I had no idea that it was illegal in C++. To me this seems like a useful way to document that a function takes an array whose size is not known before hand.
I want to know why this is legal C but invalid C++. I also wonder what it was that made the C++ committee decide to take it away (and breaking this compatibility with C).
So why is this legal C code but illegal C++ code?
Dan Saks wrote about this in 1995, during the lead up to C++ standardisation:
The committees decided that functions such as this, that accept a
pointer or reference to an array with unknown bound, complicate
declaration matching and overload resolution rules in C++. The
committees agreed that, since such functions have little utility and
are fairly uncommon, it would be simplest to just ban them. Hence, the
C++ draft now states:
If the type of a parameter includes a type of the form pointer to
array of unknown bound of T or reference to array of unknown bound of
T, the program is ill-formed.
C++ doesn't have C's notion of "compatible type". In C, this is a perfectly valid redeclaration of a variable:
extern int (*a)[];
extern int (*a)[3];
In C, this is a perfectly valid redeclaration of the same function:
extern void f();
extern void f(int);
In C, this is implementation-specific, but typically a valid redeclaration of the same variable:
enum E { A, B, C };
extern enum E a;
extern unsigned int a;
C++ doesn't have any of that. In C++, types are either the same, or are different, and if they are different, then there is very little concern in how different they are.
Similarly,
int main() {
const char array[] = "Hello";
const char (*pointer)[] = &array;
}
is valid in C, but invalid in C++: array, despite the [], is declared as an array of length 6. pointer is declared as a pointer to an array of unspecified length, which is a different type. There is no implicit conversion from const char (*)[6] to const char (*)[].
Because of that, functions taking pointers to arrays of unspecified length are pretty much useless in C++, and almost certainly a mistake on the part of the programmer. If you start from a concrete array instance, you almost always have the size already, so you cannot take its address in order to pass it to your function, because you would have a type mismatch.
And there is no need for pointers to arrays of unspecified length in your example either: the normal way to write that in C, which happens to also be valid in C++, is
int accumulate(int n, int *array)
{
int i;
int sum = 0;
for (i = 0; i < n; ++i) {
sum += array[i];
}
return sum;
}
to be called as accumulate(ARRAY_LENGTH(a), a).

Defining arrays with(out) constant expressions

I am slowly bringing myself up to c++11. I was looking at constexpr and stumbled into this wikipedia article which lead me to "something completely different". The basic example it gives is:
int get_five() {return 5;}
int some_value[get_five() + 7]; // Create an array of 12 integers. Ill-formed C++
It states "This was not legal in C++03, because get_five() + 7 is not a constant expression." and says that adding constexpr to the get_five() declaration solves the problem.
My question is "What problem?". I compiled that code with neither errors nor warnings. I played with it making it horribly non constant:
#include <iostream>
int size(int x) { return x; }
int main()
{
int v[size(5) + 5];
std::cout << sizeof(v) + 2 << std::endl;
}
This compiles with no complaints using:
g++ -Wall -std=c++03
and when executed I get the (correct) answer 42.
I admit that I generally use stl containers, not arrays. But I thought (and apparently so did wikipedia) that compilation of the above code would fail miserably. Why did it succeed?
Variable-length arrays (that is, arrays whose size is determined by a non-constant expression) are allowed in C, and some C++ compilers allow them as an extension to the language. GCC is one such compiler.
You'll get a warning if you compile with -pedantic or -Wvla, or an error with -pedantic-errors. Use those flags if you want to avoid non-standard compiler extensions.
As it has been said already some C++ compilers support C feature named Variable Length Array(s) whose sizes can be specified at run-time.
However VLA(s) may not be declared with static storage duration. The program you showed
#include <iostream>
int size(int x) { return x; }
int main()
{
int v[size(5) + 5];
std::cout << sizeof(v) + 2 << std::endl;
return 0;
}
can be compiled. However if you place the array outside any function then the code will not be compiled. Consider the following program that is similar to your original program with minor changes.
#include <iostream>
int size(int x) { return x; }
int v[size(5) + 5];
int main()
{
std::cout << sizeof(v) + 2 << std::endl;
return 0;
}
In this case the compiler will issue an error. However if you will specify constexpr for function size then the above program will be compiled successfully
#include <iostream>
constexpr int size(int x) { return x; }
int v[size(5) + 5];
int main()
{
std::cout << sizeof(v) + 2 << std::endl;
return 0;
}
The C++ Standard requires that sizes of arrays would be constant expressions.
8.3.4 Arrays [dcl.array]
1 In a declaration T D where D has the form
D1 [ constant-expressionopt] attribute-specifier-seqopt
and the type of the identifier in the declaration T D1 is “derived-declarator-type-list T”, then the type of the identifier of D is an array type;
Take into acount that not all C++ compilers (and even C compilers; for C compilers it is implementation defined whether a compiler supports VLA) have such a language extension as VLA. So if you want that your program would be C++ compliant then you should not rely on specific language extensions of a compiler.
Some compilers have extensions, which implement VLA (Variable Length Arrays).
Compile with -pedantic and you'll see the difference.

array bound is not an integer constant before is resolved by declaring array inside main

program 1
#include <iostream>
std::size_t three() {
return 3;
}
int i[three()];
int main()
{
return 0;
}
program 2
std::size_t three() {
return 3;
}
int main()
{
int i[three()];
return 0;
}
The issue here is Program 1 as expected gives the compilation error
"error: array bound is not an integer constant before ']' token"
But I have no idea why the Program 2 is compiled successfully?
C99 allows int i[three()]; to declare a variable-length array, but only if it is not of static or thread storage duration. Declaring it in file scope means that it has static storage duration, so it's illegal even in C99. Declaring it in main() the way you did means that it has automatic storage duration, which is allowed in C99.
Some compilers such as GCC and Clang supports it in C89 and C++ modes as well, as an extension. But this is not legal C++ as far as the standard is concerned. Both GCC and Clang will produce a warning for this code if you compile with -pedantic.

"const variables" set by a constructor for expressing the bounds of a C++ array?

The following code compiles and it seems to run fine:
class Test {
private:
const unsigned MAX;
public:
Test (const unsigned int n) : MAX(n) { }
void foo () {
int array[MAX];
...
}
};
but is it really OK? I mean:
Test a (3);
Test b (8);
does array actually have 3 and 8 cells respectively?
If so, is it because array is an automatic var and gets instantiated with the appropriate dimension?
Thanks
What you have written is valid in c99 but not valid c++.
I am of course talking about your use of VLA's, not the full snippet.
When compiling using g++ -pedantic -ansi -Wall we get the below warning;
foo.cpp: In member function 'void Test::foo()':
foo.cpp:18:23: warning: ISO C++ forbids variable length array 'array' [-Wvla]
As mentioned in the above warning the pattern you are using is often referred to as using a variable length array, which is standard in C99 and "allowed" in C++ through a g++ extension.
I'd recommend you to use a STL container instead of hacks as these, for one single reason; what you are doing is not legal, and therefor isn't guaranteed to be portable cross compilers.
Variable length arrays are not standard C++. You could make Test a template instead:
template <int MAX>
class Test {
public:
Test () {}
void foo () {
int array[MAX];
}
};
Test<4> t4;
Test<8> t8;
You are correct that this is not legal C++. If it works on your compiler, it is probably because you are using a GCC extension.