Retrieving 2D array from pointer in C - c

I tried the following code
char arr[5] = {'A', 'E', 'I', 'O', 'U'};
char (*p_arr)[1] = &arr;
printf("%c\n", p_arr[0][4]); //returns 'U'
On this stackoverflow thread,
char (*p_arr2D)[m] = &arr2D //or arr2D
is also a syntax for the decay of a n*m 2D-array arr2D, returning a pointer to its first element, an array of m elements.
p_arr[0] seems to be the "reverse" array decay, retrieving a 2D array from a pointer to an array. How can that happen ? In the code snippet above, is p_arr interpreted as a 1*5 array (from the original 1D-array with 5 elements) ?

a 2D array is stored in memory like a 1D array How are multi-dimensional arrays formatted in memory?
the char (*p_arr)[1] = &arr; creates a pointer to an array of char pointers with one element ( (*p_arr)[1] ) and by the assignment &arr this pointer gets the address of an already existing array with 5 elements
so printf("%c\n", p_arr[0][4]); prints the 5th element ( U ) because by p_arr[0][4] adresses the 5th element in line 1
printf("%c\n", p_arr[1][4]); would give an error
so the reason why this code works is that a 2D array is stored in memory like a 1D array

This declaration
char (*p_arr)[1] = &arr;
is incorrect because the declared pointer and the initializer have incompatible types. There is no implicit conversion from the type char ( * )[5], the type of the initializer, to the type char ( * )[1], the ty[e of the declared pointer. So you need an explicit casting to reinterpret a pointer of one type as a pointer of another type as for example
char (*p_arr)[1] = ( char ( * )[1] )&arr;
As for your question then if you have an object of type T where T is some type
T obj;
and a pointer to the object like
T *ptr = &obj;
then expression *ptr or ptr[0] yields the referenced object of the type T.
So let's assume that you have the following declarations
char arr[5] = {'A', 'E', 'I', 'O', 'U'};
char (*p_arr)[5] = &arr;
For the type char[5] that is the type of the variable arr you can introduce a typedef name.
typedef char T[5];
Then the declarations will look like
T arr = {'A', 'E', 'I', 'O', 'U'};
T *p_arr = &arr;
As it was mentioned above the expression *p_arr or p_arr[0] yields the referenced object of the type T that is of the type char[5].
From the C Standard (6.5.3.2 Address and indirection operators)
4 The unary * operator denotes indirection. If the operand points to a
function, the result is a function designator; if it points to an
object, the result is an lvalue designating the object. If the operand
has type ‘‘pointer to type’’, the result has type ‘‘type’’. If an
invalid value has been assigned to the pointer, the behavior of the
unary * operator is undefined.
And (6.5.2.1 Array subscripting)
2 A postfix expression followed by an expression in square brackets [] is a subscripted designation of an element of an array object. The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))). Because of the conversion rules that apply to the binary + operator, if E1 is an array object (equivalently, a pointer to the initial element of an array object) and E2 is an integer, E1[E2] designates the E2-th element of E1 (counting from zero).
and at last (6.5.6 Additive operators)
7 For the purposes of these operators, a pointer to an object that
is not an element of an array behaves the same as a pointer to the
first element of an array of length one with the type of the object as
its element type.
So in this declaration
char arr[5] = {'A', 'E', 'I', 'O', 'U'};
char (*p_arr)[5] = &arr;
the pointer p_arr does not point to an array element. However it can behaves as it points to the first element of an array of length 1. You can imagine it the following way
char arr[1][5] = { {'A', 'E', 'I', 'O', 'U'} };
char (*p_arr)[5] = arr;
So the expression p_arr[0] gives the first element of the imagined two-dimensional array that is an element of the type char[5].

