2D Arrays and Pointers - C - c

Just trying to really get my head round Arrays and Pointers in C and the differences between them and am having some trouble with 2d arrays.
For the normal 1D array this is what I have learned:
char arr[] = "String constant";
creates an array of chars and the variable arr will always represent the memory created when it was initialized.
char *arr = "String constant";
creates a pointer to char which is currently pointing at the first index of the char array "String constant". The pointer could point somewhere else later.
char *point_arr[] = {
"one", "two","three", "four"
};
creates an array of pointers which then point to the char arrays "one, "two" etc.
My Question
If we can use both:
char *arr = "constant";
and
char arr[] = "constant";
then why can't I use:
char **pointer_arr = {
"one", "two", "three", "four"
};
instead of
char *pointer_arr[] = {
"one", "two", "three", "four"
};
If I try the char ** thing then I get an error like "excess elements in scalar initializer". I can make the char** example work by specifically allocating memory using calloc, but as I didn't have to do this with char *arr = "blah";. I don't see why it is necessary and so I don't really understand the difference between:
char **arr_pointer;
and
char *arr_pointer[];
Many thanks in advance for your advice.

See this answer in the C FAQ:
There it is explained for char [] vs char *. The same thing can be extended to char *[] vs char **.

In short, you cannot use { ... } as an initialiser for a scalar.
char **arr_pointer declares a scalar, not an array. In contrast, the reason you can do char *arr = "constant"; is because you're still declaring a scalar, it just happens to point at a string literal.

If you really want to get through the bottom of it then try to understand arrays and pointers through ints rather than chars. According to my experience I had trouble understanding pointers and arrays when chars were involved. Once you understand ints properly you will realize that it's not diff at all.
int *ptr[] is an array of pointers to integers where as int **ptr is a pointer to a pointer that references an integer.
int *arrptrs[2];
arrptrs[0]=(int *)malloc(sizeof(int)*5);
arrptrs[1]=(int *)malloc(sizeof(int)5);
This initializes two arrays referenced by the elements of the array arrptrs. The name of an array refers to the memory location of the first element of an array so arrptrs is of type (int *) as the first element of this array is of type (int *)
Suppose we do
int **ptr=arrptrs
Then,
*ptr is the first element of arrptrs which is arrptrs[0]and *(ptr+1) is arrptrs[1] and doing a *arrptrs[0] is the first element in the array referenced by arrptrs[0].
I hope this helps although I am not sure if you needed this.

Pointers (char *pointer;) have values; arrays (char array[];) have elements.
The declaration char **ptr2 declares an object that can take a single value, not an object that can take several elements.

Quote from Wikipedia:
In computing, a scalar variable or field is one that can hold only one value at a time... ...For example, char, int, float, and double are the most common scalar data types in the C programming language.
So as Oli Charlesworth pointed out in his reply using {.....} initializes multiple items but as char **arr_pointer is a 'scalar' and so can only point at 1 thing at a time (an address) then the {...} notation cannot work here.

Related

Ragged arrays of int in C

Just learning C, so bear with me. I understand that char *argv[] is an array whose elements are pointers to strings. So, for example:
char *fruits[] = {"Apple", "Pear"};
represents a "ragged array" of char arrays (i.e. a two-dimensional array whose rows have different lengths). So far, so good.
But when I try to abstract this to int types, it does not seem to work.
int *numbers[] = { {1,2,3}, {4,5,6} };
I get the following warning from the GCC compiler:
warning: braces around scalar initializer.
Can someone help me wrap my brain around this?
int *numbers[] = { {1,2,3}, {4,5,6} } can't work, you are attempting to initialize elements of an array of pointers with lists of integers.
To initialize an array of pointers you need to provide the addresses that point to the desired values, i.e. you must initialize each element of the pointer array with addresses of the ints you want them to point to, in this case the initial element of an array of int so that you can have access to the beginning of the array and thus to the rest of it via indexing:
//have 2 flat arrays of int
int a[] = {1, 2, 3};
int b[] = {4, 5, 6};
// make the array of pointers point to its initial elements
int *numbers[] = { &a[0], &b[0] };
// access
printf("%d", numbers[1][1]); // 5
You could also use:
int *numbers[] = { a, b };
Why? Because when you use an array name in an expression, for example you pass it as argument of a function or an initializer list like the above one, it decays to a pointer to its first element.
char *fruits[] = {"Apple", "Pear"}; works fine because string literals have type char[] generally, e.g. "Apple" has type char[6], so when you use them in the initializer list expression the same decay process occurs, and you'll end up with a pointer to the first element of the nul terminated array of chars.
Note that unlike the above string literals (which all end with a nul byte \0), the int arrays have no sentinel value, unless you establish one, otherwise for you to safely navigate inside the bounds of each array you must keep track of its size.
int *numbers[] It is not "ragged array" only an array of pointers.
int *numbers[] = { (int[]){1,2,3}, (int[]){4,5,6,7,8} };
In this example it has two elements having type pointer to int. Those pointers hold the reference of the first element of the arrays used to initialize it.

