Initializing an array of char pointers in C - c

I'm trying to create an array of char pointers in different ways, but only the first method works:
#include <stdio.h>
int main(){
char* a[] = {"hello"};
// works
char** b = {"hello"};
// warning: incompatible pointer types initializing
// 'char **' with an expression of type 'char [6]'
char c[][] = {"hello"};
// error: array has incomplete element type 'char []'
return 0;
}
What am I doing wrong?

Since neither element is an array, the C compiler does not recognize the {"hello"} syntax as an array, which will cause the code to break. If you do char** b = a you can observe that the syntax does in fact work.
When working with multidimensional arrays in C, every dimension except the first must be given a length, since the compiler cannot infer it. If you change it to char c[][6] = {"hello"} you can observe that it works.

All of these are incorrect at some extent.
a initializes an array of 1 single pointer char*, pointing at the string literal "hello". But since we aren't allowed to modify string literals, this should have been written as const char* a[] = {"hello"};.
b attempts to set a pointer-to-a-char-pointer to point at a string literal, which is not valid C. A conforming compiler must give you a message saying that the code isn't correct.
c is nonsense syntax, we cannot declare arrays of arrays with no dimensions known. Only the left-most dimension can be left blank, for the compiler to set implicitly.

This char* a[]; is an array of char*, aka array of char pointers.
This char** b; is a pointer to char*, or, in other words, a pointer to pointer to char. The initializer {"hello"} is wrong because its type is "array of char*".
This char c[][]; is an invalid declaration. You are declaring an array of arrays of char, but you don't specify the size of the subarray. You have to change it to something like char c[][N]; so that the sub-array has a known size, or even char c[M][N]; so that you know the entire size. Using the initializer here is OK, but note that it is interpreted as a completely different type than the first.
Correct initializations would be:
char* a[] = {"hello"};, this will create an array a with one element pointing to some memory location chosen by the compiler where the "hello" string is stored.
char** b = a; or char** b = &a[0];, this simply creates a pointer to pointer to char, so assigning a or &a[0] to it is the only reasonable thing here. There could be other applications, but it depends on the case.
char c[][10] = {"hello"};, this will create an array c with one sub-array of size 10 holding the elements {'h', 'e', 'l', 'l', 'o', '\0', '\0', '\0', '\0', '\0'}.

Related

Different Types Of Initializing Character Arrays in C

