In the code above the row and col are the wrong way round on line 18. It should be
p[r][c] = 'a'; // row then column
As coder777 says, you can pass a 1-dimensional array, even though you can't pass a 2D array via a double pointer. You can, however, pass the address of the first element to a function, or use an intermediary "row array".
And If you're using C++ and don't really need to use a char**, you just need the function to handle any size array, you can use a template function.
Note: in an attempt to make the layout of the array clearer, the fill routine uses the fact that a byte is two nybbles wide and puts the row in the top nybble (0, 1, 2, ...) and the row in the lower nybble (a, b, c, ...). For this to display as wanted, the array elements have to be cast to int before outputting with cout.
Pass the address of the first element
Note this version of the function is called like
insert_values(&a[0][0]);
This is a good approach for a C program where you have to pass assorted sizes of array to the function. But you should pass the row and column counts explicitly using extra parameters.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
#include <iostream>
#include <iomanip> // for std::hex
using namespace std;
void insert_values(char* e);
const size_t rows = 8;
const size_t cols = 5;
int main(){
cout << hex << setfill('0'); // set hex mode and fill
char a[rows][cols] = {0}; // zero array
insert_values(&a[0][0]);
for (int r = 0; r < rows; r++){
for (int c = 0; c < cols; c++){
cout << ((0 == c) ? "" : " ") // separate elems with a space
<< setw(2) << (int)(unsigned char)a[r][c]; // display ascii value
}
cout << endl;
}
cout << endl;
return 0;
}
// fill array with 0a, 0b, ... 1a, 1b, ...
void insert_values(char* e)
{
for (int r = 0; r < rows; r++){
for (int c = 0; c < cols; c++){
e[r * cols + c] = (char)(0x10 * r + 0xa + c);
}
}
}
|
Use an intermediary "row array".
This approach uses the insert_values function you originally posted (but with the indices swapped round.)
But uses a helper array which stores the addresses of the row in the original array (these are char* value, to it's a char* array, which can be passed to a char** function parameter)
(Not sure how often this trick is useful?)
1 2 3
|
char* a_rows[] = {a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]};
insert_values(a_rows);
|
Template function
As it's a template function, the whole definition must be higher up in the cpp file than the calling function or, if used by multiple cpp files, in a header file.
1 2 3 4 5 6 7 8
|
template<size_t R, size_t C>
void insert_values(char (&a)[R][C]){
for (int r = 0; r < R; r++){
for (int c = 0; c < C; c++){
a[r][c] = (char)(0x10 * r + 0xA + c);
}
}
}
|
Note that while the form of the parameter definition for the template function is a bit convoluted, it is a way of enforcing the size of an array.
The problem with functions like this
int Test(int a[3]);
is that the compiler only cares about the address of the first element and how many elements are in each row. For example, this compiles with GCC and VC++ (i.e. the compiler doesn't care how many elements you pass to the function.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
|
#include <iostream>
#include <iomanip>
using namespace std;
#if (_MSC_VER < 1600)
typedef unsigned char uint8_t;
#else
#include <stdint.h> // might also be missing from earlier versions of MinGW?
#endif
void Test(int a[3]);
int main() {
// the guards are to look out for overruns
// D1 5E A5 E -- "Disease" -- nicked from Nintendo
uint8_t guard1[16] = {0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0};
int ARRAY1[ 1] = {1};
uint8_t guard2[16] = {0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0};
int ARRAY2[ 2] = {1, 2};
uint8_t guard3[16] = {0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0};
int ARRAY3[ 3] = {1, 2, 3};
uint8_t guard4[16] = {0xD1, 0x5E, 0xA5, 0xE0, 0xD1, 0x5E, 0xA5, 0xE0};
cout << hex;
cout << "ARRAY[1]" << endl << endl;
Test(ARRAY1);
cout << "ARRAY[2]" << endl << endl;
Test(ARRAY2);
cout << "ARRAY[3]" << endl << endl;
Test(ARRAY3);
return 0;
}
void Test(int a[3]) {
for (int i = 0; i < 3; ++i) {
cout << "a[i] = " << a[i] << endl;
}
cout << endl;
}
|
where the output is (noting the spurious values for the one and two element arrays)
ARRAY[1]
a[i] = 1
a[i] = e0a55ed1
a[i] = e0a55ed1
ARRAY[2]
a[i] = 1
a[i] = 2
a[i] = e0a55ed1
ARRAY[3]
a[i] = 1
a[i] = 2
a[i] = 3 |
whereas if you code Test() like this
1 2 3 4 5 6
|
void Test(int (&a)[3]) {
for (int i = 0; i < 3; ++i) {
cout << "a[i] = " << a[i] << endl;
}
cout << endl;
}
|
then the attempts to pass the one and two element array do not compile.
(Visual C++)
main.cpp(27) : error C2664: 'Test' : cannot convert parameter 1 from 'int [1]'
to 'int (&)[3]' |
(g++)
main.cpp:27:16: error: invalid initialization of reference of type 'int (&)[3]'
from expression of type 'int [1]'
main.cpp:11:6: error: in passing argument 1 of 'void Test(int (&)[3])' |
So, from a point of view of safety, you should either use the void Test(int (&a)[3]);
form, or pass the number of elements as a parameter void Test(int a[], size_t elemCount);
Or, of course, use a template version.
Andy