To begin with, the line char (*p_arr)[1] = &arr; is not valid C. Your compiler must give a diagnostic message. p_arr is an array pointer to an array of 1 char, but you assign it to point at an array of 5 char.
As for why it seems like you have a 2D array - you don't have one - is because p_arr[0] works like any other pointer arithmetic, giving you pointed-at item "offset + 0". Which is the array. And then you access item 4 in the array.
A simple example of the very same pointer arithmetic principle would be this:
int a;
int* ptr = &a;
ptr[0] = whatever; // perfectly valid C
ptr[0] is guaranteed to be equivalent to *(ptr+0).
Correct use of the array pointer should be printf("%c\n", (*p_arr)[4]). In this case it happened to work, but in other cases you might invoke undefined behavior, since p_arr is not allowed to point beyond the allocated memory.

You are declaring p_arr to be a pointer to an array of chars (and that array is of length one which is a sure sign of a problem).
You may want the much simpler syntax:
char *p_arr = arr;
or maybe:
#include <stdio.h>
int main() {
char arr[] = {"Hello world"};
char *p[3] = {NULL, arr, NULL};
printf("%s %c\n", p[1], p[1][3]);
}
John

Related

Compiler error while incrementing the address in a character array [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is array name a pointer in C?
Suppose I have a char array say arr and arr will represent the address of first element so arr++ should be perfectly legal then why compiler says 'lvalue required'.
Also if I do:
arr=arr+1 then why it is invalid conversion. I am just increasing pointer by one. Compiler tells that on LHS the operand type is char[4] but on RHS it is char *.
main()
{
char arr[]={'a','b','c','d'};
for(;arr!=arr+4;arr++) //lvalue required
printf("%c",*arr);
}
An array name is not a variable that can be assigned to. If you want to modify it, you should use a pointer variable:
char *arr_ptr = arr;
You can then do arr_ptr++ and arr_ptr = arr_ptr+1;
Arrays aren't pointers. arr does not represent an address.
An array name, or any expression of array type, is implicitly converted to a pointer to the array's first element unless it's either:
The operand of a unary & (address-of) expression (which yields the address of the whole array, not of its first element -- same memory address, different type); or
The operand of a sizeof operator (sizeof arr yields the size of the array, not the size of a pointer); or
The operand of an _Alignof operator (_Alignof arr yields the alignment of the array, not the alignment of a pointer); or
A string literal in an initializer that's used to initialize an arrary object.
_Alignof is new in C2011.
(The conversion is often referred to as a "decaying".)
None of these apply here, so arr++ tries to increment a pointer object. The problem is, there is no pointer object, just a pointer value. arr, after it decays to a pointer value, is not an lvalue, which means it cannot appear on the left side of an assignment or as the operand of ++.
Similarly, given:
int x;
the expression x is an lvalue, so you can write x = 42; -- but the expression (x + 1) is not an lvalue, so you can't write (x + 1) = 42;. You can assign to x because it refers to an object. (x+1) doesn't refer to an object, so there's nothing to assign to. arr, after it decays, doesn't refer to an object either (you have an array object, but there's no pointer object).
(Your use of arr != arr + 4 should have been a clue; that can never be true.)
Rather than this:
char arr[] = {'a', 'b', 'c', 'd'};
for (; arr != arr + 4; arr++) {
printf("%c", *arr);
}
you can write this:
char arr[] = {'a', 'b', 'c', 'd'};
char *ptr;
for (ptr = arr; ptr != arr + 4; ptr++) {
printf("%c", &ptr);
}
Incidentally, at the top of your program, you should change this:
main()
to this:
#include <stdio.h>
int main(void)
Also, run, do not walk, to the comp.lang.c FAQ and read section 6, "Arrays and pointers".

Initializing an array of char pointers in 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'}.

Why isn't this pointer arithmetic allowed in C? [duplicate]

This question already has answers here:
Is array name a constant pointer in C++?
(2 answers)
Closed 6 years ago.
char arr[] = "Hello";
arr = arr + 1; // error occurs
As far as I know, an expression that has array type is converted to pointer type that points to the initial element of the array. Therefore, I expected arr = arr + 1 (pointer to first element(arr) of the array becomes the pointer to the second element of the array)to work. Why doesn't this work in C?
arr + 1 is indeed a pointer to the second element of the array (i.e. &arr[1]).
However, that does not mean that you can somehow write that pointer value back into arr. You can't do it for at least two reasons.
Firstly, arr is an array of char elements, not a pointer. There's an obvious type mismatch here.
Secondly, being an array, arr a non-modifiable lvalue. You cannot change arr itself, you can only change its elements (this distinction is somewhat hard to grasp, but it is there).
Finally, if we just ignore the deeper intricacies and focus on what formally happens at the top level, due to array type decay your expression is equivalent to
(char *) arr = (char *) arr + 1;
The assignment is impossible since the left-hand side is a result of [implicit] type conversion. In C type conversions always produce rvalues. You cannot assign to rvalues.
In other words, it is not the "pointer arithmetic" that's disallowed here. The pointer arithmetic is fine. It is what you do with the result of that pointer arithmetic that causes the error.
Arrays are non-modifiable lvalues. They can't be the left operand of an assignment operator.
C11-§6.3.2.1:
A modifiable lvalue is an lvalue that
does not have array type, does not have an incomplete type, [...]
§6.5.16/2:
An assignment operator shall have a modifiable lvalue as its left operand.
In the statement
arr = arr + 1;
arr is the left operand of = operator and is of array type. It can't be modified.
So, it's not the pointer arithmetic but the constraint by the language on the assignment operator that is the reason for syntactical error.
Note that in some contexts arrays decay to a pointer to its first element, though pointers and arrays are different types. Arrays are not pointers. It is only the pointer arithmetic and array indexing which are equivalent. For example
char *ptr = &arr[0] + 1 => &(*(arr + 0)) + 1 => &(*arr) + 1 => arr + 1 // * and & nullify each other
This is because arrays are similar to pointers except that they can not be modified. However you can modify a pointer that is pointing to an array. For the above example you can do like this:
char arr[]="Hello";
char *ptr=arr;
ptr=ptr+1;
Initially the pointer ptr will be pointing to the first character of the array i.e. 'H' and after modifying the value it will point to the second character i.e. 'e'. You can also do the following:
char arr[]="Hello";
char *ptr=arr;
ptr=arr+1;
Both produce the same effect which shows that arr+1 is indeed pointer arithmetic. However you can not modify the value of arr because its type is that of a character array and not pointer to a character array.
As far as I know, an expression that has array type is converted to pointer type that points to the inital element of the array.
It is true in most contexts. It is not true in the following contexts:
When using the addressof operator (&arr). The type of &arr is char (*)[6]. It is not char**.
When using the sizeof operator. sizeof(arr) is 6. Had it been a pointer, it would be the size of a pointer (4 or 8 in most common platforms).
When used as the LHS of an assignment operator. A variable of array type is not modifiable.
Therefore, I expected arr = arr + 1 (pointer to first element(arr) of the array becomes the pointer to the second element of the array)to work. Why doens't this work in C?
The RHS of the expression evaluates to a pointer to the second element of arr. However, that line does not work due to (3) above. arr is not a modifiable value. It cannot be used as the LHS of an assignment operator.
char[] is not pointer, while char* is a pointer.
This works, but it is wrong solution:
int main()
{
char *arr = "Hello";
arr = arr + 1; // Wrong!
printf("%s\n", arr); // Output: ello
}
If arr is heap-allocated with malloc you can get memory leak if free memory starting arr+1 not arr.
But you can do something like this:
int main()
{
char arr[] = "Hello";
char *wordFromSecondLetter = &arr[0] + 1;
printf("%s\n", wordFromSecondLetter); // Output: ello
}
Or like this
int main()
{
char arr[] = "Hello";
printf("%s\n", &arr[1]); // Output: ello
}
Because arr is not a pointer but a char array. You can verify this by checking sizeof arr. To get a pointer to char, you should use char *arr = "Hello";.
The biggest difference between a pointer and an array is that you can directly assign a value to a pointer, but you can't do this to an array.
In fact, when you write arr + 1, arr "decays" to the pointer to its first element, that is to say, arr == &arr[0]. So arr + 1 is legal pointer arithmetic, but giving its value to arr, which is an array, is illegal.

"lvalue required" error when trying to increment array [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is array name a pointer in C?
Suppose I have a char array say arr and arr will represent the address of first element so arr++ should be perfectly legal then why compiler says 'lvalue required'.
Also if I do:
arr=arr+1 then why it is invalid conversion. I am just increasing pointer by one. Compiler tells that on LHS the operand type is char[4] but on RHS it is char *.
main()
{
char arr[]={'a','b','c','d'};
for(;arr!=arr+4;arr++) //lvalue required
printf("%c",*arr);
}
An array name is not a variable that can be assigned to. If you want to modify it, you should use a pointer variable:
char *arr_ptr = arr;
You can then do arr_ptr++ and arr_ptr = arr_ptr+1;
Arrays aren't pointers. arr does not represent an address.
An array name, or any expression of array type, is implicitly converted to a pointer to the array's first element unless it's either:
The operand of a unary & (address-of) expression (which yields the address of the whole array, not of its first element -- same memory address, different type); or
The operand of a sizeof operator (sizeof arr yields the size of the array, not the size of a pointer); or
The operand of an _Alignof operator (_Alignof arr yields the alignment of the array, not the alignment of a pointer); or
A string literal in an initializer that's used to initialize an arrary object.
_Alignof is new in C2011.
(The conversion is often referred to as a "decaying".)
None of these apply here, so arr++ tries to increment a pointer object. The problem is, there is no pointer object, just a pointer value. arr, after it decays to a pointer value, is not an lvalue, which means it cannot appear on the left side of an assignment or as the operand of ++.
Similarly, given:
int x;
the expression x is an lvalue, so you can write x = 42; -- but the expression (x + 1) is not an lvalue, so you can't write (x + 1) = 42;. You can assign to x because it refers to an object. (x+1) doesn't refer to an object, so there's nothing to assign to. arr, after it decays, doesn't refer to an object either (you have an array object, but there's no pointer object).
(Your use of arr != arr + 4 should have been a clue; that can never be true.)
Rather than this:
char arr[] = {'a', 'b', 'c', 'd'};
for (; arr != arr + 4; arr++) {
printf("%c", *arr);
}
you can write this:
char arr[] = {'a', 'b', 'c', 'd'};
char *ptr;
for (ptr = arr; ptr != arr + 4; ptr++) {
printf("%c", &ptr);
}
Incidentally, at the top of your program, you should change this:
main()
to this:
#include <stdio.h>
int main(void)
Also, run, do not walk, to the comp.lang.c FAQ and read section 6, "Arrays and pointers".

C pointer : array variable

I read this in my book (and many sources on the internet):
The array variable points to the first element in the array.
If this true, then the array variable and the first element are different. Right?
It means by below code, it will produce two different results:
int main(){
char msg[] = "stack over flow";
printf("the store string is store at :%p\n",&msg);
printf("First element: %p\n",&msg[0]);
}
But I receive the same results for the two cases. So, by this example, I think we should say: the array variable is the first element. (because it has the same address)
I don't know if this true or wrong. Please teach me.
The array variable signifies the entire memory block the array occupies, not only the array's first element. So array is not the same as array[0] (cf. sizeof array / sizeof array[0]). But the array's first element is located at the same memory address as the array itself.
Saying the array points to the first element is also incorrect, in most circumstances, an array expression decays into a pointer to its first element, but they are different things (again cf. sizeof for example).
They point to the same address, i.e. printf will show the same value but they have different types.
The type of &msg is char(*)[16], pointer to array 16 of char
The type of &msg[0] is char *, pointer to char
A cheap way to test this is to do some pointer arithmetic. Try printing &msg + 1.
This C FAQ might prove useful.
The array variable is the whole array. It decays into a pointer to the first element of the array.
If you look at the types:
msg is of type char [16]
&msg is of type char (*)[16]
&msg[0] is of type char *
So in a context where msg can decay into an array, for example when passed as an argument, its value would be equal to &msg[0].
Let me draw this:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+--+
|s|t|a|c|k| |o|v|e|r| |f|l|o|w|\0|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+--+
Imagine the starting point of this array, where 's' is located is address 0x12345678.
msg itself, refers to the whole 16 bytes of memory. Like when you say int a;, a refers to 4 bytes of memory.
msg[0] is the first byte of that 16 bytes.
&msg is the address where array begins: 0x12345678
&msg[0] is the address of first element of array: 0x12345678
This is why the values of &msg and &msg[0] are the same, but their types are different.
Now the thing is, msg by itself is not a first class citizen. You cannot for example assign arrays. That is why, in most of the cases, the array will decay into its pointer.
If you know function pointers, this is very similar:
int array[10];
int function(int);
In int *var = array, array decays to a pointer (&array)
In void *var = function, function decays to a pointer (&function)
Note that, in case of function pointers, we like to keep the type, so we write:
int (*var)(int) = function;
Similarly, you can do with arrays:
int (*var)[10] = array;
char myChar = 'A'
char msg[] = 'ABCDEFGH'
When you type myChar you get value.
But with msg you get pointer to first char(for values you have to use msg[x])
msg = &msg[0]
This can help you to understand, I think.
Look at it this way:
&msg = 0x0012
&msg[0] = 0x0012
&msg[1] = 0x0013
In this case &msg[1] is pointing to msg+1. When you reference &msg or &msg[0] you are referring to the same address of memory because this is where the pointer starts. Incrementing the array variable will increment the pointer by +1 since a char variable is only 1 byte in size.
If you do the same trick with say an integer you will increment the pointer by +4 bytes since an integer is 4 bytes in size.
When you use an array expression, the compiler converts it to a pointer to the first element. This is an explicit conversion specified by the 1999 C standard, in 6.3.2.1 3. It is a convenience for you, so that you do not have to write &array[0] to get a pointer to the first element.
The conversion happens in all expressions except when an array expression is the operand of sizeof or the unary & or is a string literal used to initialize an array.
You can see that an array and its first element are different by printing sizeof array and sizeof array[0].
In most circumstances, an expression of array type ("N-element array of T") will be replaced with / converted to / "decay" to an expression of pointer type ("pointer to T"), and the value of the expression will be the address of the first element in the array.
So, assuming the declaration
int a[10];
the type of the expression a is "10-element array of int", or int [10]. However, in most contexts, the type of the expression will be converted to "pointer to int", or int *, and the value of the expression will be equivalent to &a[0].
The exceptions to this rule are when the array expression is the operand of the sizeof or unary & operators, or is a string literal being used to initialize another array in a declaration.
So, based on our declaration above, all of the following are true:
Expression Type Decays to Value
---------- ---- --------- -----
a int [10] int * address of the first element of a
&a int (*)[10] n/a address of the array, which is the
same as the address of the first
element
&a[0] int * n/a address of the first element of a
*a int n/a value of a[0]
sizeof a size_t n/a number of bytes in the array
(10 * sizeof (int))
sizeof &a size_t n/a number of bytes in a pointer to
an array of int
sizeof *a size_t n/a number of bytes in an int
sizeof &a[0] size_t n/a number of bytes in a pointer to int
Note that the expressions a, &a, and &a[0] all have the same value (address of the first element of a), but the types are different. Types matter. Assume the following:
int a[10];
int *p = a;
int (*pa)[10] = &a;
Both p and pa point to the first element of a, which we'll assume is at address 0x8000. After executing the lines
p++;
pa++;
however, p points to the next integer (0x8004, assuming 4-byte ints), while pa points to the next 10-element array of integers; that is, the first integer after the last element of a (0x8028).

Resources