Why doesn't the C double asterisk work for creating a 2D array?

Why does this work:
char *name = "steven";
but this doesn't:
char **names = {"steven", "randy", "ben"};
Or, why does this work:
char *names[] = {"steven", "randy", "ben"};
but, again, this doesn't:
char **names = {"steven", "randy", "ben"};
A char **p is not a 2D array, it is a pointer to a pointer to a character. However, you can have more pointers and more characters following, resembling a kind of model of a 2D structure of characters.
C compiler interpret { "steven" } as a 1D array of characters, because the braces are optional (standard chapter 6.7.9 paragraph 14).
As you tried, you can declare an array of pointers to a character by char *p[].
But if you want to have that pointer (to pointers to characters), you need to tell your compiler. The address of an array can be assigned to the pointer.
char **p = (char *[]){ "steven", "randy", "ben", };
Additional note: Since string literals are unmutable, you better add a const for the characters. And since the address of these unnamed string literals are constant, too, you can provide another one.
const char * const *p = (const char * const []){ "steven", "randy", "ben", };
I also wondered, what if I could answer you in the simplest way possible.
Why are you confused?
A simple pointer to integer for example allocated with 8 cells, acts in the same way as an array has a dimension of 8 cells.
The only difference, that you can't see, is that a pointer that has 8 cells allocated is on a part of the memory that is called the HEAP, while a variable of type int tab[8] is allocated on the STACK.
Indeed, since the cells are linked in memory, it is easy to imagine that a pointer and an array whose first cell address is sent are the same thing.
Why it doesn't work in the other case
However, when the idea comes to associate (** and [][])
Let's take the example of an int ** ;
int **tab;
tab = malloc(sizeof(int *) * 4);
//secure malloc do not forget
for (int i = 0; i < 4; i++)
{
tab[i] = malloc(sizeof(int) * 3);
//secure malloc do not forget
}
and an
int[4][3];
You have a problem.
To imagine, a double array type follows itself in memory, because it is the very principle of arrays.
While a double pointer has first 4 cells of type int * allocated (which follow each other in memory) and then each pointer of these 4 cells, each points to a memory area of 3 ints which follow each other. But the whole thing does not follow each other in the memory!
A way that may interest you
One thing you can do instead is to create an int ptr(*)[3];
which can point to the first element of an array of size 3, i.e. the address of an array [4][3] for example.
The initializer for a scalar object may not contain more than one item.
6.7.9 Initialization
...
Constraints
2 No initializer shall attempt to provide a value for an object not contained within the entity
being initialized.
...
11 The initializer for a scalar shall be a single expression, optionally enclosed in braces. The
initial value of the object is that of the expression (after conversion); the same type
constraints and conversions as for simple assignment apply, taking the type of the scalar
to be the unqualified version of its declared type
C 2011 Online Draft
char **names declares a single, scalar object, not an array, so any initializer for it must only contain a single item. That initializer may be a single string ("steven"), optionally enclosed in braces ({ "steven" }). However, it may not be a list of initializers.

char **s vs char *s[] initialization

I am trying hard to understand the difference between char *s[] and char s** initialization.
My char *s[] works fine, whereas my char s1** throws an error [Error] scalar object 's1' requires one element in initializer. I don't get the meaning of that error.
How can we initialize char s1** properly?
#include<stdio.h>
int main(void)
{
char *s[]={"APPLE","ORANGE"};
char **s1={"APPLE","ORANGE"};
return 0;
}
TLDR: char **s1=(char*[]){"apple","orange"};.
You can, of course, initialize pointers with the address of an element in an array. This is more common with simpler data types: Given int arr[] = {1,2};you can say int *p = &arr[0];; a notation which I hate and have only spelled out here in order to make clear what we are doing. Since arrays decay to pointers to their first elements anyway, you can simpler write int *p = arr;. Note how the pointer is of the type "pointer to element type".
Now your array s contains elements of type "pointer to char". You can do exactly the same as before. The element type is pointer to char, so the pointer type must be a pointer to that, a pointer to pointer to char, as you have correctly written:
char **s2= &s[0];, or simpler char **s2= s;.
Now that's a bit pointless because you have s already and don't really need a pointer any longer. What you want is an "array literal". C99 introduced just that with a notation which prefixes the element list with a kind of type cast. With a simple array of ints it would look like this: int *p = (int []){1, 2};. With your char pointers it looks like this:
char **s1=(char*[]){"apple","orange"};.
Caveat: While the string literals have static storage duration (i.e., pointers to them stay valid until the program ends), the array object created by the literal does not: Its lifetime ends with the enclosing block. That's probably OK if the enclosing block is the main function like here, but you cannot, for example, initialize a bunch of pointers in an "initialize" routine and use them later.
Caveat 2: It would be better to declare the arrays and pointers as pointing to const char, since the string literals typically are not writable on modern systems. Your code compiles only for historical reasons; forbidding char *s = "this is constant"; would break too much existing code. (C++ does forbid it, and such code cannot be compiled as C++. But in this special case C++ does not have the concept of compound literals in this way, and the program below is not valid C++.) I adjusted the types accordingly in the complete program below which demonstrates the use of a compound literal. You can even take its address, like that of any other array!
#include<stdio.h>
int main(void)
{
/// array of pointers to char.
const char *arrOfCharPtrs[2]={"APPLE","ORANGE"};
/// pointer to first element in array
const char **ptrToArrElem= &arrOfCharPtrs[0];
/// pointer to element in array literal
const char **ptrToArrLiteralElem=(const char*[]){"apple","orange"};
/// pointer to entire array.
/// Yes, you can take the address of the entire array!
const char *(*ptrToArr)[2] = &arrOfCharPtrs;
/// pointer to entire array literal. Note the parentheses around
/// (*ptrToArrLiteral)- Yes, you can take the address of an array literal!
const char *(*ptrToArrLiteral)[2] = &(const char *[]){"apples", "pears"};
printf("%s, %s\n", ptrToArrElem[0], ptrToArrElem[1]);
printf("%s, %s\n", ptrToArrLiteralElem[0], ptrToArrLiteralElem[1]);
printf("%s, %s\n", (*ptrToArr)[0], (*ptrToArr)[1]);
// In order to access elements in an array pointed to by ptrToArrLiteral,
// you have to dereference the pointer first, yielding the array object,
// which then can be indexed. Note the parentheses around (*ptrToArrLiteral)
// which force dereferencing *before* indexing, here and in the declaration.
printf("%s, %s\n", (*ptrToArrLiteral)[0], (*ptrToArrLiteral)[1]);
return 0;
}
Sample session:
$ gcc -Wall -pedantic -o array-literal array-literal.c && ./array-literal
APPLE, ORANGE
apple, orange
APPLE, ORANGE
apples, pears

