Only compiles as an array of pointers, not array of arrays - c++

Suppose I define two arrays, each of which have 2 elements (for theoretical purposes):
char const *arr1[] = { "i", "j" };
char const *arr2[] = { "m", "n" };
Is there a way to define a multidimensional array that contains these two arrays as elements? I was thinking of something like the following, but my compiler displays warnings about incompatible types:
char const *combine[][2] = { arr1, arr2 };
The only way it would compile was to make the compiler treat the arrays as pointers:
char const *const *combine[] = { arr1, arr2 };
Is that really the only way to do it or can I preserve the type somehow (in C++, the runtime type information would know it is an array) and treat combine as a multidimensional array? I realise it works because an array name is a const pointer, but I'm just wondering if there is a way to do what I'm asking in standard C/C++ rather than relying on compiler extensions. Perhaps I've gotten a bit too used to Python's lists where I could just throw anything in them...

No. First, this
char const *combine[][2] = { arr1, arr2 };
cannot work, because arr1 and arr2 cannot be used to initialize an array. But this:
char const *arr1[] = { "i", "j" };
char const *arr2[] = { "m", "n" };
char const *(*combine[])[2] = { &arr1, &arr2 };
works as well as this
char const *arr1[] = { "i", "j" };
char const *arr2[] = { "m", "n" };
char const *combine[][2] = { {"i", "j"}, {"m", "n"} };

Related

Rerefence to a const char* array

I am trying to avoid to repeat my code for 5 differentes arrays. I have 3 arrays (could be more in future):
const char *FirstlistOfOptionText[2] = {OPT_1,
OPT_2};
const char *SecondlistOfOptionText[2] = {OPT_1,
OPT_2};
const char *ThirdlistOfOptionText[2] = {OPT_1,
OPT_2};
THe elements in each one will not be the same. Now they are because I just copy&paste them. Number of elements won't neither.
I have a function in which I want to print every element of a list depending on a value I give as parameter. Also, I need to print one of those elements in other color (all in white except one in green).
I just want to have one code for printing and selecting the color as I have right now. But I want to select the correct array before doing that. I thought about:
const char *listOfOptions[];
if(menu_t.first_level_option == 0) {
listOfOptions = FirstlistOfOptionText;
}
if(menu_t.first_level_option == 1) {
listOfOptions = SecondlistOfOptionText;
}
if(menu_t.first_level_option == 2) {
listOfOptions = ThirdlistOfOptionText;
}
But I get some errors about storage size of 'listOfOptions' isn't known. Or that I can't use const char** for a char* or thing like that.
What is the correct way to do this?
Essentially you neeed to make listOfOptions a char **;
An array of pointers can be referenced through a pointer to pointers (that's what the char ** is).
The size will be unknown to anyone using listOfOptions thus you need a way to determine the size. Either terminate the list with a NULL pointer or you will have to use a 2nd variable (listOfOptionsSize) that tracks the size.
So the code below should compile (I opted for the NULL terminated lists).
const char *FirstlistOfOptionText[] = {"a", "b", NULL};
const char *SecondlistOfOptionText[] = {"c", "d", "e", "f", NULL};
const char *ThirdlistOfOptionText[] = {"e", "f", "g", NULL};
const char **listOfOptions= NULL; // pointer to pointer(s)
int first_level_option= 2; // some value for testing
if(first_level_option == 0) {
listOfOptions = FirstlistOfOptionText;
}
if(first_level_option == 1) {
listOfOptions = SecondlistOfOptionText;
}
if (first_level_option == 2) {
listOfOptions = ThirdlistOfOptionText;
}
printem(listOfOptions);
Now for your printing function, it will get the pointer to the list of pointers as a parameter and and will look like this:
void printem(const char **listOfOptions)
{
const char *word;
while (*listOfOptions!=NULL) { // while the pointer in the list not NULL
word= *listOfOptions; // get the char * to which listOfOptions is pointing
printf("entry: %s\n", word);
listOfOptions++;
}
}
Oh, and welcome to C-Pointer Hell :-)
const char *FirstlistOfOptionText[2] is an array of two pointers to char.
const char *listOfOptions[] is an array of unknown size with pointers to char.
const char **listOfOptions; is a pointer to a pointer to char and you can assign it the address of your list of options array:
listOfOptions = FirstlistOfOptionText;
const char *listOfOptions[];
But I get some errors about storage size of 'listOfOptions' isn't known.
The size of an array is part of its type. T[1] and T[2] are different types.
An array without a specified size is an incomplete type, and you cannot create an object of an incomplete type.
Or that I can't use const char** for a char* or thing like that.
Yes, because those are completely different types. The first is a pointer to a pointer to a constant char object. The second is a pointer to a non-constant char object.
Your code attempts to treat an array like a first-class citizen which you can assign like a "normal" object such as an int. It also attempts to treat arrays of different dimensions equally. None of that can work. C++ arrays are more limited than you think.
The solution is thus to use std::vector. And while you're at it, std::string instead of char*.
#include <string>
#include <vector>
#define OPT_1 "a"
#define OPT_2 "b"
int main()
{
std::vector<std::string> FirstlistOfOptionText = { OPT_1, OPT_2 };
std::vector<std::string> SecondlistOfOptionText = { OPT_1, OPT_2 };
std::vector<std::string> ThirdlistOfOptionText = { OPT_1, OPT_2 };
int first_level_option = 0;
std::vector<std::string> listOfOptions;
if (first_level_option == 0) {
listOfOptions = FirstlistOfOptionText;
}
if (first_level_option == 1) {
listOfOptions = SecondlistOfOptionText;
}
if (first_level_option == 2) {
listOfOptions = ThirdlistOfOptionText;
}
}
Of course, this can (and should) be improved even more. For example, getting rid of the preprocessor macros and putting the list selection into a function like std::vector<std::string> GetListOfOptions(int).
To print a list of options you may use a template function that takes a reference to a static array as a parameter:
template <int n>
void PrintOptions(const char* (&listOfOptions)[N])
{
for (int i = 0; i < N; i++)
{
//actual printing
}
}
void PrintMenu(/* ... */)
{
//...
switch (menu_t.first_level_option)
{
case 0:
PrintOptions(FirstlistOfOptionText);
break;
case 1:
PrintOptions(SecondlistOfOptionText);
break;
case 2:
PrintOptions(ThirdlistOfOptionText);
break;
}
//...
}
The size of an array will be deduced by the compiler.