I was learning about pointers and strings.
I understood that,
Pointers and Arrays/Strings have similar behaviours.
array[] , *array , &array[0]. They all are one and the same.
Why does the three statements in this code work, and char * help one does not ?
#include <stdio.h>
void display(char*help){
for(int i=0; help[i]!='\0'; i++){
printf("%c", help[i]);
}
}
int main(){
// char help[] = "Help_Me"; //Works
// char help[] = {'H','e','l','p','_','M','e','\0'}; //Works
// char *help = "Help_Me"; //Works
char *help = {'H','e','l','p','_','M','e','\0'}; //Error
display(help);
}
Error Messages :
warning: initialization of 'char *' from 'int' makes pointer from integer without a cast
warning: excess elements in scalar initializer
Pointers and Arrays/Strings have similar behaviours.
Actually, no, I wouldn't agree with that. It is an oversimplification that hides important details. The true situation is that arrays have almost no behaviors of their own, but in most contexts, an lvalue designating an array is automatically converted to pointer to the first array element. The resulting pointer behaves like a pointer, of course, which is what may present the appearance that pointers and arrays have similar behaviors.
Additionally, arrays are objects, whereas strings are certain configurations of data that char arrays can contain. Although people sometimes conflate strings with the arrays containing them or with pointers to their first elements, that is not formally correct.
array[] , *array , &array[0]. They all are one and the same.
No, not at all, though the differences depend on the context in which those appear:
In a declaration of array (other than in a function prototype),
type array[] declares array as an array of type whose size will be determined from its initializer;
type *array declares array as a pointer to type; and
&array[0] is not part of any valid declaration of array.
In a function prototype,
type array[] is "adjusted" automatically as if it were type *array, and it therefore declares array as a pointer to type;
type *array declares array as a pointer to type; and
&array[0] is not part of any valid declaration of array.
In an expression,
array[] is invalid;
*array is equivalent to array[0], which designates the first element of array; and
&array[0] is a pointer to array[0].
Now, you ask,
Why does the three statements in this code work, and char * help one does not ?
"Help_Me" is a string literal. It designates a statically-allocated array just large enough to contain the specified characters plus a string terminator. As an array-valued expression, in most contexts it is converted to a pointer to its first element, and such a pointer is of the correct type for use in ...
// char *help = "Help_Me"; //Works
But the appearance of a string literal as the initializer of a char array ...
// char help[] = "Help_Me"; //Works
... is one of the few contexts where an array value is not automatically converted to a pointer. In that context, the elements of the array designated by the string literal are used to initialize the the array being declared, very much like ...
// char help[] = {'H','e','l','p','_','M','e','\0'}; //Works
. There, {'H','e','l','p','_','M','e','\0'} is an array initializer specifying values for 8 array elements. Note well that taken as a whole, it is not itself a value, just a syntactic container for eight values of type int (in C) or char (in C++).
And that's why this ...
char *help = {'H','e','l','p','_','M','e','\0'}; //Error
... does not make sense. There, help is a scalar object, not an array or a structure, so it takes only one value. And that value is of type char *. The warnings delivered by your compiler are telling you that eight values have been presented instead of one, and they have, or at least the one used for the initialization has, type int instead of type char *.
array[] , *array , &array[0]. They all are one and the same.
No. Presuming array names some array, array[] cannot be used in an expression (except where it might appear in some type description, such as a cast).
array by itself in an expression is automatically converted to a pointer to its first element except when it is the operand of sizeof or the operand of unary &. (Also, a string literal, such as "abc", denotes an array, and this array has another exception to when it is converted: When it is used to initialize an array.)
In *array, array will be automatically converted to a pointer, and then * refers to the element it points to. Thus *array refers to an element in an array; it is not a pointer to the array or its elements.
In &array[0], array[0] refers to the first element of the array, and then & takes its address, so &array[0] is a pointer to the first element of the array. This makes it equivalent to array in expressions, with the exceptions noted above. For example, void *p = array; and void *p = &array[0]; will initialize p to the same thing, a pointer to the first element of the array, because of the automatic conversion. However, size_t s = sizeof array; and size_t s = sizeof &array[0]; may initialize s to different values—the first to the size of the entire array and the second to the size of a pointer.
// char help[] = "Help_Me"; //Works
help is an array of char, and character arrays can be initialized with a string literal. This is a special rule for initializations.
// char help[] = {'H','e','l','p','_','M','e','\0'}; //Works
help is an array, and the initializer is a list of values for the elements of the array.
// char *help = "Help_Me"; //Works
help is a pointer, and "Help_Me" is a string literal. Because it is not in one of the exceptions—operand of sizeof, operand of unary &, or used to initialize an array—it is automatically converted to a pointer to its first element. Then help is initialized with that pointer value.
char *help = {'H','e','l','p','_','M','e','\0'}; //Error
help is a pointer, but the initializer is a list of values. There is only one thing to be initialized, a pointer, but there are multiple values listed for it, so that is an error. Also, a pointer should be initialized with a pointer value (an address or a null pointer constant), but the items in that list are integers. (Character literals are integers; their values are the codes for the characters.)
{'H','e','l','p','_','M','e','\0'} is not a syntax that creates a string or an array. It is a syntax that can be used to provide a list of values when initializing an object. So the compiler does not recognize it as a string or array and does not use it to initialize the pointer help.
Pointer is not the array and it cant be initialized like an array. You need to create an object, then you can assign its reference to the pointer.
char *help = (char[]){'H','e','l','p','_','M','e','\0'};

Passing char arrays to functions that take char pointers

