Ragged arrays of int in C - arrays

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.

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'};

Declare an array with pointer in C

Why is this syntax illegal in C?
int *a = {1,2,3};
Aren't arrays and pointers the same thing? Why would this work and not the above?
int a[] = {1,2,3};
int *b = a;
Why is this syntax illegal in C?
In int a[] = {1,2,3};, the {1,2,3} is a syntax for providing initial values of the things being initialized. A list of values in braces is a general notation for listing initial values. It is not a notation for representing an array.
The declaration says a is an array, but that is just in its int a[] part. The {1,2,3} does not indicate an array—it just provides a list of values, and it could be used for non-array objects, such as providing values for the members of a structure.
In int *a = {1,2,3};, the int *a part defines a to be a pointer. A pointer just has one value in it—the address of something (or null). So it does not make sense to initialize it with multiple values. To initialize a pointer, you need to provide an address for it to point to.
There is a notation in C for representing an array of given values. It is called a compound literal, and can be used for types in general, not just arrays. Its syntax is “(type) { initial values }”. So we can create an array with (int []) {1, 2, 3}. A pointer could be defined and initialized to point to this array with int *a = (int []) {1, 2, 3};. However, this should be done with care. Such an array is temporary (has automatic storage duration that ends when execution of its associated block ends) when defined inside a function, so the pointer will become invalid when the function returns (or before, if it is in an inner block).

Why can I cast from int** (as a 2 dimension array) to int*?

I wrote the code -
int arr2d[2][2] = {{1, 2}, {3, 4}};
int * arr = (int*)arr2d;
int i = 0;
for(i = 0; i < 4; i++)
{
printf("%d ", arr[i]);
}
The output was as if I printed every element in the arrays in arr2d, nothing out of the ordinary.
Why is that so?
Why can I cast from int** (as a 2 dimension array) to int*?
You have a misunderstanding. Arrays are not pointers. They do decay to pointers in most contexts, but that's a question of evaluation, not nature. Accordingly, 2D arrays are arrays of arrays, not arrays of pointers. Thus, they do not decay to pointers to pointers, but rather pointers to arrays. There is no int** involved anywhere in your code.
Given this declaration:
int arr2d[2][2] = {{1, 2}, {3, 4}};
The relevant pointer assignment you can perform without a cast is
int (*arr2d_ptr)[2];
arr2d_ptr = arr2d; // arr2d is not a pointer, but it does decay to one in this expression
arr2d_ptr is a pointer to a two-element array of int. The assignment makes it point to the first element of arr2d. If you convert that to type int *, then the result points to the first int in the array to which arr2d_ptr points. For example,
int *ip = (int *) arr2d_ptr;
That's natural, because that int is exactly the first part of the array. You can access it by index as ip[0] or *ip. And you can access the second int in that array as ip[1].
I guess the other facet of the question is about the expressions ip[2] and ip[3]. Arrays are contiguous sequences of elements. Arrays of arrays are not special in this regard: they are contiguous sequences of (smaller) arrays. Thus, the layout of your arr2d is like this:
array..|array..|
. If you overlay the layout of each of the member arrays then you get this:
int|int|int|int|
, which is exactly the same as the layout of a one-dimensional array of four int. This is why you can access all four ints by indexing ip (or arr in your example code).
Fun fact: because expressions of array type decay to pointers, you don't need a cast here. You could instead dereference arr2d_ptr to form an expression designating the first element of the 2D array, which is a 1D array, and let that decay to a pointer:
int *ip2 = *arr2d_ptr;
or, equivalently,
int *ip3 = *arr2d;
The values are stored in memory as if it was a linear array. That's why you can access them like that.
It doesn't work when the 2 dimensional array is composed out of pointers, and I have to understand why.
If you are working with an array of pointers you'll have to use a pointer to pointer, aka, double pointer.
It seems logic that if you want a pointer to access elements in an array of pointers you'll need a pointer to pointer.
//...
int *arr2d[2][2];
int **arr = (int**)arr2d;
//...
If you want to use it as a 2D array, which is what you would want in most cases, you can use:
Live sample
#include <stdio.h>
#include <string.h>
int main()
{
int arr2d[2][2] = {{1, 2}, {3, 4}};
int(*arr)[2] = arr2d; //pointer to 2d array with 2 columns
for (int i = 0; i < 2; i++){
for(int j = 0; j < 2; j++){
printf("%d ", arr[i][j]); //use it as if it were a 2d array
}
putchar('\n');
}
}
A double pointer is not to be confused with a 2D array, these are different things, this thread has a nice explanation of what each of these mean.
In memory, the array char a[4] = {1, 2, 3, 4} looks likes this:
[(1)(2)(3)(4)]
with () representing a byte of memory and [] hinting you where the array begins/ends.
The array char a[2][2] = {{1, 2}, {3, 4}} looks like this in memory:
[[(1)(2)][(3)(4)]]
See, there is no difference, as [] don't really exist in memory, they are just hints I used in my reply. Effectively, if you don't know anything about the arrays and just look at the content of the raw bytes in memory, you'll see:
(1)(2)(3)(4)
in both cases.
Thus instead of creating an array a[Xmax][Ymax] and accessing the elements using a[x][y], you can as well create the array as b[Xmax * Ymax] and access the elements using b[x * Xmax + y], as that's in fact what happens behind the scenes anyway.
And in C, you can always turn an array reference into a pointer, as an array reference is a reference to a memory location where an array is located and a pointer is a reference to a memory location (regardless if an array is located there or not). So
int a[5] = { ... };
int * b = a;
works as a is a reference to an int array, which are just several int values stored in memory, and b is a pointer to a memory location where an int value is stored. Well, at the address where the array a starts, an int value is stored, so this assignment is perfectly correct.
And m[3] just means "increase the memory address m references three times by the size of the value type m references and fetch the value from that address". And that works and returns the expected value, no matter if m is a pointer or an array reference. So while this syntax is actually intended for array access, it also works with pointers.
The syntax for a pointer would actually be *(m + 3), which means "increase the pointer address three times by the size of the value type it points to and then fetch the value from that address". Yet, just in the first case, this syntax would work with an array reference as well, since an array reference can always become a pointer when required.

