How to initialize a static array in C++ - c++

Given the following very basic function, how would I initialize arr to all random values, and how would I initialize arr to a set of given values, say the numbers 0-11?
void func() {
static int arr[2][2][3];
}
With my limited knowledge of static variables and C++ in general, I think that the static array needs to be initialized in one line, so when the function is called again, it does not get re-initialized. Basically, I want to say:
static int arr[2][2][3] = another_array
But this raises an error that 'another_array' is not an initializer list. I looked up initializer lists but they all included classes and other stuff I didn't understand. Is there any way to initialize this array? Any help is appreciated.

Technically if you try to assign the value of arr in a separate line, it will never be re-initialzied after the first time it was initialized. It will be re-assigned. But based on what you described, I assume that's the behavior you want to prevent.
So to initialized arr in the same line, what you could do is first create a function that will generate the desired number for you, then call that function many times during initializing arr:
int gen_num() {
// some code to return a random number for you...
}
void func() {
// I reduced `arr` to a 2D array, to make the code shorter. Making it a 3D array basically works the same
static int arr[2][3] = {{gen_num(), gen_num(), gen_num()}, {gen_num(), gen_num(), gen_num()}};
}
Note, if you make arr an std::array instead of the C-style array, then you can actually build up an array in a separate function, then just return the new array from that function:
std::array<std::array<int, 3>, 2> create_array()
{
std::array<std::array<int, 3>, 2> result;
// here you can assign each value separately
result[0][0] = 20;
result[2][1] = 10;
// or use for loop freely as needed
for(auto& arr : result)
{
for(auto& value : arr)
{
value = get_num();
}
}
return result;
}
void func() {
// Basically the same as having `int arr[2][3]`
static std::array<std::array<int, 3>, 2> arr = create_array();
}

Related