In C, why is it that I'm able to pass character arrays to functions that take a char * as an argument, but I cannot pass the address of an array to functions that take a char **?
UPDATE: Interestingly, changing the argument type to char* qux[12] doesn't change the compiler warning at all
For example:
#include <stdio.h>
void foo(char* qux) { puts(qux); }
void bar(char** qux) { puts(*qux); }
void baz(char* qux[12]) { puts(*qux); }
int main() {
char str[12] = "Hello there";
foo(str);
bar(&str); // Compiler warning
baz(&str); // Same compiler warning
return 0;
}
In the second case, I get a compiler warning:
warning: incompatible pointer types passing 'char (*)[12]' to
parameter of type 'char **' [-Wincompatible-pointer-types]
What's going on here?
Arrays naturally decays to pointers to their first element. So in the call foo(str) it's really the same as foo(&str[0]). This is of type char * so it's all okay.
Now the second call, the one to bar, is a different matter. When you use &str you don't get a pointer to the arrays first element, you get a pointer to the array itself. And as the compiler noted, this is of type char (*)[12], which is very different from (and incompatible with) char **.
Lastly, when you declare baz you say that the argument is of type char *[12], that is you have an array or 12 pointers to char, not that you have a pointer to an array of 12 char. Furthermore, due to the array decay to pointer thing, char *[12] is actually the same as char **.
In C, char * represents a pointer to a contiguous sequence of characters. A contiguous sequence of characters with a null termination is what we call a string in C.
char ** is a pointer to a contiguous sequence of strings, and since each string is a contiguous sequence of characters terminated by a null ('\0') character, char ** represents a contiguous sequence to a contiguous sequence of null terminated characters.
Your declaration:
char str[12] = "Hello there";
Declares str to be an array of characters of length 12, and it is initialized to the 12 characters {'H','e','l','l','o',' ','t','h','e','r','e','\0'}. This is compatible with the parameter in foo(), but not with bar and baz both of which expect a contiguous sequence of pointers to strings. That is why those two give you a compiler warning because the parameter is incompatible with the arguments passed in.

Using char* a[] vs char a[][]

I know that when used as function parameter char* a[] is equivalent to char a[][].
When used as function parameter char* a[] is equivalent to char** a. Also known as Array to pointer conversion to some.
However when used in block scope they are not the same, and I'm confused as when I should prefer one over the other, or if I should skip char a[][], as I usually tend to see char* a[] in other people's code.
One argument against char a[][] is obviously that you have to give a fixed size for the C-strings it will contain, but does that affect performance in any way?
Should I prefer this:
char* a[] = {"hello", "world"};
Or this:
char a[][10] = {"hello", "world"};
The key to understanding the seemingly strange syntax cases of function parameters is to understand array decay. This is all about one rule in C that says, whenever you pass an array as parameter to a function, it decays into a pointer to the first element of that array (*).
So when you write something like
void func (int a[5]);
then at compile-time, the array gets replaced with a pointer to the first element, making the above equal to:
void func (int* a);
This rule of pointer decay applies recursively to multi-dimensional arrays. So if you pass a multi-dimensional array to a function:
void func (int a[5][3]);
it still decays to a pointer to the first element. Now as it happens, a 2D array is actually an array of arrays. The first element is therefore a one-dimensional array, with size 3 in this example. You get an array pointer to that array, the type int(*)[3]. Making the above equivalent to
void func (int (*a)[3]);
And this is actually the reason why we can omit the left-most dimension of the array parameter, and only that dimension. Upon doing so we make an array of incomplete type, which you normally wouldn't be able to use: for example you can't write code like int array[]; inside a function's body. But in the parameter case, it doesn't matter that the left-most dimension isn't specified, because that dimension will "decay away" anyway.
(*) Source, C11 6.7.6.3/7:
A declaration of a parameter as ‘‘array of type’’ shall be adjusted to
‘‘qualified pointer to type’’, ...
Adjustment of array type to pointer type works only when it is declared as a parameter of a function.
As a function parameter char* a[] will be adjusted to char** a and char a[][10] to char (*a)[10]. Otherwise char* a[] declares a as an array of pointers to char while char a[][10] declares a an array of arrays of char.
Preference of
char* a[] = {"hello", "world"};
over this
char a[][10] = {"hello", "world"};
make sense when you want to save some bytes of memory. In latter case for each of a[0] and a[1] 10 bytes are allocated. Note that in case of char* a[], strings pointed by the elements of a are immutable.
If you want contiguous memory allocation then go with char a[][10].

