I'm reading Stroustrup's A Tour of C++. On page 9, he states:
"The size of an array must be a constant expression."
Yet later, on pg. 16, he uses the following code sample:
void vector vector_init(Vector& v, int s)
{
v.elem = new double[s]; // Allocate an array of s doubles
v.sz = s;
}
Here s is not a constant expression, so how is initializing v.elem to new double[s] legal?
There is a differentiation between allocated arrays (i.e. those created with a new[] expression, like new double[s]), whose lifetimes must be managed by the code (via delete[]) and declared arrays, whose lifetimes are managed by their scope alone:
int* p = new int[s]; // allocated array, p has type int*
int q[10]; // declared array, q has type int[10]
std::vector<int> u; // has member allocated array
std::array<int, 5> v; // has member declared array
The differentiation is not based on stack/heap. A declared array can be heap allocated (e.g. new array<int,5>) or not on the stack (e.g. static double x[100];)
With an allocated array, the size does not have to be a constant expression. The size will simply be encoded into the block of memory yielded by the allocator somehow (for instance the four leading bytes before the actual data starts) so that the corresponding delete[] knows how many elements to delete.
With a declared array (or non-allocated array, no new/malloc/etc.), the size must† be coded into the type, so that the destructor knows what to do. The only allowed, standard array declaration is:
T D[constant-expression_opt];
(where D is a declarator that could be a name or another array declaration, etc.) Declared arrays are not limited to the stack. Note that, for added confusion, the constant-expression is optional.
Arrays offer many sources of confusion in C++. Allocated and declared arrays have different size rules, different management practices, but you can assign a T* to either and they're equivalently indexed. An allocated array is a pointer (that's all you get), but a declared array decays to a pointer (but is an array!).
†Note that there is a concept of a Variable Length Array (VLA). gcc, for instance, supports them as an extension, but they are non-standard C++. It gets periodically proposed though, and you can see this question for more information about them.
When creating an array whose memory is managed by compiler, its size must be (compile time) constant. For ex:
int a[5];
const int sz = 7;
int b[sz] = {0};
(Some languages for ex: C (C99 onwards) support dynamic array size)
If you want a dynamically sized array (Example snippet given by you), you need to allocate memory for it by yourself you'll also need to free it with delete when you're done. Size of such arrays can be non-const also. Moreover you need to manage the memory by yourself, allocation may fail and operators (for example sizeof) would operate on the pointer rather than array.
In modern C++ (C++11 onwards), even stl container std::array must have a constant size.
The quote
The size of an array must be a constant expression.
is talking about an array declaration, such as
double a[EXPR];
where EXPR must indeed be a constant or constexpr (C has variable-length arrays, but they're not part of standard C++).
The expression you mention as a counter-example,
new double[s]
is not an array, despite the []. It is a new-expression, and yields a pointer, not an array. You didn't show the definition of v.elem, but I can tell it's a pointer-to-double.
Note from the linked discussion on new expressions that
If type is an array type, all dimensions other than the first must be specified as positive {something like an integral constant - detail elided}.
So the type referred to above is double[s], which is explicitly allowed.
Admittedly the difference between an array, and the array type passed to a new expression is a little subtle, but you can't conflate them just because of the [], any more than you can claim that
map["key"]
violates something by declaring an array with length "key".
Related
I want to dynamically allocate known size of memory (just memory, not bothering about type) and fill it with exactly the same amount of data but any type (I'm only sure it will be primitive type). Ofc later I'm going to free it.
Is it ok? :
auto dest = new int8_t[n];
std::memcpy(dest, src, n);
delete[] dest;
src is ptr to an array of size n (Bytes).
I've ofc chosen int8_t becuase it's the clearest way to allocate certain amount of memory.
In fact the code above isn't exaclt what it will be. delete[] will be called on pointer of type which actually it points to.
So for example if src was an array of floats (forget about last line of above code):
float * ptr = dest;
delete[] ptr;
So again. Will it be ok?
It is ok, but only if you use a char array or an unsigned char array, because of the special alignment guarantees for these types:
5.3.4 New
11 When a new-expression calls an allocation function and that
allocation has not been extended, the new- expression passes the
amount of space requested to the allocation function as the first
argument of type std::size_t. That argument shall be no less than the
size of the object being created; it may be greater than the size of
the object being created only if the object is an array. For arrays of
char and unsigned char, the difference between the result of the
new-expression and the address returned by the allocation function
shall be an integral multiple of the strictest fundamental alignment
requirement (3.11) of any object type whose size is no greater than
the size of the array being created. [ Note: Because allocation
functions are assumed to return pointers to storage that is
appropriately aligned for objects of any type with fundamental
alignment, this constraint on array allocation overhead permits the
common idiom of allocating character arrays into which objects of
other types will later be placed. — end note ]
emphasis by me.
Another requirement is that you only use primitive types or PODs, because you don't call a constructor, but the (trivial) destructor (through delete).
According to the specification of the new expression execution of new int8_t[n] calls operator new[](sizeof(int8_t)*n + overhead).
Important note about overhead:
Many implementations use the array overhead to store the number of objects in the array which is used by the delete[] expression to call the correct number of destructors.
In its turn, operator new[](std::size_t count) returns the void* pointer to the allocated memory block. This pointer further should be used in delete [] expression.
You have simple types with no destructors, so delete[] only need to deallocate memory block. It can do it safely with both int8_t* dest or float* ptr.
UPDATE:
It's better to change array type from int8_t to char or unsigned char. For arrays with such types C++ standard garantees that you will get the proper aligment for further data placement. Thanks #alain. More details together with standard quote can be found in his answer.
Maybe will be better to use the std vector? No need to manually free memory..
std::vector<uint8_t> vdest(n);
uint8_t * dest = vdest.data();
std::memcpy(dest, src, n);
float * ptr = reinterpret_cast<float*>(dest);
...
EDIT:
As #alain pointed out: "There is no guarantee that an array of uint8_t is properly aligned for the new type"
So if you want use this approach It will be a good idea to look this question: Is it good practice to use std::vector as a simple buffer?
I understand this is common scenario in Real Time programming. To make sure that each structure is aligned to uint8_t/unsigned char(any custom defined Byte size datatype), make sure each of you structure (apart from primitive data Structure) use the concept #pragma pack.
Consider the following code:
#include <iostream>
int main()
{
int index;
std::cin >> index;
int* dynArray = new int[5];
dynArray[index] = 1;
int stackArray[5];
stackArray[index] = 1;
}
I know for sure that dynArray is a simple int* pointer and it takes additional sizeof(int*) bytes on stack.
Question 1: Is an additional pointer variable also created on stack for stackArray? If so, does that always happen? If not, then how does stackArray[index] = 1; work without knowing array base, i.e. how does the compiler decide what value to add to index for calculating address?
Question 2: Is there any difference between C/C++?
I want an answer for these 2 environments:
GCC/Linux, x86
Visual Stuidio 2013, x86
dynarray is a variable. Its type is int *, and it has automatic storage duration ("on the stack"). Its value is a pointer to some element somewhere else. The fact that it happens to point to an element of some array somewhere else is to some extent incidental.
stackArray is a variable. Its type is int[5] and it has automatic storage duration. Its value is an array of five integers. When you use the name of the variable in an expression like stackArray[index], that expression denotes the corresponding array element (which is a subobject of the array object).
Note that in your example, the array of which dynarray[index] is an element is not itself a variable, nor is the element itself. You can only access the element objects because you have a pointer to them. (And in fact you cannot express the array object itself at all, since its type is know knowable at compile time. Dynamic arrays are the one part of C++ where you have truly dynamic typing.)
stackArray is an array, not a pointer. Its type is int[5], i.e., an array of 5 integers. The compiler knows the type of elements of the array which is int. stackArray[index] is evaluated to *(stackArray + index). Here, the array stackArray evaluates to a pointer to its first element.
C and C++ are same in terms of an array which has automatic storage allocation.
reading this question: What is the difference between int* x[n][m] and int (*x) [n][m]?
the correct reply stated this:
int arr[2][3];
int (*p1)[2][3] = &arr; // arr decays to int(*)[3], so we need to take the address
int (*p2)[2][3] = new int[42][2][3]; // allocate 42 arrays dynamically
the comment in that last line says it allocates 42 new arrays.
how does that work? can someone explain that to me?
thanks!
i would reply on that question, but i can't, so new question it is (:
new int[3] allocates 3 integers. Now new int[2][3] allocates 2 arrays, each of length 3 integers. Extending this further, new int[42][2][2] allocates 42 arrays, each of length 2 arrays, each of which in turn is, of length 2 integers. Declarations are really a recursive idea.
Allright, class time.
C++ Multidementional arrays vs. Jagged array
A multidemenional array (in C++) is a block of contiguous memory. It allocates the full number of elements, all in a line, and then access them by combining the index accessors. In essense. If I have an array defined by int x[2][3], it essentially turns x[i][j] into (&x[0][0]+i*3)[j].
sizoef(int[2][3])==24 (4bytes/int*2*3)
This type of array is often called a "static" array, because the size of the array must be allocated at compile time.
Note that the first size is irrelevent to this lookup. This makes it so that, when REFERENCING this type of array, we can exclude the smallest size from the type. This makes is so that both the functions below are valid and can work on the array x declared above:
int foo(int y[2][3]){ return y[1][1];}
int bar(int y[][3]){ return y[1][1];}
Note in this context, sizeof(y)==sizeof(void*), but that is a different problem all together.
The typeing convention for static arrays behaves differently than your used to. Part of the type information comes AFTER the variable declaration. This actually persists in typedefs as well:
typedef int a[4][20];
typedef int b[][20];
If you wanted to take the address of such a value type, then what you need to declare the pointer to this array type. That can be done with:
int (*xptr)[2][3] = &x;
The int (*var)[...] says that var is a pointer to a int[2][3].
When people say C++ arrays are pointers, they are NOT refering to this type of array: the array is a little more compilicated than a pointer here, though we could flatten it into a 1D array.
A jagged array (or dynamic array) is a single block of contiguose memory that is allocated in a linear fashion. This type of array is often called dynamic because the size does NOT need to be known at compile time.
Dynamic arrays are allocated with new or malloc (though you should only use new in C++). These types of array are strictly pointers. When I say int* a=new int[4], I allocate 4 integers: thats it.
We achieve mutlidementionality here by crating jagged arrays, which are arrays of pointers. So for example:
int** a = new int*[2];
for (int i = 0; i < 2; i++) { a[i]=new int[3];}
What your code does
int arr[2][3];//
int (*p1)[2][3] = &arr; // arr decays to int(*)[3], so we need to take the address
int (*p2)[2][3] = new int[42][2][3]; // allocate a dynamic array (42 elements long) of int[2][3] (sizeof(int[2][3])==24, so this allocates 42*24 bytes!)
It essentially allocates 43 int[2][3] in a row. So it actually comes up with all contiguous memory (though why it needs to be dynamic and not static is beyond me).
Personally, my rule is multidementional arrays "confusing as hell" and only use them in a local context now, but to each their own.
In this excellent answer by AndreyT, he explains that in C, when a function needs an array whose dimension is known at compile-time, it's a major technique-level error to declare
void process_array(int *ptr, size_t plen);
instead of
void process_array(int (*arr_ptr)[10]);
Furthermore, he opines that many programmers are oblivious to the second option and know only about the first. One of the reasons, he writes, for this behaviour is when an array needs to be dynamically allocated and passed to the second version, programmers don't know how to do it; they're so accustomed to int *p = malloc(sizeof(*p) * 10) which returns an int *. In C, the way to do it is, as he shows
int (*arr_ptr) [10] = malloc(sizeof(*arr_ptr));
This got me thinking on how one would do the same in C++. I know we've std::array, std::vector, etc. but I'm interested in understanding new's usage. So I tried doing this:
typedef int Array10[10];
Array10 *p = new Array10; // error: cannot convert ‘int*’ to ‘int (*)[10]’
When I change p's type to int* the compiler (GCC 4.8.1) is happy. I looked up C++11 standard (draft n3337, §5.3.4/5) to understand this further, it says:
When the allocated object is an array (that is, the noptr-new-declarator syntax is used or the new-type-id or type-id denotes an array type), the new-expression yields a pointer to the initial element (if any) of the array. [Note: both new int and new int[10] have type int* and the type of new int[i][10] is int (*)[10] — end note]
I understand that new int [10] is in action; what I get back should be freed using delete [] p and not delete p. However what I need seems to be the latter, where the allocation is not an array of integers but an array itself, as a single/whole object.
Is there a way to do it? Or my trying to do this in itself shows a misunderstanding of the C++ type system? Or it is right but is simply not allowed by the standard?
Aside: When a function takes an array whose size is fixed, as in option 2, IMHO the best thing to do for the calling function is to declare an automatic array instead of resorting to dynamically allocating one and worrying about clean-up.
Yes, in fact it is suggested in the standard text you quoted:
int (*arr_ptr)[10] = new int[1][10];
The pointer returned by the new-expression has type int (*)[10], i.e. pointer to array of 10 ints. You need delete[] to delete it.
If I have understood correctly what you want then you can use std::array. For example
{
typedef std::array<int, 10> Array10;
Array10 *p = new Array10;
delete p;
}
I have a few array-related questions. I've studied that array-size must be constant on declaration/compiler must know its value. But using the GNU GCC compiler (C++11 standard filter) and I am able to perfectly compile and run a program using a variable as array size, when declaring said array dynamically (using new)
int num;
cout << "How big an array? ";
cin >> num;
int *arr = new int [num];
Ques1) Is this considered standard? My profs are contradictory.
Ques2) If it is standard, in that case, is it possible to extend the size of the array (or any array) after creation?
Ques3) Again, if this expression is standard, then is it possible to use it within a function - eg. using a function to create such an array? (if so, how?)
(PS: Hi, I'm new here and also still a novice in C++)
Ques1) Is this considered standard? My profs are contradictory.
Yes, this is completely valid. Note that you need to explicitly delete the memory pointed by arr using operator delete[]. It is much better to use a std::vector<int> which will perform the memory management for you.
You might be mistaken with variable length arrays(VLAs), which are not allowed in C++:
// same num as the one in your example
int arr[num]; // ERROR
This is valid in C but invalid in C++(C++14 will include VLAs, although they will have some differences with C VLAs).
Ques2) If it is standard, in that case, is it possible to extend the
size of the array (or any array) after creation?
No, you can't extend it. You can allocate a bigger array, copy the elements and delete the previous one. Again, this is done automatically if you're using std::vector<int>.
Ques3) Again, if this expression is standard, then is it possible to
use it within a function - eg. using a function to create such an
array? (if so, how?)
Yes, of course:
int *allocate(size_t size) {
return new int[size];
}
But again use std::vector:
int num;
cout << "How big an array? ";
cin >> num;
std::vector<int> arr(num); // num elements, all of them initialized to 0
// insert 42 at the end. reallocations(if any) are done automatically
arr.push_back(42);
I've studied that array-size must be constant on declaration/compiler must know its value.
Well, that's true, but only for static or automatic arrays. You are allocating a dynamic array on the heap, which is different.
Static array
An array declared at global scope must have a constant size.
int arr[5];
Automatic array
An array allocated automatically within a function must have constant size (with exception, see below).
void f() {
int arr[5];
}
Dynamic array
A dynamic array allocated on the heap with new can have any size, constant or variable.
new int[5];
new int[n * 4];
GCC extension
The exception is that GCC allows one to use a variable to declare the size of an automatic array:
void f(int n) {
int arr[n];
}
However, this usage is not standard.
Question 1 - operator 'new' is used to make dynamic allocation, I mean, when you don't know previously what is the size of the array, so, there is no problem, you can do it! I think your profs are confusing with C sintax, where new neither exists and is not allowed to make things like: int p[n]; for instance.
Question 2 - No, it is not possible increase the size of an array created using operator new. You have to alocate another array and copy the data. You can consider use vector in order to do it easily.
Question 3 - I don't see why to do it, but it is possible..
int* createarray(int size)
{
return new int[size];
}
int main()
{
int *p = createarray(10);
}
Q1:Is this considered standard?
Given the definition int n = 42, new float[n][5]
is well-formed (because n is the expression of a
noptr-new-declarator), but new float[5][n] is ill-formed (because n is
not a constant expression).
--5.3.4.6,N3242
If the allocated type is an array type, the allocation function’s
name is operator new[] and the deallocation function’s name is
operator delete[].
--5.3.4.8,N3242
new T[5] results in a call of operator new[](sizeof(T)*5+x)
Here, x and y are non-negative unspecified values representing array allocation overhead;
--5.3.4.12,N3242
Q2:If it is standard, in that case, is it possible to extend the size of the array (or any array) after creation?
Partially no, or not recommended.
when the allocation function returns a value other than null, it must be a pointer to a block of storage
in which space for the object has been reserved. Mostly allocation happened in heap, and there may not have more contiguous memory left which is important for array.
If you have to do this and have a memory poll, use placement new operator you can do this partially, but what you do now is what the designer of allocator do, and have risk ruin the inner memory storage.
Q3: using a function to create such an array? (if so, how?)
Entities created by a new-expression have dynamic storage duration
(3.7.4). [Note: the lifetime of such an entity is not necessarily
restricted to the scope in which it is created. — end note ]
--5.3.4.1,N3242
The rest of things are how to design such function to meet your need, even use template.
1 template<typename T>T* foo(std::size_t size){
2 return new T[size] ;
3 }
As a complement to other answers :
Vectors
(Agreed with vector for dynamic resize) :
std::vector<int> v;
v.push_back(1);
v.push_back(1);
v.resize(v.size()+10, 5); // Greater resized
v.resize(v.size()-1); // Lower resized
If the new size is greater than the old one, additional elements will be initialized to 5 (or 0 in case of int, if the second parameter is not used) and ancient elements remain unchanged.
If the new size is lower than the old one, it is truncated.
Arrays
Side note : (about stack and heap allocation)
The way an array is handled can lead to significantly different results (See this very interesting discussion):
// Create 500 bytes on the heap
char *pBuffer = new char[500]; // or malloc in C
// Create 500 bytes on the stack
char buffer[500];