Why can't we initialize a 2D integer array as a pointer?

Why can a 2D character array be initialized as a pointer but not as a 2D integer array? Why does it give an error when I try to do so? Also, what does initializing an array as a pointer mean?
#include<stdio.h>
int main()
{
char* m[] = { "Excellent","Good", "bad" };
int* x[] = { {1,2,3},{4,5,6} };
return 0;
}
In the context of a declaration, { and } just mean “here is a group of things.” They do not represent an object or an address or an array. (Note: Within initializations, there are expressions, and those expressions can contain braces in certain contexts that do represent objects. But, in the code shown in the question, the braces just group things.)
In char* m[] = { "Excellent","Good", "bad" };, three items are listed to initialize m: "Excellent", "Good", and "bad". So each item initializes one element of m.
"Excellent" is a string literal. During compilation, it becomes an array of characters, terminated by a null character. In some situations, an array is kept as an array:
When it is used as the operand of sizeof.
When it is used as the operand of unary & (for taking an address).
When it is a string literal used to initialize an array.
None of these apply in this situation. "Excellent" is not the operand of sizeof, it is not the operand of &, and it is initializing just one element of m, not the entire array. So, the array is not kept as an array: By a rule in C, it is automatically converted to a pointer to its first element. Then this pointer initializes m[0]: m[0] is a pointer to the first element of "Excellent".
Similarly, m[1] is initialized to a pointer to the first element of "Good", and m[2] is initialized to a pointer to the first element of "bad".
In int* x[] = { {1,2,3},{4,5,6} };, two things are listed to initialize x. Each of these things is itself a group (of three things). However, x is an array of int *. Each member of x should be initialized with a pointer. But a group of three things, {1,2,3}, is not a pointer.
The C rules on interpreting groups of things when initializing arrays and structures are a bit complicated, because they are designed to provide some flexibility for omitting braces, so I have to study the standard a bit more to explain how they apply here. Suffice it to say that the compiler interprets the declaration as using 1 to initialize x[0]. Since 1 is an int and x[0] is an int *, the compiler complains that the types do not match.
Supplementary Notes
char *m[] does not declare a two-dimensional array. It is an array of pointers to char. Because of C’s rules, it can generally be used syntactically the same way as a two-dimensional array, so that m[i][j] picks out character j of string i. However, there is a difference between char *m[] and char a[3][4], for example:
In m[i][j], m[i] is a pointer. That pointer is loaded from memory and use as the base address for [j]. Then j is added to that address, and the character there is loaded from memory. There are two memory loads in this evaluation.
In a[i][j], a[i] is an array. The location of this array is calculated by arithmetic from the start of a. Then a[i][j] is a char, and its address is calculated by adding j, and the character there is loaded from memory. There is one memory load in this evaluation.
There is a syntax for initialization an array of int pointers to point to an array of int. It is called a compound literal. This is infrequently used:
int *x[] = { (int []) {1, 2, 3}, (int []) {4, 5, 6} };
A crucial difference between these string literals and compound literals is that string literals define objects which exist for the lifetime of program execution, but compound literals used inside functions have an automatic storage duration—it will vanish when your function returns, and possibly earlier, depending on where it is used. Novice C programmers should avoid using compound literals until they understand the storage duration rules.

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