In a 2D array a[row][column] what does a[row] stand for?

I want to add a string to a char 2D array.
#include<stdio.h>
#include<string.h>
int main(void)
{
char a[20][20]={"fire","ice","water"};
a[3]="land";
printf("%s",a[3]);
}
I get and error message saying
incompatible types when assigning to type ‘char[20]’ from type ‘char *’
a[3]="land";
^
The code works if instead I use strcpy(a[3],"land").
So my question is why doesn't the first code work?Isn't a[3] a pointer to the first element of the fourth row of the char array? If it is not a pointer,then why does strcpy() work even though it expects a pointer argument?
I'm a beginner and this is my first question on SO so I apologize for any mistakes.
Arrays are not assignable. You must assign individual characters one by one which is what strcpy does.
The type of a[i] is char[20] ,i.e, an array of characters of size 20 and the type of "land" is char* , a pointer to char. These are not compatible types and this is what the compiler is complaining about.
Arrays are not assignable in C.
Yes array decays to a pointer while using strcpy() and a[3] will point to the 4th row of your 2D array.
Whereas in the case a[3] = "land" a[3] doesn't decay to a pointer.
For ex:
char *p;
p = "hello";
This is a valid assignment because p is a pointer.Whereas
char a[10];
a = "hello";
Since a is a array and not a pointer you will get an error for this. Note the difference between an array and a pointer.
You cannot add directly a string to a char **. This is a feature usually supported by the OOP (Oriented Object Programming) languages.
"a[3]="land";" is not supported by C, you need to use a fonction that will assign each character to each char of your char *. The main one is strcpy() (man strcpy).

How to define a double dimensional array using a single pointer

is char a[64][] equivalent to char *a[64]?
If yes, what if I want to declare char a[][64] using a single pointer. How can i do it?
char a[][64]
is similar in pointer representation to
char (*a)[64]
you can read this. for 2D-array the second dimension size should be specified.
You're looking for a pointer to an array:
char (*a)[64];
Perhaps you are not sure about how the following two statements are different.
char* a[64];
char (*a)[64];
The first one defines a to be an array of 64 char* objects. Each of those pointers could point to any number of chars. a[0] could point to an array of 10 chars while a[1] could point to an array of 20 chars. You would do that with:
a[0] = malloc(10);
a[1] = malloc(20);
The second one defines a to be a pointer to 64 chars. You can allocate memory for a with:
a = malloc(64);
You can also allocate memory for a with:
a = malloc(64*10);
In the first case, you can only use a[0][0] ...a[0][63]. In the second case, you can use a[0][0] ... a[9][63]
char a[64][] is equivalent to char *a[64].
No, because char a[64][] is an error. It attempts to define a to an array of 64 elements where each element is of type char[] - an incomplete type. You cannot define an array of elements of incomplete type. The size of element must be a fixed known constant. The C99 standard §6.7.5.2 ¶2 says
The element type shall not be an incomplete or function type.
Now if you were to compare char a[][64] and char *a[64], then again they are different. That's because the array subscript operator has higher precedence than *.
// declares an array type a where element type is char[64] -
// an array of 64 characters. The array a is incomplete type
// because its size is not specified. Also the array a must have
// external linkage.
extern char a[][64];
// you cannot define an array of incomplete type
// therefore the following results in error.
char a[][64];
// however you can leave the array size blank if you
// initialize it with an array initializer list. The size
// of the array is inferred from the initializer list.
// size of the array is determined to be 3
char a[][2] = {{'a', 'b'}, {'c', 'd'}, {'x', 'y'}};
// defines an array of 64 elements where each element is
// of type char *, i.e., a pointer to a character
char *a[64];
If you want to declare a pointer to an array in a function parameter, then you can do the following -
void func(char a[][64], int len);
// equivalent to
void func(char (*a)[64], int len);
char (*a)[64] means a is a pointer to an object of type char[64], i.e., an array of 64 characters. When an array is passed to a function, it is implicitly converted to a pointer to its first element. Therefore, the corresponding function parameter must have the type - pointer to array's element type.

Resources