Initializing an array with a pointer in c - c

When I run the code:
char *abc="Goodbye";
for(i=0; i<=7; i++){
printf("%c\n",*(abc+i)); }
it runs without problem, but when I run the following:
char *der={'a','a','a','a','a'};
for(i=0; i<=4; i++){
printf("%c\n",*(der+i)); }
it doesn't show the correct results and I receive warnings.
So why is this happening since "Goodbye" and {'a','a','a','a','a'} are arrays of chars?

{'a','a','a','a','a'} is not an array but an initializer list, and it can be used to initialize either an aggregate or a scalar.
(Yes, int x = {'a'}; is valid.)
If it's used to initialize a scalar, such as a pointer, only the first value is used, so your declaration of der is equivalent to
char *der = 'a';
You can probably see what the problem is.
So today's programming lessons:
When your compiler warns you that something might be wrong, it probably is.
(Most experienced programmers treat warnings as errors, -Werr. It's an even more important habit for the inexperienced.)
If you don't understand what a warning means, don't do anything until you've found out.

One way to initialize the array - which is related to your second example - is by doing:
char foo[] = {'a', 'b', 'c'};
In this syntax, to the right side of = you provide the array elements as comma separeted values inside a { }. So in the above example, array foo has three elements. First is a, second is b, and third is c. If you wanted a \0 at the end, you need to do it explicitly as char foo[] = {'a', 'b', 'c', '\0'};.
Regarding your second example, answer by #molbdnilo already explains what is semantically wrong with your statment char *der={'a','a','a','a','a'};. If you want to define der as array and initialize it using { }, you can do:
char der[] = {'a','a','a','a','a'};
This way you are actually defining a char array and initializing it with the content you want.
Note that, you NEED to mention the array size if you are not initializing it while defining it. Which means:
char foo[]; // Will give error because no size is mentioned
char foo[10]; // Fine because size is given as 10
However, mentioning size is optional if you initialize the array while defining it, as we saw in the examples above. But, if you mention the size and if your initializer is smaller than the array size, remaining elements will be initialized to 0. Like:
char bar[10] = {'a', 'b', 'c', '\0'};
/* Your arrays' content will be 'a', 'b', 'c', '\0',
* and all remaning 6 elements will be 0
*/

Related

In initializing pointer array, why only string doesn't show warning?

I'm not making a specific program. I was curious about some kind of syntax things in C language.
I made these declarations.
int main()
{
char *titles[] = {"NUDGE", "DECOUPLEING", "WORLD WAR Z"};
char *letters[] = {{'a', 'b', 'c'}, {'x', 'y', 'z'}};
}
In the second declaration, there were 3 kinds of warnings.
braces around scalar initializer
initialization makes pointer from integer without a cast
excess elements in scalar initializer
I got that *numbers[] kind of things should have pointer values in it.
Is the first declaration doesn't show warning because string type is a pointer?
and character is not a string?
+) What does 'scalar initializer' means in first, and third warning?
+)
char titles[][] = {"NUDGE", "DECOUPLEING", "WORLD WAR Z"};
What this shows error and the first one doesn't show error?
I'm a beginner at C pointer. Please explain :)
the second initialization is invalid as {'a', 'b', 'c'} is not the array which can dacal to pointer
you should use compound literals instead:
char *letters[] = {(char[]){'a', 'b', 'c'}, (char []){'x', 'y', 'z'}};
which defines array of two (2) pointers to arrays of three chars.

I don't understand this use of pointers

I'm trying to understand this use of pointers. From what I realized so far the value pointers hold is a reference to the memory address of another entity, and when using the * sign we access the value of the entity referenced by the pointer.
However, in this code that I encountered in the tutorial i'm using, the ptr_strpointer has a string value which is not a memory address, so I don't understand how *ptr_str (which I expected to be the value of a referenced entity) is used in the for loop.
char *ptr_str; int i;
ptr_str = "Assign a string to a pointer.";
for (i=0; *ptr_str; i++)
printf("%c", *ptr_str++);
This:
ptr_str = "Assign a string to a pointer.";
Is a shorthand for this:
// Somewhere else:
char real_str[] = {'A', 's', 's', 'i', 'g', ..., '.', '\0'};
// In your main():
ptr_str = real_str;
// or
ptr_str = &real_str[0];
In other words, string literals like "Hello World" are actually pointers to a character array holding your string. This is all done transparently by the compiler, so it might be confusing at first sight.
If you're curious, take a look at this other answer of mine, where I explain this in more detail.

What do array initialisers return?

