Related
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>
{
}
How come I can do this:
char a[] = {1, 2};
char b[] = {3, 4, 5};
const char *r[] = {
a, b
};
But I can't do it this way:
const char *r[] = {
{1,2}, {3,4,5}
};
Is there any shortcut for initializing an array of pointers to arrays of different length?
There is no easy solution to achieve the syntax you're looking for: these pointers have to point at some arrays, and these arrays must have an adequate lifetime.
You probably want them to be automatic, which implies declaring them as local variables the way you did.
You could achieve sort of what you want with dynamic allocation, i.e:
const char *r[] = {
new char[]{1,2}, new char[]{3,4,5}
};
... in which case the arrays will have sufficient lifetime, but then you have the burden of delete[]ing them at the right time. Not really a solution.
The first form works because, when used like this, the name of each array is converted to a pointer (to its first element). Since the result of the conversion is a char * it can be stored in an array of char *.
The second form doesn't work since {1,2} and {3,4,5} are (initialisers for) arrays, and arrays are not pointers. An unnamed array (which these are) cannot be implicitly be converted to a pointer.
The first form IS the only shortcut to initialise an array of pointers, so each pointer points to the first element of an array.
You would be normally better off using standard containers (e.g. a std::vector<std::string>) and avoid the complexity of pointers (and conversions of array names to pointers) entirely.
this code doesn't work,
const char r[][] = {{1,2}, {3,4,5}};
fails with :
x86-64 gcc 5.1!!error: declaration of 'r' as multidimensional array
must have bounds for all dimensions except the first
fix it like so:
const char r[][3] = {{1,2}, {3,4,5}};
side note: it's a bad practice to use C-like arrays in c++ use std::array if you know your values at compile time.
EDIT:
you can do this ugly hack(it compiles, but I don't know how many rockets it will launch).
const char *r[] = { (const char[]){1,2}, (const char[]){1,2,3}};
you can't write:
const char *r[] = { {1,2}, {3,4,5} };
because you cannot write:
r[0] = 1, 2, 3; // but you can r[0] = "1, 2, 3";
to initialize your array of pointers to char you can:
const char *r[] = {
{"1,2"}, {"3,4,5"}};
so r is a an array of pointers to char.
I have a variable in class declared as compile-time constant with known size:
static const int array[5][5]; // constants initlialised in another place
And a function that returns it virtually:
virtual const int** getArray() { return array; }
How to get this array with this method, and cast it to fixed-size array, not pointers-based, so I can use it like cout << data[2][2] ?
Sample that dosn't compile:
const int[5][5] data = object->getArray();
cout << data[2][2];
Sample that compiles but crashes application:
const int** data = object->getArray();
cout << data[2][2];
Note: one solution is to create typedef arr[5] and declare methods with arr* but i don't want to create a typedef for each compile-time size wich I use like typedef arr5[5]; typedef arr10[10] etc. I'm looking for something more like:
const int(*)[5] data = object->getArray(); // won't compile, example only
Let's assume that compile-time constant array is loaded with dynamic DLL and is already in memory, is it possible to use this data as array without allocating new memory and populating it from compile-time constants?
virtual const int** getArray() { return array; }
That won't work, and shouldn't compile; you're trying to return a pointer to some pointers, but there are no pointers to point to, only an array of arrays.
You can return a pointer to those arrays, preserving the type:
virtual const int (*getArray())[5] { return array; }
const int (*data)[5] = getArray();
cout << data[2][2];
In C++11, it might be nicer to wrap the arrays up in std::array, and return a reference to that, to avoid some of the nasty syntax.
Use std::array:
#include <array>
static const std::array<std::array<int, 5>, 5> myarray; // constants initlialised in another place
virtual const std::array<std::array<int, 5>, 5>& getArray() const
{
return myarray;
}
If you do not have C++11 support, you can use std::tr1::array or boost::array (or roll out your own fixed size array type.)
that is not possible. You lose all size information when an array is passed to or returned from a function. The array decays into a pointer so to speak
It's simple enough, you are almost there
const int(*)[5] data = reinterpret_cast<int(*)[5]>(object->getArray());
One of the interview questions asked me to "write the prototype for a C function that takes an array of exactly 16 integers" and I was wondering what it could be? Maybe a function declaration like this:
void foo(int a[], int len);
Or something else?
And what about if the language was C++ instead?
In C, this requires a pointer to an array of 16 integers:
void special_case(int (*array)[16]);
It would be called with:
int array[16];
special_case(&array);
In C++, you can use a reference to an array, too, as shown in Nawaz's answer. (The question asks for C in the title, and originally only mentioned C++ in the tags.)
Any version that uses some variant of:
void alternative(int array[16]);
ends up being equivalent to:
void alternative(int *array);
which will accept any size of array, in practice.
The question is asked - does special_case() really prevent a different size of array from being passed. The answer is 'Yes'.
void special_case(int (*array)[16]);
void anon(void)
{
int array16[16];
int array18[18];
special_case(&array16);
special_case(&array18);
}
The compiler (GCC 4.5.2 on MacOS X 10.6.6, as it happens) complains (warns):
$ gcc -c xx.c
xx.c: In function ‘anon’:
xx.c:9:5: warning: passing argument 1 of ‘special_case’ from incompatible pointer type
xx.c:1:6: note: expected ‘int (*)[16]’ but argument is of type ‘int (*)[18]’
$
Change to GCC 4.2.1 - as provided by Apple - and the warning is:
$ /usr/bin/gcc -c xx.c
xx.c: In function ‘anon’:
xx.c:9: warning: passing argument 1 of ‘special_case’ from incompatible pointer type
$
The warning in 4.5.2 is better, but the substance is the same.
There are several ways to declare array-parameters of fixed size:
void foo(int values[16]);
accepts any pointer-to-int, but the array-size serves as documentation
void foo(int (*values)[16]);
accepts a pointer to an array with exactly 16 elements
void foo(int values[static 16]);
accepts a pointer to the first element of an array with at least 16 elements
struct bar { int values[16]; };
void foo(struct bar bar);
accepts a structure boxing an array with exactly 16 elements, passing them by value.
& is necessary in C++:
void foo(int (&a)[16]); // & is necessary. (in C++)
Note : & is necessary, otherwise you can pass array of any size!
For C:
void foo(int (*a)[16]) //one way
{
}
typedef int (*IntArr16)[16]; //other way
void bar(IntArr16 a)
{
}
int main(void)
{
int a[16];
foo(&a); //call like this - otherwise you'll get warning!
bar(&a); //call like this - otherwise you'll get warning!
return 0;
}
Demo : http://www.ideone.com/fWva6
I think the simplest way to be typesafe would be to declare a struct that holds the array, and pass that:
struct Array16 {
int elt[16];
};
void Foo(struct Array16* matrix);
You already got some answers for C, and an answer for C++, but there's another way to do it in C++.
As Nawaz said, to pass an array of N size, you can do this in C++:
const size_t N = 16; // For your question.
void foo(int (&arr)[N]) {
// Do something with arr.
}
However, as of C++11, you can also use the std::array container, which can be passed with more natural syntax (assuming some familiarity with template syntax).
#include <array>
const size_t N = 16;
void bar(std::array<int, N> arr) {
// Do something with arr.
}
As a container, std::array allows mostly the same functionality as a normal C-style array, while also adding additional functionality.
std::array<int, 5> arr1 = { 1, 2, 3, 4, 5 };
int arr2[5] = { 1, 2, 3, 4, 5 };
// Operator[]:
for (int i = 0; i < 5; i++) {
assert(arr1[i] == arr2[i]);
}
// Fill:
arr1.fill(0);
for (int i = 0; i < 5; i++) {
arr2[i] = 0;
}
// Check size:
size_t arr1Size = arr1.size();
size_t arr2Size = sizeof(arr2) / sizeof(arr2[0]);
// Foreach (C++11 syntax):
for (int &i : arr1) {
// Use i.
}
for (int &i : arr2) {
// Use i.
}
However, to my knowledge (which is admittedly limited at the time), pointer arithmetic isn't safe with std::array unless you use the member function data() to obtain the actual array's address first. This is both to prevent future modifications to the std::array class from breaking your code, and because some STL implementations may store additional data in addition to the actual array.
Note that this would be most useful for new code, or if you convert your pre-existing code to use std::arrays instead of C-style arrays. As std::arrays are aggregate types, they lack custom constructors, and thus you can't directly switch from C-style array to std::array (short of using a cast, but that's ugly and can potentially cause problems in the future). To convert them, you would instead need to use something like this:
#include <array>
#include <algorithm>
const size_t N = 16;
std::array<int, N> cArrayConverter(int (&arr)[N]) {
std::array<int, N> ret;
std::copy(std::begin(arr), std::end(arr), std::begin(ret));
return ret;
}
Therefore, if your code uses C-style arrays and it would be infeasible to convert it to use std::arrays instead, you would be better off sticking with C-style arrays.
(Note: I specified sizes as N so you can more easily reuse the code wherever you need it.)
Edit: There's a few things I forgot to mention:
1) The majority of the C++ standard library functions designed for operating on containers are implementation-agnostic; instead of being designed for specific containers, they operate on ranges, using iterators. (This also means that they work for std::basic_string and instantiations thereof, such as std::string.) For example, std::copy has the following prototype:
template <class InputIterator, class OutputIterator>
OutputIterator copy(InputIterator first, InputIterator last,
OutputIterator result);
// first is the beginning of the first range.
// last is the end of the first range.
// result is the beginning of the second range.
While this may look imposing, you generally don't need to specify the template parameters, and can just let the compiler handle that for you.
std::array<int, 5> arr1 = { 1, 2, 3, 4, 5 };
std::array<int, 5> arr2 = { 6, 7, 8, 9, 0 };
std::string str1 = ".dlrow ,olleH";
std::string str2 = "Overwrite me!";
std::copy(arr1.begin(), arr1.end(), arr2.begin());
// arr2 now stores { 1, 2, 3, 4, 5 }.
std::copy(str1.begin(), str1.end(), str2.begin());
// str2 now stores ".dlrow ,olleH".
// Not really necessary for full string copying, due to std::string.operator=(), but possible nonetheless.
Due to relying on iterators, these functions are also compatible with C-style arrays (as iterators are a generalisation of pointers, all pointers are by definition iterators (but not all iterators are necessarily pointers)). This can be useful when working with legacy code, as it means you have full access to the range functions in the standard library.
int arr1[5] = { 4, 3, 2, 1, 0 };
std::array<int, 5> arr2;
std::copy(std::begin(arr1), std::end(arr1), std::begin(arr2));
You may have noticed from this example and the last that std::array.begin() and std::begin() can be used interchangeably with std::array. This is because std::begin() and std::end() are implemented such that for any container, they have the same return type, and return the same value, as calling the begin() and end() member functions of an instance of that container.
// Prototype:
template <class Container>
auto begin (Container& cont) -> decltype (cont.begin());
// Examples:
std::array<int, 5> arr;
std::vector<char> vec;
std::begin(arr) == arr.begin();
std::end(arr) == arr.end();
std::begin(vec) == vec.begin();
std::end(vec) == vec.end();
// And so on...
C-style arrays have no member functions, necessitating the use of std::begin() and std::end() for them. In this case, the two functions are overloaded to provide applicable pointers, depending on the type of the array.
// Prototype:
template <class T, size_t N>
T* begin (T(&arr)[N]);
// Examples:
int arr[5];
std::begin(arr) == &arr[0];
std::end(arr) == &arr[4];
As a general rule of thumb, if you're unsure about whether or not any particular code segment will have to use C-style arrays, it's safer to use std::begin() and std::end().
[Note that while I used std::copy() as an example, the use of ranges and iterators is very common in the standard library. Most, if not all, functions designed to operate on containers (or more specifically, any implementation of the Container concept, such as std::array, std::vector, and std::string) use ranges, making them compatible with any current and future containers, as well as with C-style arrays. There may be exceptions to this widespread compatibility that I'm not aware of, however.]
2) When passing a std::array by value, there can be considerable overhead, depending on the size of the array. As such, it's usually better to pass it by reference, or use iterators (like the standard library).
// Pass by reference.
const size_t N = 16;
void foo(std::array<int, N>& arr);
3) All of these examples assume that all arrays in your code will be the same size, as specified by the constant N. To make more your code more implementation-independent, you can either use ranges & iterators yourself, or if you want to keep your code focused on arrays, use templated functions. [Building on this answer to another question.]
template<size_t SZ> void foo(std::array<int, SZ>& arr);
...
std::array<int, 5> arr1;
std::array<int, 10> arr2;
foo(arr1); // Calls foo<5>(arr1).
foo(arr2); // Calls foo<10>(arr2).
If doing this, you can even go so far as to template the array's member type as well, provided your code can operate on types other than int.
template<typename T, size_t SZ>
void foo(std::array<T, SZ>& arr);
...
std::array<int, 5> arr1;
std::array<float, 7> arr2;
foo(arr1); // Calls foo<int, 5>(arr1).
foo(arr2); // Calls foo<float, 7>(arr2).
For an example of this in action, see here.
If anyone sees any mistakes I may have missed, feel free to point them out for me to fix, or fix them yourself. I think I caught them all, but I'm not 100% sure.
Based on Jonathan Leffler's answer
#include<stdio.h>
void special_case(int (*array)[4]);
void anon(void){
int array4[4];
int array8[8];
special_case(&array4);
special_case(&array8);
}
int main(void){
anon();
return 0;
}
void special_case(int (*array)[4]){
printf("hello\n");
}
gcc array_fixed_int.c &&./a.out will yield warning:
array_fixed_int.c:7:18: warning: passing argument 1 of ‘special_case’ from incompatible pointer type [-Wincompatible-pointer-types]
7 | special_case(&array8);
| ^~~~~~~
| |
| int (*)[8]
array_fixed_int.c:2:25: note: expected ‘int (*)[4]’ but argument is of type ‘int (*)[8]’
2 | void special_case(int (*array)[4]);
| ~~~~~~^~~~~~~~~
Skip warning:
gcc -Wno-incompatible-pointer-types array_fixed_int.c &&./a.out
How do I reliably get the size of a C-style array? The method often recommended seems to be to use sizeof, but it doesn't work in the foo function, where x is passed in:
#include <iostream>
void foo(int x[]) {
std::cerr << (sizeof(x) / sizeof(int)); // 2
}
int main(){
int x[] = {1,2,3,4,5};
std::cerr << (sizeof(x) / sizeof(int)); // 5
foo(x);
return 0;
}
Answers to this question recommend sizeof but they don't say that it (apparently?) doesn't work if you pass the array around. So, do I have to use a sentinel instead? (I don't think the users of my foo function can always be trusted to put a sentinel at the end. Of course, I could use std::vector, but then I don't get the nice shorthand syntax {1,2,3,4,5}.)
In C array parameters in C are really just pointers so sizeof() won't work. You either need to pass in the size as another parameter or use a sentinel - whichever is most appropriate for your design.
Some other options:
Some other info:
for C++, instead of passing a raw array pointer, you might want to have the parameter use something that wraps the array in a class template that keeps track of the array size and provides methods to copy data into the array in a safe manner. Something like STLSoft's array_proxy template or Boost's boost::array might help. I've used an array_proxy template to nice effect before. Inside the function using the parameter, you get std::vector like operations, but the caller of the function can be using a simple C array. There's no copying of the array - the array_proxy template takes care of packaging the array pointer and the array's size nearly automatically.
a macro to use in C for determining the number of elements in an array (for when sizeof() might help - ie., you're not dealing with a simple pointer): Is there a standard function in C that would return the length of an array?
A common idiom mentioned in GNU Libstdc++ documentation is the lengthof function:
template<typename T, unsigned int sz>
inline unsigned int lengthof(T (&)[sz]) { return sz; }
You can use it as
int x[] = {1,2,3,4,5};
std::cerr << lengthof(x) << std::endl;
Warning: this will work only when the array has not decayed into a pointer.
How about this?..
template <int N>
void foo(int (&x)[N]) {
std::cerr << N;
}
You can either pass the size around, use a sentinel or even better use std::vector. Even though std::vector lacks initializer lists it is still easy to construct a vector with a set of elements (although not quite as nice)
static const int arr[] = {1,2,3,4,5};
vector<int> vec (arr, arr + sizeof(arr) / sizeof(arr[0]) );
The std::vector class also makes making mistakes far harder, which is worth its weight in gold. Another bonus is that all C++ should be familiar with it and most C++ applications should be using a std::vector rather than a raw C array.
As a quick note, C++0x adds Initializer lists
std::vector<int> v = {1, 2, 3, 4};
You can also use Boost.Assign to do the same thing although the syntax is a bit more convoluted.
std::vector<int> v = boost::assign::list_of(1)(2)(3)(4);
or
std::vector<int> v;
v += 1, 2, 3, 4;
c provides no native support for this. Once an array is passed out of its declared scope, its size is lost.
You can pass the size with the array. You can even bundle them into a structure if you always to to keep the size, though you'll have some bookkeepping overhead with that.
I also agree that Corwin's method above is very good.
template <int N>
void foo(int (&x)[N])
{
std::cerr << N;
}
I don't think anybody gave a really good reason why this is not a good idea.
In java, for example, we can write things like:
int numbers [] = {1, 2, 3, 4};
for(int i = 0; i < numbers.length(); i++)
{
System.out.println(numbers[i]+"\n");
}
In C++ it would be nice instead of saying
int numbers [] = {1, 2, 3, 4};
int size = sizeof(numbers)/sizeof(int);
for(int i = 0; i < size; i++)
{
cout << numbers[i] << endl;
}
We could take it a step further and go
template <int N>
int size(int (&X)[N])
{
return N;
}
Or if that causes problems I suppose you could write explicitly:
template < int N >
int size(int (&X)[N])
{
int value = (sizeof(X)/sizeof(X[0]));
return value;
}
Then we just have to go in main:
int numbers [] = {1, 2, 3, 4};
for(int i = 0; i < size(numbers); i++)
{
cout << numbers[i] << endl;
}
makes sense to me :-)
An array expression will have its type implicitly converted from "N-element array of T" to "pointer to T" and its value will be the address of the first element in the array, unless the array expression is the operand of either the sizeof or address-of (&) operators, or if the array expression is a string literal being used to initialize another array in a declaration. In short, you can't pass an array to a function as an array; what the function receives is a pointer value, not an array value.
You have to pass the array size as a separate parameter.
Since you're using C++, use vectors (or some other suitable STL container) instead of C-style arrays. Yes, you lose the handy shorthand syntax, but the tradeoff is more than worth it. Seriously.
Now, you can use C++11's extent and rank.
By example:
#include <iostream>
#include <type_traits>
int main()
{
int a[][3] = {{1, 2, 3}, {4, 5, 6}};
std::cout << "\nRank: : " << std::rank<decltype(a)>::value;
std::cout << "\nSize: [_here_][]: " << std::extent<decltype(a), 0>::value;
std::cout << "\nSize: [][_here_]: " << std::extent<decltype(a), 1>::value;
std::cout << "\nSize: [][]_here_: " << std::extent<decltype(a), 2>::value;
}
prints:
Rank: : 2
Size: [_here_][]: 2
Size: [][_here_]: 3
Size: [][]_here_: 0
You need to pass the size along with the array, just like it is done in many library functions, for instance strncpy(), strncmp() etc. Sorry, this is just the way it works in C:-).
Alternatively you could roll out your own structure like:
struct array {
int* data;
int size;
};
and pass it around your code.
Of course you can still use std::list or std::vector if you want to be more C++ -ish.
Since c++11, there is a very convenient way:
static const int array[] = { 1, 2, 3, 6 };
int size = (int)std::distance(std::begin(array), std::end(array))+1;