Memory allocation for pointer to a char array

would someone please explain the difference between
char *names[3]
and
char (*names)[3]
and how to read this operators?
If I want to allocate memory for them dynamically how to do so?
For the first case, I think it's just an array of char* of length 3, so no memory allocation not applicable. But in second case how to do memory allocation?
When faced with questions like this, you can usually turn to cdecl (online version here):
cdecl> explain char *names[3]
declare names as array 3 of pointer to char
cdecl> explain char (*names)[3]
declare names as pointer to array 3 of char
So the former creates array of three pointers-to-char:
+----------+
| names[0] | -> char
| [1] | -> char
| [2] | -> char
+----------+
And the latter creates a single pointer to a char array of size three.
+-------+
| names | -> char, char, char - no, not a dance step :-)
+-------+
The second line decodes as "declare names as pointer to array 3 of char".
I've been writing C for over 25 years, and I've never used such a variable.
Anyway, I guess this should work:
char data[3];
char (*names) = data;
Note that the variable name, names, is highly misleading since the variable holds only 3 single characters, as opposed to char *names[3] which is three pointers to characters and thus easily could be used to hold three strings.
Also note that the above code makes little sense, you could just use data directly if you had it.
The first an array of three pointers to char.
The second is a pointer to an array of three chars.
(Read it as "*names is a char[3]").
You can create such a pointer by taking the address of an array of three chars:
char name[3];
char (*names)[3] = &name;
or dynamically in the normal way:
char (*names)[3] = malloc(sizeof(*names)); /* or sizeof(char[3]), if you're fond of bugs */
or through the regular array-to-pointer conversion:
char stuff[2][3] = {};
char (*names)[3] = stuff; /* Same as &stuff[0], as normal. */
and how to read this operators?
Let's take the second one as it is the more complex of the two:
char (*names)[3];
When you are looking at a complex definition like this, the best way to attack it is to start in the middle and work your way out. “Starting in the middle” means starting at the variable name, which is names.
“Working your way out” means looking to the right for the nearest item (nothing in this case; the right parenthesis stops you short), then looking to the left (a pointer denoted by the asterisk), then looking to the right (an array of 3), then looking to the left (char).
This right-left-right motion works with most declarations.
This means names is a pointer to an char array of size 3.
This is very strange declaration but that is how it is read.
If I want to allocate memory for them dynamically how to do so?
Now that you know what the declaration means, memory allocation becomes easy:
char (*names)[3] = malloc(3 * sizeof(char));
char *names[3] is an array of 3 char pointers.
char (*names)[3] is an array pointer (pointer to array) to an array of 3 characters char[3].
So these two have fundamentally different meanings! Don't confuse them with each other.
If you wish to allocate an array of pointers, then you can do it as in either of these examples:
char** names = malloc(3 * sizeof(*names) );
char** names = malloc(sizeof(char*[3]));
char** names = calloc(3, sizeof(char*));
These are all equivalent (but calloc also sets all pointers to NULL). names will be a pointer to the first element in the array. It will be a pointer to a char*.
If you wish to allocate an array and point to the first element, simply do:
char* names = malloc(3 * sizeof(*names));
Alternatively, you can use the array pointer syntax and point to the array as whole:
char (*names)[3] = malloc(sizeof(*names));

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