I am trying to understand an example taken from the C++ Primer Book regarding array initialization.
They say that
The number of elements in an array is part of the array’s type. As a result, the dimension must be known at compile time, which means that the dimension must be a constant
expression
An example follows which is supposed to result in an error:
unsigned cnt = 42; // not a constant expression
string bad[cnt]; // error: cnt is not a constant expression
However, compiling this with g++ 4.8.4 does not result in an error.
Is this an error or outdated information in the book, or is it just a g++ feature?
Yes, it should be a g++ feature.
It will emit a warning when -pedantic option is used.
test program
#include <string>
using std::string;
int main(void){
unsigned cnt = 42; // not a constant expression
string bad[cnt]; // error: cnt is not a constant expression
return 0;
}
result on Wandbox
prog.cc: In function 'int main()':
prog.cc:6:19: warning: ISO C++ forbids variable length array 'bad' [-Wvla]
string bad[cnt]; // error: cnt is not a constant expression
^
I think it is worth to talk about "alloca" here. This is how those types of arrays are implemented. Of course they have their limitations like size of operator is not supported for them. You can check details: http://man7.org/linux/man-pages/man3/alloca.3.html
Related
I am trying to understand whether references to array of unknown bound can be used as call parameter in functions in C++. Below is the example that i have:
EXAMPLE 1
void func(int (&a)[])
{
}
int main()
{
cout << "Hello World" << endl;
int k[] = {1,2,3};
// k[0] = 3;
func(k);
return 0;
}
To my surprise this example 1 above works when compiled with GCC 10.1.0 and C++11 but doesn't work with GCC version lower that 10.x. I don't think that we can have references to arrays of unknown size in C++. But then how does this code compile at the following link: successfully compiled
My second question is that can we do this for a function template? For example,
EXAMPLE 2
template<typename T1>
void foo(int (&x0)[])
{
}
Is example 2 valid C++ code in any version like C++17 etc. I saw usage of example 2 in a book where they have int (&x0)[] as a function parameter of a template function.
I don't think that we can have references to arrays of unknown size in C++.
That used to be the case, although it was considered to be a language defect. It has been allowed since C++17.
Note that implicit conversion from array of known bound to array of unknown bound - which is what you do in main of example 1 - wasn't allowed until C++20.
Is example 2 valid C++ code in any version like C++17
Yes; the template has no effect on whether you can have a reference to array of unknown bound.
The book says that std::array is safer and simpler than other methods of the assignment.
Here's my code:
#include<iostream>
#include<array>
using namespace std;
int main()
{
array<int, 5> a = {1, 2, 3, 4, 5};
a.at(-2) = 100;
cout << a[-2] << endl;
return 0;
}
Why is there no warning or error in this code?
The at member function does a runtime check, and the operator[] does no check (this is intentional design).
You can do compile-time checking by using get:
#include <array>
using namespace std;
int main()
{
array<int, 5> a = {1, 2, 3, 4, 5};
get<1>(a) = 100; // ok
get<-2>(a) = 100; // error
get<5>(a) = 100; // error
}
std::array::at() accepts an argument of size_type which usually is defined in terms of some unsinged type e.g. std::size_t / unsigned long.
The actual argument -2 is of signed int type which is implicitly converted to size_type while passing to at() and becomes a valid size_type number; but, only a wrapped-around one. See example.
By default, you don't get a compiler warning/error for implicit conversion. You have to look for your compiler options like GCC's -Wconversion, Wsign-conversion, etc. to enable these settings. In your case on GCC, -Wsign-conversion will warn you about these conversions; and, in combination with -Werror, these warnings will become errors.
Observe the compiler output of your code with compiler flags -std=c++11 -Werror -Wsign-conversion (live):
Compiler output:
<source>: In function 'int main()':
<source>:6:10: error: unsigned conversion from 'int' to 'std::array<int, 5>::size_type' {aka 'long unsigned int'} changes value from '-2' to '18446744073709551614' [-Werror=sign-conversion]
6 | a.at(-2)=100;
| ^~
<source>:7:13: error: unsigned conversion from 'int' to 'std::array<int, 5>::size_type' {aka 'long unsigned int'} changes value from '-2' to '18446744073709551614' [-Werror=sign-conversion]
7 | cout<<a[-2]<<endl;
| ^~
cc1plus: all warnings being treated as errors
Here's another example that simulates the same thing.
And, in at(), the passed argument is validated against the array size. Here, you will get a runtime exception of type std::out_of_range as described by the documentation:
If pos is not within the range of the container, an exception of type std::out_of_range is thrown.
You may look at its implementation provided by your compiler if you're so inclined to.
And, when you use an invalid index with subscript operator [] e.g. a[-2] where -2 to size_type conversion returns into a wrapped-around value, the result will be an out-of-bounds access and will cause an Undefined Behavior.
Hope this explanation helps!
accessing invalid std::array element with .at will cause exception handler to throw run time error regardless of the case if you compile and run it in the release or debug.
Only time you would not see an error is when you use the member access operators [] as you have done in your example in release mode.
cout<<a[-2]<<endl;
in debug mode you will only get the out of bounds error after compiling and running. In release mode, it's undefined behaviour.
This is also why using .at is recommended instead of directly accessing the elements with [] operator.
This code on array of structures is working but according to my understanding it shoudn't.
Code:
#include<stdio.h>
#include<string.h>
int main()
{
struct virus {
char signature[25];
char status[20];
int size;
} v[2] = {
"Yankee Doodle", "Deadly", 1813,
"Dark Avenger", "Killer", 1795
};
for(int i=0;i<=1;i++) {
printf("%s %s\n", &v[i].signature, &v[i].status);
}
return 0;
}
According to me, when we try to access the members of a structure using the address of an instance of the structure, we must use the -> operator rather than the . operator. I know that we do it only in case a pointer,but as far as I know, array is functionally equivalent to a pointer. Please help me where I am going wrong with my concepts. And pardon my silly question, because I am just a beginner.
It should not according to Operator Precedence. [] has higher precedence than ., and . has higher precedence than &. So &v[i].signature is equivalent to & ( (v[i]).signature). So first the ith item is taken from v, then it's signature member is accessed, which is a character array, and then & is applied which gives the address of the character array.
It works in VS, but this is not standard behavior.
It doesn't really work. You probably expected &v[i].signature to be (&v[i]).signature (which should have been (&v[i])->signature), but it's actually &(v[i].signature), of type char (*)[25]. If the program still produces the expected output, then that's purely by chance.
GCC would have told you this even without -Wall:
$ gcc -c a.c
a.c: In function ‘main’:
a.c:18:9: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘char (*)[25]’ [-Wformat=]
printf("%s %s\n",&v[i].signature,&v[i].status);
^
a.c:18:9: warning: format ‘%s’ expects argument of type ‘char *’, but argument 3 has type ‘char (*)[20]’ [-Wformat=]
It's working because:
&v[i].signature == &v[i] == v->signature
(&v).signature == v->signature
You have taken the address using & and dereferenced using [].
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.
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.