Initializing char* vs int*

This is possible in C++:
const char* ch = "hello";
But something like this is not possible:
int* i = { 1, 2, 3 };
Both char *ch and int* i are plain pointers. Why can char* be assigned with multiple chars while int* can not be assigned with multiple ints?
I know we can use
int x[] = {1, 2, 3};
but that is not the question.
const char* ch = "hello";
is sort of like
static const char string_literal[] = { 'h', 'e', 'l', 'l', 'o', '\0' };
const char* ch = &string_literal[0];
except that each identical string literal does not necessarily point to a distinct location in memory.
The same is also possible for any other type:
static int integer_list[] = { 1, 2, 3 };
int* i = &integer_list[0];
// or equivalently, just int* i = integer_list;
Now, i[0] is 1, i[1] is 2, and i[2] is 3.
There is a special syntax for string literals, because they are so frequently used, and so frequently in contexts where it is not desirable to clutter the code with dummy variables.
If you've got a lot of code that uses statically allocated read-only arrays of integer type, you may be able to hide the boilerplate using templates:
template <int a, int b, int c>
struct int_array { static const int values[3]; };
template <int a, int b, int c>
const int int_array<a, b, c>::values[] = { a, b, c };
You only need to define the template once, and then each different user can use that template for the specific values that user is interested in.
const int* i = int_array<1, 5, 6>::values;
Often, it will be easier to simply define a separate array variable, but there are cases where such a template helps.
As noted in the comments, it's possible to define the template more generically, so that it works for arrays of arbitrary type and arbitrary length, but it requires an up-to-date compiler with good support for the current version of C++ (for GCC and clang, current versions are fine, but make sure to pass the -std=c++11 or -std=gnu++11 option to enable C++11 features):
template <typename T, T... v>
struct static_array {
static const T values[sizeof...(v)];
};
template <typename T, T... v>
const T static_array<T, v...>::values[sizeof...(v)] = { v... };
Now, the syntax for a user of this array is
const int* i = static_array<int, 1, 2, 3, 4>::values;
const unsigned* j = static_array<unsigned, 1, 2, 3, 4, 5>::values;
A string literal is an array of characters. Note that ch is just a pointer to a single character so it really doesn't point to the string as a whole but just its base address (the address of the first character). An initializer-list (i.e {1, 2, 3}) is not an array, and therefore can't be used to initialize the pointer.
The character literal is compiled to a piece of initialized storage in the data segment of your binary. The const char * is a pointer to that storage.
As a matter of fact, it should be possible to do the same for an const int *, if only you had the address of a memory block. You could do this using inline assembler (but I never tried) specifying a .data segment.

