I have an API taking some options:
void init_api(const char* options[][2]);
I am allowed to pass a NULL pointer for no options, alternatively, an options array such as this can be passed:
const char* some_options[][2] = { {"opt1", "val1"},
{"opt2", "val2"},
{0,0}
};
This works without problems:
...
init_api(some_options);
... or ...
init_api(NULL);
...
However, this fails to compile:
const char* my_options[][2] = NULL; // error C2440: 'initializing' : cannot convert from 'int' to 'const char *[][2]'
if(...) {
my_options = some_options; // error C2440: '=' : cannot convert from 'const char *[4][2]' to 'const char *[][2]'
}
init_api(my_options); // no error here
What is going on here? Can someone explain this?
To declare an empty array of array of pointers to const char, you should use:
const char* my_options[][2] = {};
You need to declare a pointer to an array of pointers to const char instead. I recommend using a typedef to simplify the syntax.
typedef const char* array_of_two_cstring[2];
array_of_two_cstring* my_options = NULL;
if (...) {
my_options = some_options;
}
init_api(my_options);
In C++ (it is herited from C), array can be implicitly converted to pointer (only once though, that is char[] is compatible with char* but char[][] is compatible with char*[] but not `char**). However, the variable cannot be reassigned. So here you need to use a pointer instead of an array.
The init_api option accepts NULL as a parameter because for the compiler, its prototype is void init_api(char const* (*)[2]) (the first array degenerated into a pointer), and NULL is a valid pointer.
The compiler must know the array size.
If you omit the size of the array (ie: using []) you need to initialize the array with the definition, in order to let the compiler count how many items that array will contain.
Moreover you are assigning a pointer (NULL) to an array: const char *x[][2] is an array of two pointers to const char.
Edit:
In C++ (as in C), arrays can decay into pointers when you use them (with three exceptions which are not interesting here).
When you pass an array to a function expecting an array, what happens is that you actually pass a pointer to the array, since the array decays; you cannot pass an array by value in C or C++.
For this reason you can pass NULL to your function; the function parameter will be NULL, and if you try to access the array within your function (options[0]) your application will crash: you'll be dereferencing an invalid pointer.
You cannot however set your array variable to NULL, since it's not a pointer, it's an array: it will only decay when you'll use it in an expression.
const char* options[][2]
is an array of const char* pointers. You can't assign a pointer to an array.
A parameter declared as being an array of type T[N] or T[] becomes actually a parameter of type T*. Same is done for functions (a parameter declared as R(Params) becomes actually a parameter of type R(*)(Params...)).
Such transformation however is not done for other declarations. The reason it's done for function by-value parameters is that there is no way in C to actually copy an array directly (that is, to actually copy its contents) and it doesn't make sense to try and copy a function either, so such parameters are transformed in a way that conveys their purpose in a meaningful way.
So while you are initializing a pointer in the function parameter case, you are trying to initialize an array in the other case.
Related
I am working on a project and I am stuck at this for a while.
I have to pass to the "write" function a char array.I tried to convert the "data" curenty string to char arrays using https://www.techiedelight.com/convert-std-string-char-cpp/ but i get only errors
error: request for member ‘c_str’ in ‘data’, which is of pointer type ‘const string*’ {aka ‘const std::__cxx11::basic_string<char>*’} (maybe you meant to use ‘->’ ?)
char* c[] = const_cast<char*>(data.c_str());
void LCD::print(const std::string* data)
{
for (int i = 0; i < strlen(data); i++)
write(data[i]);
}
const std::string*
It's hardly ever a good idea to pass a std::string pointer into a function. I recommend not doing such thing. How to replace it depends on your intentions. Reference to const is a typical default choice if you're passing a single string.
strlen(data)
The strlen function does not accept an argument of type const std::string*. This program is ill-formed.
If you want the length of a std::string, you can use the size member function.
write(data[i]);
Here, you're using subscript operator to access the ith std::string object within an array of std::string objects pointed by data.
The strlen usage and the description imply that there is only one string involved which contradicts this array iteration.
error: request for member ‘c_str’ in ‘data’, which is of pointer type ‘const string*’ {aka ‘const std::__cxx11::basic_string<char>*’}
(maybe you meant to use ‘->’ ?)
char* c[] = const_cast<char*>(data.c_str());
The error message explains what you did wrong. data is a pointer. Pointers don't have member functions. You tried to call the member function c_str of the pointer which doesn't exist.
The error message also suggests the likely solution. If you indirect through the pointer (using the indirecting member access operator ->) to access the pointed string, then you will access the c_str member function of the string. std::string does have such member function.
There's another bug in the quoted code. You're trying to use the returned pointer to initialise an array of pointers c. That's not possible. There's only one pointer returned, and it doesn't make much sense to create an array of one elements in this case.
I have to pass to the "write" function a char array
I suspect that you actually mean, you have to pass a pointer to first element of a null terminated char array. It can be done like this:
std::string data = ...;
write(data.c_str());
It's so trivial, that I wouldn't recommend writing a function for this purpose.
The problem is that strlen accepts a const char* while you're supplying a const string*. You can solve this by using the size() member function of std::string as follows:
void print(const std::string* data)
{
for (int i = 0; i < (*data).size(); i++)//first we dereference the pointer named data then call the size() member function on pointed string object
write((*data)[i]); //first we dereference the pointer named data then access the ith element of the pointed string object
}
char* pointer = new char [5];
strcpy_s(pointer,4, "foo");
I am not fully understanding how pointers work. In my understanding the variable pointer is supposed to store the starting address of the new allocated string of chars. If so why is it important that the pointer is a char since its only storing an address.
why can't i just type
void* pointer = new char [5]
Thanks.
the pointer needs to know the size of its element, thanks to it you can use [] operator to reach a certain element of the array, how else would it know how much memory it has to move to get to the n-th element? If you could declare a pointer to any type as void, then it would have to automatically deduce the type it points to. Consider this piece of code:
char* pointer = new char[5];
pointer[3] = 'a';
for void* this would not be possible. I suppose that the strcpy_s function expects the first parameter as char*, not void* and that is the reason why your code doesn't compile. Pointer itself just allows you to know where some variable/array or even function is in memory, but when you specify pointer's type it gives it more flexibility.
Trying to figure out string literal types in C/CPP
printf("%s\n", typeid("abc").name());
printf("%s\n", typeid(const char[]).name());
print
A4_c
A_c
Not familiar with C, is different length/capacity of array means different type in C? if yes, why could we pass char[n] as char[] when passing function parameters?
is different length/capacity of array means different type in C?
Yes.
if yes, why could we pass char[n] as char[] when passing function parameters?
It is actually not possible to accept an array value as a function argument. When a function argument is declared to be an array type, that declaration is adjusted by the language to mean a pointer to an element of that array type. I.e. array of char becomes pointer to char. Same applies to return types. Example:
void function(char argument[N]); // this
void function(char *argument); // actually means this
Similarly, when an array name is used as a value argument, that array name implicitly converts to a pointer to the first element of the array. This implicit conversion is called decaying. Example:
void function(char *argument);
char array[N];
function(array); // array decays to pointer to first element
Note that this adjustment is only applied to "toplevel" arrays. Pointers to arrays and references to arrays are not adjusted to be pointers or references to pointers to element of that array even in function argument declarations.
What's the difference between char[] and char[n] in C/CPP?
char[n] is an array type. It is an array of n elements. It is a complete type. It is possible to create arrays of this type.
char[] is an array of unknown bound. It is an incomplete type. It is not possible to create an array of this type. This type can only be used in contexts where it is adjusted to another type. In a function declaration, it is adjusted to a pointer to element.
In a declaration of a non-argument array, it is adjusted to the the actual array type of known bound that is deduced from an initialiser:
char arr[] = {'1', '\0'}; // type of arr is adjusted to char[2]
char arr[]; // ill-formed declaration
Since the typeid().name() output varies from compiler to compiler, piping output to c++filt is a better way to see the type's name.
This code:
#include <iostream>
int main(void)
{
std::cout << typeid("abc").name() << std::endl;
std::cout << typeid(const char[]).name() << std::endl;
return (0);
}
compiled and ran as ./a.out outputs:
A4_c
A_c
but when ran as ./a.out | c++filt
outputs the following:
char [4]
char []
For char[] vs char[4] its minimal, but for big nested types and auto in later standards of C++ it becomes a real handy tool for seeing whats happening under the hood.
(Sorry for not being a direct answer but oh how I cringe to see unformatted typeid names in any context, especially since they are compiler specific)
Recently, I need to declare a string array, so I wrote down the following statement:
const char** directories = {"cricket_batting", "cricket_bowling", "croquet", "tennis_forehand", "tennis_serve", "volleyball_smash"};
However, g++ showed me the error:
error: scalar object ‘directories’ requires one element in initializer
So I changed the statement to this:
const char* directories[] = {"cricket_batting", "cricket_bowling", "croquet", "tennis_forehand", "tennis_serve", "volleyball_smash"};
This time, it was right. But I can't exactly know the difference between char** and char[].
= {...};
Initialisation of this form is known as list-initialization.
const char**
This type is a "pointer to pointer to const char".
const char*[]
This type is an "array of pointer to const char".
Simply put, you cannot initialise a pointer with list-initialization. You can initialise an array with list-initialization; it initializes each of the elements in the array with the items in the braced list.
The reason comes down to what exactly you get from a declaration. When you declare a const char**, all you get is a single pointer object. It's a const char**, which is a pointer promising to point at a pointer to const char. Nonetheless, all you actually have is the outer pointer. You can't then initialise this as though it's an array, since you only have one pointer. Where exactly are you going to store the elements of the initialization list? There is no array of pointers in which you can store them.
However, when you declare a const char*[], what you get is an array of pointers. The size of the array is determined by the size of the list because you have omitted it.
The former is a pointer to a pointer to const char while the latter is an array to pointer to const char. The latter is the correct way to initialize an array of strings.
You would need to allocate memory using new to set up char** , for you can't simply initialize pointers with { }.
When you write int a[] we are making an array of integers. Similarly when you write const char* directories[] you are creating an array of char* pointers. Now each char* can point to a char or a string as in your case. This creates individual string constants and assigns the base address of each to the corresponding pointer in the array
char **directories is pointer to a pointer and you can't assign value to a pointer using { .. }
What is the difference between Type** name, and Type* name[]?
Why would someone use one over the other?
Thanks
Well that depends, is it in a variable declaration or in a function argument? If in a variable declaration:
Type** name = &pointer_to_type;
Type* name[] = { &pointer_to_type, 0, &pointer_to_type };
The first one is a pointer to pointer to type, while the second one is an array of pointers to type of length 3.
If in a function argument, they are the same thing. Arrays decay to pointers, and both Type** name and Type* name[] are exactly the same as function arguments. However, the second form makes it clear that name is an array of pointers of unknown length, while the first one does not. I would use Type** to specify a single element and Type*[] to specify an array.
The difference between the two is mostly demonstrated when declaring/defining objects of either type.
The notation Type *name[] creates an array of unknown size (can be inferred from the initializer), Type** name creates a pointer. That means:
char *array[]={"hello", "world", 0}; /* OK, array of size 3 */
char **ptr={"hello", "world", 0}; /* not possible */
They behave differently in some expressions. Particularly, arrays can't be assigned to, but pointer variables can:
ptr++, ptr=array; /* assignment and mutation of ptr possible */
// array=whatever /* impossible */
The sizeof operator works differently on the two. sizeof(array) will depend on the number of elements of the array (may be 12 in this case), but sizeof(ptr) returns always the same size (eg. 4 on main 32-bit architectures)
Also, when declaring global variables, you mustn't mix the two:
extern char* data[];
must be accompanied in the .c file by
char* data[N];
and vice versa. Basically, defining the array means allocating several consecutive objects, whereas defining a pointer means allocating a single variable. The compiler treats both differently, and must know which is which.
However, when declaring or passing parameters to functions, they are the same. So
int main(int argc, char** argv)
int main(int argc, char* argv[]) /* the same */
Depends on the context.
If it defines a variable which is not a function parameter, then
in Type** name, name is a pointer to a pointer to a variable of type Type and in Type* name[SOME_POSITIVE_INTEGER_CONSTANT], it's an array of pointers to variables of type Type.
If it's a function parameter, then both are the same, and name is a pointer to a pointer to a variable of type Type.
Basically, Type** is a pointer to pointer. Think it like (Type*)* . So it points to Type* which can be a Type or Type[].
And the other one, Type* is a pointer to a Type or in this case, an array Type[]. So they are 'almost' the same.