What do array initialisers such as { 'a', 'b', 'c' } return? My understanding is that using an initialiser allocates contiguous memory blocks and return the address to the first block.
The following code doesn't work:
char *char_ptr_1 = { 'a', 'b', 'c', '\0' };
On the other hand, this is seems to work fine:
char char_array[] = { 'a', 'b', 'c', '\0' };
char *char_ptr_2 = char_array;
char_array stores the address to the first memory block which explains why I am able to assign the value of char_array to chat_ptr_2. Does C convert the value returned by the initialiser to something which can be stored in a pointer?
I did look online and and found a couple of answers which talked about the difference between arrays and pointers but they didn't help me.
Initializers do not return anything per se. They give the compiler directions as to what to put into the item being declared - in this case, they tell the compiler what to put into elements of an array.
That is why you cannot assign an initializer to a pointer: an array initializer needs to be paired with an array to make sense to the compiler.
A pointer can be initialized with a pointer expression. That is why the initialization in your
char *char_ptr_2 = char_array;
declaration works: the compiler converts char_array to a pointer, and initializes char_ptr_2 with it.
it's called array initializer, because it initalizes an array and not a pointer.
It's simply C syntax, why the pointer option is not allowed.
They are array initializers, not normal expressions that have a value. I. e., an array initializer can only be used to initialize an array. It is a special bit of syntax for a specific use, end of the story.
It doesn't really "return" anything, it's parsed compile time and an array is created. A pointer needs to point to something, you cannot assign it a direct value. So you first need the array, and then your pointer can point to it.

Why can't I omit the dimensions altogether when initializing a multi-dimensional array?

In Visual Studio 2010, this initialization works as expected:
char table[2][2] = {
{'a', 'b'},
{'c', 'd'}
};
But it does not seem legal to write something like:
char table[][] = {
{'a', 'b'},
{'c', 'd'}
};
Visual Studio complains that this array may not contain elements of 'that' type, and after compiling, VS reports two errors: a missing index and too many initializations.
QUESTION: Why can't I omit the dimensions altogether when initializing a multi-dimensional array?
Only the innermost dimension can be omitted. The size of elements in an array are deduced for the type given to the array variable. The type of elements must therefore have a known size.
char a[]; has elements (e.g. a[0]) of size 1 (8bit), and has an unknown size.
char a[6]; has elements of size 1, and has size 6.
char a[][6]; has elements (e.g. a[0], which is an array) of size 6, and has an unknown size.
char a[10][6]; has elements of size 6. and has size 60.
Not allowed:
char a[10][]; would have 10 elements of unknown size.
char a[][]; would have an unknown number of elements of unknown size.
The size of elements is mandatory, the compiler needs it to access elements (through pointer arithmetic).
Is this an acceptable work-around?
char * table [] = { "ab", "cd" };
EDIT: Note that it will add an extra '\0' on the end of each string.

C compound literals, pointer to arrays