Is it alright to use memcpy() to copy a struct that contains a pointer?

I was thinking about this the other day and I am curious if this is a bad idea...
Lets say there is a structure that contains a pointer to a string array.
Would the memcpy() copy the 'name' array pointer in the below example?
Edit: The std is inaccessible in this example.
struct charMap
{
unsigned char * name;
unsigned char id;
};
typedef struct charMap CharMapT;
class ABC
{
public:
ABC(){}
void Function();
CharMapT* structList;
}
void ABC::Function ()
{
CharMapT list[] =
{
{"NAME1", 1},
{"NAME2", 2},
{"NAME3", 3}
};
structList = new CharMapT[sizeof(list)];
memcpy(structList, &list, sizeof(list));
}
There are several errors in the code presented, which I will talk about first, followed by my stock-diatribe of pointers vs. arrays.
struct charMap
{
unsigned int * name;
unsigned int id;
};
typedef struct charMap CharMapT;
This declares a structure type that includes a pointer to unsigned int as the first member (name) and an int as the second member (id). On a 32-bit system with default byte packing this will be 8 bytes wide (32-bit pointer = 4bytes, 32-bit signed int=4bytes). If this is a 64-bit machine the pointers will be 8 bytes wide, the int still-likely 32-bits wide, making the structure size 12 bytes.
Questionable Code
void ABC::Function ()
{
CharMapT list[] =
{
{"NAME1", 1},
{"NAME2", 2},
{"NAME3", 3}
};
structList = new CharMapT[sizeof(list)];
memcpy(structList, &list, sizeof(list));
}
This allocates dynamic array of CharMapT structs. How many? More than you think. The sizeof(list) will return the byte-count of the list[] array. Since a CharMapT structure is 8 bytes wide (see above) this will 3 * 8, or 24 CharMapT items (36 items if using 64-bit pointers).
We then memcpy() 24 bytes (or 36 bytes) from list (the & in &list is unecessary) to the newly allocated memory. this will copy over 3 CharMapT structures, leaving the other 21 we allocated untouched (beyond their initial default construction).
Note: you're initializing a const char * to a field declared as unsigned int *, so if this even compiled the fundamental data type would be different. Assuming you fixed your structure and change the pointer type to const char *, the addresses of the static string constants (the addresses of the "NAME" constants) somewhere in your const data segment will be assigned to the pointer variables of the elements in structList[0].name, structList[2].name, and structList[3].name respectively.
This will NOT copy the data pointed to. it will only copy the pointer values. If you want copies of the data then you must raw-allocate them (malloc, new, whatever).
Better still, use an std::vector<CharMapT>, use std::string for CharMapT::name, and use std::copy() to replicate the source (or even direct-assignment).
I hope that explains what you were looking for.
Pointer vs. Array Diatribe
Never confuse a pointer with an array. A pointer is a variable that holds an address. Just like an int variable hold an integer value, or a char variable holds a character type, the value held in a pointer is an address
An array is different. It is also a variable (obviously), but it cannot be an l-value, and nearly every place it is typically used a conversion happens. Conceptually that conversion results in a temporary pointer that points to the data type of the array, and holds the address of the first element. There are times when that concept does not happen (such as the applying the address-of operator).
void foo(const char * p)
{
}
char ar[] = "Hello, World!";
foo(ar); // passes 'ar', converted to `char*`, into foo.
// the parameter p in foo will *hold* this address
or this:
char ar[] = "Goodbye, World!";
const char *p = ar; // ok. p now holds the address of first element in ar
++p; // ok. address in `p` changed to address (ar+1)
but not this:
char ar[] = "Goodbye, World!";
++ar; // error. nothing to increment.
It won't copy your actual data pointed by name. It will copy the pointer and you'll have 2 pointers to the same place in 2 objects (for each pair of objects in 2 arrays).
All you really need to know here is that memcpy will give you a bit for bit copy of the original. So what you'll have is two pointers with the same value (i.e., an address) which refer to the same data.
On a side note, you have declared name as a pointer to int, which is of course wrong here. It should be a const char*. Also, as this is C++ and not C, you're better served by something like std::copy which won't break your code subtly if charMap someday becomes a complex type. On the same note, prefer std::string instead of const char* in most situations.
Your use of sizeof() is wrong when calling new. You are allocating an array of CharMapT elements. You have to specify the number of elements, but you are specifying a byte count instead. So you need to fix that:
structList = new CharMapT[sizeof(list) / sizeof(CharMapT)];
With that fixed, the result of the memcpy() will be that structList will contains an exact copy of the raw data that list[] contains. That means that the structList[N].name pointers will contain the same values as the list[N].name pointers, and thus they will all be pointing at the same physical memory for the string values.
If you want to do a deep copy of the string values, you have to allocate them separately, eg:
void ABC::Function ()
{
CharMapT list[] =
{
{"NAME1", 1},
{"NAME2", 2},
{"NAME3", 3}
};
int num = sizeof(list) / sizeof(CharMapT);
structList = new CharMapT[num];
for (int i = 0; i < num; ++i)
{
int len = strlen(list[i].name);
structList[i].name = new char[len+1];
strcpy(structList[i].name, list[i].name);
structList[i].name[len] = 0;
structList[i].id = list[i].id;
}
...
for (int i = 0; i < num; ++i)
delete[] structList[i].name;
delete[] structList;
}
I'd like to add to #EdS.'s answer:
Your code is just much more c++ than c-style c++ code if you do it like this:
#include<string>
#include<vector>
struct CharMap
{
CharMap(const std::string& name, unsigned char id); // only needed if you don't use -std=c++11
std::string name;
unsigned char id;
};
CharMap::CharMap(const std::string& name, unsigned char id):
name(name),
id(id)
{}
class ABC
{
public:
ABC(); // or ABC() = default; if you use -std=c++11
void Function();
private:
std::vector<CharMap> structList;
}
ABC::ABC(){} // not needed with -std=c++11
void ABC::Function ()
{
// This works with -std=c++11:
//structList =
//{
// {"NAME1", 1},
// {"NAME2", 2},
// {"NAME3", 3}
//};
// without c++11:
structList = std::vector<CharMap>(3);
structList[0] = CharMap("NAME1",1); // don't worry about copies, we have RVO (check wikipedia or SO)
structList[1] = CharMap("NAME2",2);
structList[2] = CharMap("NAME2",3);
}
Why not using std::vector for making an array? You can do that like this:
#include<vector>
std::vector<CharMapT> structList(list.size());
It is safer, too, avoiding using pointers decreases the chance of memory leaks or bugs arising due to wrongly using the sizeof operator.
I suppose you do not really want a structList, that has as many elements as the memory size of your list. (If list is double this could be many times more than the number of elements in your list.)
Also, memcpy is really not necessary, if list is also a vector (that is a c function really). You just do a simple assign operation:
structList = list; // given that list is a vector.
This will copy the elements like memcpy.