Why does a two-dimensional array become a one-dimensional array after passing it to a function?(C++

I'm making a simple Snake game. When making a map, my definition of the map is as follows
int map[25][25] = { 0 };
for (int i = 0; i < 25; i++)//Set the boundary to - 2
{
map[0][i] = -2;
map[24][i] = -2;
}
for (int i = 1; i < 25; i++)//Set the boundary to - 2
{
map[i][0] = -2;
map[i][24] = -2;
}
Then I made a function to simulate the motion of the snake。(The first parameter is the class I created: snake,The second is its moving direction. The key is the third parameter, the map array I put in.)
void snake_move(Snake snake1, int direction, int map[][25])
Then I made a call to the function.(The third parameter is the two-dimensional array pointer I passed in)
snake_move(snake1, direction, map);
Then the following figure appears
I found that it was a two-dimensional array before the function call,which is as follows
Why does this happen and how to solve this problem? I look forward to your reply・v・
You cannot pass built-in arrays like this to functions. snake_move(), even though it appears to have an argument that looks like a 2D array, it actually takes a pointer to a 1D array. This:
void func(int map[][25]);
Is actually equivalent to:
void func(int (*map)[25]);
map is a pointer to an array of 25 int elements. When you call that function:
func(map);
The map array "decays" to a pointer that points to its first element.
This is an unfortunate consequence of C++'s compatibility with C.
To avoid issues like this, use std::array (for fixed-size, static allocation of elements), or std::vector (for dynamically allocated elements.)
To get a 2D array, you need to use an array of arrays or a vector of vectors. For an array, that means:
std::array<std::array<int, 25>, 25>
This means "an array containing 25 arrays of 25 int elements.
It's a good idea to make snake_move take a const reference to avoid an unnecessary copy of the whole array. So:
#include <array>
void snake_move(
Snake snake1, int direction,
const std::array<std::array<int, 25>, 25>& map);
// ...
std::array<std::array<int, 25>, 25> map{};
for (int i = 0; i < 25; i++) {
map[0][i] = -2;
map[24][i] = -2;
}
for (int i = 1; i < 25; i++) {
map[i][0] = -2;
map[i][24] = -2;
}
snake_move(snake1, direction, map);
If snake_move() needs to modify the passed array, then remove the const.
To reduce the need to write the type over and over again, you can use an alias (with the using keyword):
using MapType = std::array<std::array<int, 25>, 25>;
void snake_move(Snake snake1, int direction, const MapType& map);
// ...
MapType map{};
// ...
The {} in the map declaration will initialize all values to zero. You can also use:
MapType map = {};
which does the same.
You can actually keep the dimension without using std::array
void snake_move(Snake snake1, int direction, int (&map)[25][25]);
https://godbolt.org/z/EYz7hzjTj
Also note it's not a 1D array (i.e. map[0] is not -2), the debug window does recognize and shows it's a int[25]*, it probably just have some bug that fail to display it in the correct format.
Why does this happen
Because of type decay. In particular, in many contexts (including when appearing as a parameter to a function), an array decays to a pointer to its first element. For example:
The type int [6] decays to int*
The type int *[6] decays to int**.
The type double [10] decays to double*.
The type int [5][6] decays to int (*)[6].
Thus, in you example, the third parameter int map[][25] is actually a pointer to an array of size 25 with elements of type int, ie int (*)[25].
how to solve this problem?
You can use std::array, as shown below:
void snake_move(Snake snake1, int direction,
//----------------------------vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv------->std::array used
std::array<std::array<int, 25>,25> map)
{
}
std::array<std::array<int, 25>,25> map; //sta::array used
If the function snake_move() doesn't change the passed std::array, and to avoid unnecessary copying, you can take the std::array as a reference to const:
void snake_move(Snake snake1, int direction,
const std::array<std::array<int, 25>,25>& map)
//----------------------------^^^^^-----------------------------------^----->lvalue reference to non-const std::array<std::array<int, 25>,25>
{
}

Assigning to std::array element in std::vector of arrays fails

Today, I'm working on understanding some new-to-me features, particularly std::array and std::vector. Individually, these seem to behave as expected, but I'm very puzzled by the behavior illustrated below:
This version works:
printf("Using pointer:\n");
std::vector<std::array<int, 1>*> vap;
vap.push_back(new std::array<int, 1>());
printf("size of vector is %ld\n", vap.size());
printf("before setting value:\n");
printf("value is %d\n", vap.front()->at(0));
std::array<int, 1>* pvals = vap.front();
pvals->at(0) = -1;
printf("after setting value:\n");
printf("value is %d\n", vap.front()->at(0));
This version doesn't update the array:
printf("Without pointer:\n");
std::vector<std::array<int, 1>> va;
std::array<int, 1> arr = {99};
va.push_back(arr);
Inserting the array like this fails too: va.push_back(std::array<int, 1>());
printf("size of vector is %ld\n", va.size());
printf("before setting value:\n");
printf("value is %d\n", va.front().at(0));
std::array<int, 1> vals = va.front();
vals[0] = -1;
printf("after setting value:\n");
printf("value is %d\n", va.front().at(0));
It's likely obvious what I'm trying to do but, in case it helps, I'll write it in prose:
Loosely, I'm creating a vector of arrays of ints. In the first half of the example, I create a vector of pointers to those arrays, and am able to insert a new array, via a pointer, and then modify the element contained in that array. In the second half, I tried to avoid using pointers. That code seems to insert the array successfully but then does not allow me to alter the element within it.
I'm somewhat surprised that there are zero warnings or errors, either at compile or runtime, and I'm guessing that I'm missing something fundamental. What can I try next?
You are working on a copy of the array, you need a reference to the array in the vector
int main() {
vector<array<int, 1>> v;
array<int, 1> a = { 99 };
v.push_back(a);
cout << v[0][0];
auto& ar = v[0];
ar[0] = 42;
cout << v[0][0];
}
gives
99 42
Both your va.push_back(arr); and std::array<int, 1> vals = va.front(); statements are making copies of their source operands. Thus, any changes made to those copies won't affect the source arrays.
In the case of the push_back, this is possibly what you want, so that you can (for example) use the same source variable (arr) – after suitable modifications – to push multiple (different) arrays into your vector.
However, in the second case, that copy is almost certainly not what you want. Instead, you should declare a reference variable and assign that to the element of the vector you want to modify.
In the trivial case that your have shown in your code, the usage would be as follows:
//...
std::array<int, 1>& vals = va.front(); // The "&" declares a reference
vals[0] = -1; // This now modifies the (element of the) array referred to.
But note, you can't reassign a reference variable after it has been declared and initialized as referring to a particular source; thus, a subsequent vals = va.at(2); would not do what you might think (it will, in fact, replace the previously referred-to element with a copy of the RHS array). A notable 'exception' to this is when using such a reference variable in a range-based for loop, like this:
for (auto& vals : va) {
vals[0] = 42; // This will refer to a different "va" element each loop
}
You can, similarly, define such a reference explicitly inside a more traditional for loop (or, in fact, in any loop or block-scope); that will, effectively, create a new reference each time the loop is iterated (actually, this is also what is being done in the above, range-based loop). So, this code will set the first element of each array to the loop's i index:
for (size_t i = 0; i < va.size(); ++i) {
auto& vals = va.at(i);
vals[0] = static_cast<int>(i);
}

Pass Array to Recursive Function While Modifying It C++

I have a recursive function that takes a 2D array as a parameter in C++. The contents of this 2D array are to be unique among all recursive calls of this function.
I would like to modify this array each time before I make the recursive call. How can I do this without modifying the array for both of the 2 recursive calls, just for the current call?
Here is what I am trying to accomplish:
void foo(int a[10][10]) {
// imagine base case above
// modify array, i.e. set a[2][2] to '5'
foo(a);
// modify array i.e. set a[2][2] to '3'
foo(a);
}
I tried the following, which resulted in a compiler error:
void foo(int a[10][10]) {
// imagine base case above
foo(a[2][2] = 5);
foo(a[2][2] = 3);
}
The idea is that I want the array to be independent among recursive calls, for example, I don't want the set a[2][2] = 5 to apply to the next recursive call. I want that array modification to be "reverted", in a sense, before I apply the next modification (change).
This is easy to accomplish if I were just passing an int as an argument. For example, I could do:
void foo(int a) {
// imagine base case above
// increase a by 1
foo(a + 1);
// decrease a by 4
foo(a - 4);
}
You can see here how easy it is to make the modifications without affecting the following recursive call.
My question here is how I can make changes along the same lines with an array.
C-array cannot be copied, std::array can :)
So I would use std::array.
a[2][2] = 5 mutates array, whereas i - 4 doesn't mutate integer i (so nothing to discard in that case, contrary to f(i -= 4)).
there are no operator on array which allows easy customization,
we can create function or lambda for that:
// pass by value
std::array<std::array<int, 10>, 10>
mutated(std::array<std::array<int, 10>, 10> a, int x, int y, int value)
{
a[x][y] = value;
return a;
}
void foo(const std::array<std::array<int, 10>, 10>& a) {
// imagine base case above
// "modify" array, i.e. set a[2][2] to '5'
foo(mutated(a, 2, 2, 5));
// "modify" array i.e. set a[2][2] to '3'
foo(mutated(a, 2, 2, 3));
}
When you call foo(a + 1) in your last example, you are not passing the original integer, but a copy of it, to the function. To achieve something similar with arrays, you would have to create a copy of the entire array, and then modify the copy before passing it to the function.
Example
void foo(int a[10][10]) {
// Create two copies of a
// We cannot simply do 'int b[10][10] = a;', because that would make
// b point to the same memory region as a.
int b[10][10] = {0};
int c[10][10] = {0};
for (int i = 0; i < 10; ++i) {
for (int j = 0; j < 10; ++j) {
b[i][j] = a[i][j];
c[i][j] = a[i][j];
}
}
// Now that we have two copies of a, we can modify them separately
// and pass the mto the functions
// modify array, i.e. set [2][2] to '5'
b[2][2] = 5;
foo(b);
// modify array i.e. set [2][2] to '3'
c[2][2] = 3;
foo(c);
This function would eat memory fast, since it leads to infinite recursion and creates two new 100-element arrays for each function call. The manual copying of a could also be done using memcpy, but I wrote it out as a nested for loop for clarity.

how can I have a function with a 2D array as an argument whereas the array has a parameter/dimension that I want to change?

(I'm a student and this is my first time posting so go easy on me.)
I want to create a function that takes a 2D array as an argument and in that array, I'd like to have a variable that I want to modify later in the code. This is the closest thing to an example of what I want:
int size; //the variable I want to change later
void function(int[][size]);
int main(){
cin >> size;
int array[size][size]; //the array I'm using with the variable as a parameter
function(array)
}
void function(int array[][size]){
//Do thing....
}
The code above does give me an error (array bound is not an integer constant) so if I make the variable a constant it will compile as seen here:
const int size = 10;
void function(int[][size]);
int main(){
int array[size][size];
function(array)
}
void function(int array[][size]){
//Do thing....
}
This does compile like I said, but now I can't modify the variable and need to declare its value in the code beforehand. I assume that the variable needs to be global so that I can use it in the function, and with that said, I can't get pointers to work either most likely because it's a global variable and not a local one. Here's an example of something I tried, but got an error (invalid conversion from ‘const int*’ to ‘int*’):
const int size = 10;
void function(int[][size]);
int main(){
int *other = &size;
*other = 5;
}
Any help would be appreciated, thanks.
Plain ol' arrays aren't resizeable in C++. Even more frustrating, their size has to be a constant - you can't make the size a variable that gets set at runtime. Ever more frustrating, the size you put in an array that's a function parameter is a constraint, and it's not even enforced. It's just decor.
As it was hinted in the comments, std::vector<TYPE> is the go-to "resizeable array" in C++. You can create a vector like this:
#include <vector>
int main() {
std::vector<int> my_int_array;
}
And you can resize it like this:
int new_size = 42;
my_int_array.resize(new_size);
And you can pass it to a function by reference(see the &) so that changes to myint_array inside the function affect it outside the function.
void my_awesome_function(std::vector<int>& int_array);
my_awesome_function(my_int_array);
So let's say you have a 2D matrix, implemented as a vector of vectors:
std::vector<std::vector<int>> matrix = { { 1,2,3 }, { 4,5,6 } }
If you want to change the number of columns, you have to resize each row array:
int new_column_count = 10;
for (auto& row : matrix) {
row.resize(new_column_count );
}
You can pass around matrix by reference (e.g. std::vector<std::vector<int>>&) and resize it when you need to.

How can I initialize an array globally in C or C++?

I'm trying to do this:
for(int k=0; k<context.size(); k++)
{
cc_no_issue[k]=0;
}
Can someone tell me how I can do that globally? Whenever I try I get these errors:
expected unqualified-id before "for"
k does not define a type
k does not define a type
This will do:
long cc_no_issue[100]={0};
And this is the proper initialization.
Note: this will initialize all the contents to 0.
This sentence:
long cc_no_issue[100]={1,2};
will set cc_no_issue[0] to 1, cc_no_issue[1] to 2, and the rest to 0. You could see the link above for more information.
If you have a global array of a basic type,
int some_array[1000];
It will automatically be initialized to zero. You do not have to initialize it. If you do need to run initialization code, you can do a hack like the following (in C++):
struct my_array_initializer {
my_array_initializer() {
// Initialize the global array here
}
};
my_array_initializer dummy_variable;
If you are on GCC (or Clang), you can execute code before main with the constructor attribute:
__attribute__((constructor))
void initialize_array()
{
// Initialize the global array here
}
All global variables (variables at file scope) are by default initialized to zero since they have static storage duration (C99 6.7.8.10). So strictly speaking, you needn't initialize them to zero, the C standard guarantees that they are zero by default.
It is good programming practice to initialize them explicitly however, as mentioned in the answer by Ziyao Wei.
No, you can't have code outside of functions.
You can put it inside some function and call that from the start of main.
One way is to add a global function that:
Checks if the array is initialized
Initializes the array if it wasn't initialized
Returns the array
Example Code:
int* get_cc_no_issue()
{
if(!kIsMyArrayInitialized)
{
/* todo: somehow get "context" globally... */
for(int k = 0; k < context.size(); k++)
{
cc_no_issue[k] = 0;
}
kIsMyArrayInitialized = true;
}
return cc_no_issue;
}
This is most useful if you want non-zero initialization.
For zero-initialization, see this answer to another question:
Is global memory initialized in C++?
You can put the array in the constructor of a global object.
int cc_no_issue[256];
struct Init {
Init(int a, unsigned int size)
{
memset(a, 0, size);
}
};
Init arr(cc_no_issue, sizeof(cc_no_issue));
As #Bo Persson, do it in a function instead. But, there is already an algorithm that does it for you in C++. No need to write a hand written loop.
std::fill(cc_no_issue, cc_no_issue+context.size(); 0) ;
More info on std::fill
Response to your comment:
To increment every element, you can make use of std::for_each passing a function object as the third argument.
#include <iostream>
#include <algorithm>
using namespace std;
void incrementHelper( int& a ){
++a;
}
int main(){
int ar[] = { 1,2,3,4,5 };
for_each(ar, ar+5, incrementHelper );
for( int i=0; i<sizeof(ar)/sizeof(ar[0]); ++i ){
cout << ar[i] << endl;
}
return 0;
}
Ouput:
2
3
4
5
6
for_each(ar, ar+5, incrementHelper );
For each element in the array, the algorithm is going to call the function, incrementHelper. In C terminology,to say, it serves as a call back for each element in the array. Now the call back function, receives the passed element by reference. So, modifying the reference will modify the referrent also. See the online demo.
You need to decide on the language. The machanisms for this are different in C and C++. Basically C has no method of running code before your main function starts, so you cannot do complex initialisation of an array in C. In C++ you do have some options, one is to stop using a bare array (which is a C construct anyway) and instead wrap your array inside a class, and do the initialisation inside the class constructor.
CC cc_no_issue;
class CC
{
public:
CC()
{
// initialisation of array goes here
}
private:
int array[100];
};
Another way it to use a vector, and write a function to initialise the vector.
std::vector<int> cc_no_issue = init_vector();
std::vector<int> init_vector()
{
std::vector<int> tmp;
// initialisation of tmp goes here
return tmp;
}