I'm trying to assign a compound literal to a variable, but it seems not to work, see:
int *p[] = (int *[]) {{1,2,3},{4,5,6}};
I got a error in gcc.
but if I write only this:
int p[] = (int []) {1,2,3,4,5,6};
Then it's okay.
But is not what I want.
I don't understand why the error occurrs, because if I initialize it like a array, or use it with a pointer of arrays of chars, its okay, see:
int *p[] = (int *[]) {{1,2,3},{4,5,6}}; //I got a error
int p[][3] = {{1,2,3},{4,5,6}}; //it's okay
char *p[] = (char *[]) {"one", "two"...}; // it's okay!
Note I don't understand why I got an error in the first one, and please I can't, or I don't want to write like the second form because it's needs to be a compound literals, and I don't want to say how big is the array to the compiler. I want something like the second one, but for int values.
Thanks in advance.
First, the casts are redundant in all of your examples and can be removed. Secondly, you are using the syntax for initializing a multidimensional array, and that requires the second dimension the be defined in order to allocate a sequential block of memory. Instead, try one of the two approaches below:
Multidimensional array:
int p[][3] = {{1,2,3},{4,5,6}};
Array of pointers to one dimensional arrays:
int p1[] = {1,2,3};
int p2[] = {4,5,6};
int *p[] = {p1,p2};
The latter method has the advantage of allowing for sub-arrays of varying length. Whereas, the former method ensures that the memory is laid out contiguously.
Another approach that I highly recommend that you do NOT use is to encode the integers in string literals. This is a non-portable hack. Also, the data in string literals is supposed to be constant. Do your arrays need to be mutable?
int *p[] = (int *[]) {
"\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00",
"\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00"
};
That example might work on a 32-bit little-endian machine, but I'm typing this from an iPad and cannot verify it at the moment. Again, please don't use that; I feel dirty for even bringing it up.
The casting method you discovered also appears to work with a pointer to a pointer. That can be indexed like a multidimensional array as well.
int **p = (int *[]) { (int[]) {1,2,3}, (int[]) {4,5,6} };
First understand that "Arrays are not pointers".
int p[] = (int []) {1,2,3,4,5,6};
In the above case p is an array of integers. Copying the elements {1,2,3,4,5,6} to p. Typecasting is not necessary here and both the rvalue and lvalue types match which is an integer array and so no error.
int *p[] = (int *[]) {{1,2,3},{4,5,6}};
"Note I don't understand why I got a error in the first one,.."
In the above case, p an array of integer pointers. But the {{1,2,3},{4,5,6}} is a two dimensional array ( i.e., [][] ) and cannot be type casted to array of pointers. You need to initialize as -
int p[][3] = { {1,2,3},{4,5,6} };
// ^^ First index of array is optional because with each column having 3 elements
// it is obvious that array has two rows which compiler can figure out.
But why did this statement compile ?
char *p[] = {"one", "two"...};
String literals are different from integer literals. In this case also, p is an array of character pointers. When actually said "one", it can either be copied to an array or point to its location considering it as read only.
char cpy[] = "one" ;
cpy[0] = 't' ; // Not a problem
char *readOnly = "one" ;
readOnly[0] = 't' ; // Error because of copy of it is not made but pointing
// to a read only location.
With string literals, either of the above case is possible. So, that is the reason the statement compiled. But -
char *p[] = {"one", "two"...}; // All the string literals are stored in
// read only locations and at each of the array index
// stores the starting index of each string literal.
I don't want to say how big is the array to the compiler.
Dynamically allocating the memory using malloc is the solution.
Hope it helps !
Since nobody's said it: If you want to have a pointer-to-2D-array, you can (probably) do something like
int (*p)[][3] = &(int[][3]) {{1,2,3},{4,5,6}};
EDIT: Or you can have a pointer to its first element via
int (*p)[3] = (int[][3]) {{1,2,3},{4,5,6}};
The reason why your example doesn't work is because {{1,2,3},{4,5,6}} is not a valid initializer for type int*[] (because {1,2,3} is not a valid initializer for int*). Note that it is not an int[2][3] — it's simply an invalid expression.
The reason why it works for strings is because "one" is a valid initializer for char[] and char[N] (for some N>3). As an expression, it's approximately equivalent to (const char[]){'o','n','e','\0'} except the compiler doesn't complain too much when it loses constness.
And yes, there's a big difference between an initializer and an expression. I'm pretty sure char s[] = (char[]){3,2,1,0}; is a compile error in C99 (and possibly C++ pre-0x). There are loads of other things too, but T foo = ...; is variable initialization, not assignment, even though they look similar. (They are especially different in C++, since the assignment operator is not called.)
And the reason for the confusion with pointers:
Type T[] is implicitly converted to type T* (a pointer to its first element) when necessary.
T arg1[] in a function argument list actually means T * arg1. You cannot pass an array to a function for Various Reasons. It is not possible. If you try, you are actually passing a pointer-to-array. (You can, however, pass a struct containing a fixed-size array to a function.)
They both can be dereferenced and subscripted with identical (I think) semantics.
EDIT: The observant might notice that my first example is roughly syntactically equivalent to int * p = &1;, which is invalid. This works in C99 because a compound literal inside a function "has automatic storage duration associated with the enclosing block" (ISO/IEC 9899:TC3).
The one that you are using is array of int pointers. You should use pointer to array :
int (*p)[] = (int *) {{1,2,3}, {4,5,6}}
Look at this answer for more details.
It seems you are confusing pointers and array. They're not the same thing! An array is the list itself, while a pointer is just an address. Then, with pointer arithmetic you can pretend pointers are array, and with the fact that the name of an array is a pointer to the first element everything sums up in a mess. ;)
int *p[] = (int *[]) {{1,2,3},{4,5,6}}; //I got a error
Here, p is an array of pointers, so you are trying to assign the elements whose addresses are 1, 2, 3 to the first array and 4, 5, 6 to the second array. The seg fault happens because you can't access those memory locations.
int p[][3] = {{1,2,3},{4,5,6}}; //it's okay
This is ok because this is an array of arrays, so this time 1, 2, 3, 4, 5 and 6 aren't addresses but the elements themselves.
char *p[] = (char *[]) {"one", "two"...}; // it's okay!
This is ok because the string literals ("one", "two", ...) aren't really strings but pointers to those strings, so you're assigning to p[1] the address of the string literal "one".
BTW, this is the same as doing char abc[]; abc = "abc";. This won't compile, because you can't assign a pointer to an array, while char *def; def = "def"; solves the problem.

Resources