How to correctly initialize multidimentional char array and pass it to function?

I'm writing a program which finds exit from maze. I have a multidimentinal array representing the actual maze.
const int size = 12;
char maze[ size ][ size ] = {
"############",
"#...#......#",
"..#.#.####.#",
"###.#....#.#",
"#....###.#..",
"####.#.#.#.#",
"#..#.#.#.#.#",
"##.#.#.#.#.#",
"#........#.#",
"######.###.#",
"#......#...#",
"############",
};
VS C++ gives me a warning message, saying that size is too small for such array. I guess it's because there must be also '\0' symbol in each line. How do I initialize char array without '\0' symbols? I don't want to initialize size with value 13 because it's will be too confused to use this constant for functions (printing array, making move etc.) Is there any way to do it?
Also, how to pass this array to function void mazeTraverse using pointer?
int main()
{
mazetraverse(maze)
}
void mazeTraverse(char (*maze)[ size ])
Such code doesn't works...
You need to account for the NULL character at the end of the string:
char maze[size][size + 1] = { /* */ };
Alternatively, for more flexibility, you can do:
char *maze[size] = { /* */ };
I see you're using C++. Is there any reason you're not using std::string?
std::string maze[size] = { /* */ };
It's a lot more flexible; now you just change the prototype to:
void mazeTraverse(std::string maze[]);
If you're even more insane, you'll use std::vector<std::string>.
EDIT: I recommend learning a bit about std::string. It works just like a char* but you don't have to manually allocate it/etc. For example:
std::string mystring = "lol";
mystring = "lololol"; // perfectly legal!
std::cout << mystring[0] << "\n";
// Or: printf("%c\n", mystring[0]);
char* sz[8];
strcpy(sz, mystring[0].c_str());
// And so on...
So long as you're using C++, why not just make a simple class?:
class Maze {
public:
Maze(int width, const std::string& data)
:width_(width),
data_(data.begin(), data.end()) {
}
char operator()(int row, int column) const {
return data_[width_*row + column];
}
private:
int width_;
std::vector<char> data_;
};
You can initialize it easily by taking advantage of the fact that subsequent string literals, like "foo" "bar", are implicitly concatenated into "foobar":
Maze my_maze(12,
"############"
"#...#......#"
"..#.#.####.#"
"###.#....#.#"
"#....###.#.."
"####.#.#.#.#"
"#..#.#.#.#.#"
"##.#.#.#.#.#"
"#........#.#"
"######.###.#"
"#......#...#"
"############");
To initialize, I would:
char* maze[ size ] = {
"############",
"#...#......#",
"..#.#.####.#",
"###.#....#.#",
"#....###.#..",
"####.#.#.#.#",
"#..#.#.#.#.#",
"##.#.#.#.#.#",
"#........#.#",
"######.###.#",
"#......#...#",
"############",
};
To pass parameters you should be able to use char**. So that would be:
void mazeTraverse(char ** param)
"Doesn't works"? The code does work. And it works perfectly fine. (Assuming the compiler lets you to use those 13-character string literals to initialize arrays of size 12. This is actually an error in C++, but you said that you are getting a mere warning).
This
mazeTraverse(maze);
will compile and do exactly what you want it to do (the way I understand it). What exactly doesn't work in your case? "Doesn't work" is not exactly a meaningful description of the problem.
As for getting rid of the warning in the array initialization, if you insist on having the array of exact size, you'll have to initialize it in per-character fashion as in
char maze[ size ][ size ] = {
{ '#', '#', '#', ... },
{ ... },
// and so on
};
If you want to use string literals, then as you noted yourself, you have to declare the inner sub-arrays with bigger size
char maze[ size ][ size + 1 ] = {
"############",
// and so on
};
and change the function declaration accordingly
void mazeTraverse(char (*maze)[ size + 1 ])

Arrays of pointers to arrays?

I'm using a library which for one certain feature involves variables like so:
extern const u8 foo[];
extern const u8 bar[];
I am not allowed to rename these variables in any way.
However, I like to be able to access these variables through an array (or other similar method) so that I do not need to continually hardcode new instances of these variables into my main code.
My first attempt at creating an array is as follows:
const u8* pl[] = {
&foo,
&bar
};
This gave me the error cannot convert 'const u8 (*)[]' to 'const u8*' in initialization, and with help elsewhere along with some Googling, I changed my array to this:
u8 (*pl)[] = {
&foo,
&bar
};
Upon compiling I now get the error scalar object 'pl' requires one element in initializer.
Does anyone have any ideas on what I'm doing wrong? Thanks.
An array of pointers to arrays only works if foo and bar have exactly the same size, and that size is known at compile time in your translation unit.
const u8 (*pl[])[32] = {&foo, &bar};
If that is not the case, you must use an array of pointers to bytes.
const u8 *pl[] = {foo, bar};
As the arrays don't have a size in their declaration is there any reason you can't just an array of pointers to their first elements?
E.g.
const u8* pl[] = { foo, bar };
If you wanted an array of pointers to arrays I think that you would need to do:
const u8 (*pl[])[] = { &foo, &bar };
but I don't see that it really has any advantage over the previous solution.
extern const int a[];
const int * aa[] = { a };
Remove the &. An array decays normally into a pointer.
typedef int u8; // Using int as I don't know what a u8 is.
const u8 foo[] = { 1, 2, 3};
const u8 bar[] = { 1 };
const u8* pl[] = {
foo,
bar
};