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

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.

Related

Why cannot we change the base address of an array [duplicate]

This question already has answers here:
Why can't I increment an array?
(5 answers)
Is an array name a pointer?
(8 answers)
Closed 1 year ago.
I have an array arr say
int arr[3] = {1, 2, 3};
so as per my knowledge this arr is just like any variable that has a name arr just like
int a = 4;
a is an integer variable that asks 4 bytes of memory. And arr asks 3 * 4
= 12 bytes of memory and arr has properties like 12 bytes of memory and when used as an Rvalue it decays to a
(pointer value not a pointer variable)
so as it decays to a pointer value like &arr[0] we can use a pointer variable to point (grab the address)
int* ptr = arr; //here arr implicitly converts to &arr[0]
so here arr and ptr have the same address but ptr is like copied the memory of &arr[0] and stores it. Whereas arr is literally the address in the memory which only decays like &arr[0] when used as Rvalue.
if we do something like
arr++;
complier throws error as Expression must be a modifiable lvalue
As the arr is allocated on the stack we cannot change the address
because we are asking cpu to change the address of the array, if we change it we loose the array address and cannot further access it
Code
#include <stdio.h>
int main()
{
int arr[3] = { 1, 2, 3 };
arr++; // not allowed
int* p = arr;
p++; // allowed because its a copy of (arr) address.
return 0;
}
That's how what i know and correct me with more resources.
arr is not a pointer, it's an array. When you use the word arr the compiler may convert it to a pointer for you - it does &arr[0] automatically, and gets the address of the first element, which is a pointer value, not a pointer variable. And you can't change values. You can't do (&some_variable)++;, you can't do 5++;, and you can't do (&arr[0])++; which is what arr++; would mean.
Arrays are not pointers.
Consider the following demonstrative program.
#include <stdio.h>
int main(void)
{
int arr[3] = { 1, 2, 3 };
int *p = arr;
printf( "sizeof( arr ) = %zu\n", sizeof( arr ) );
printf( "sizeof( p ) = %zu\n", sizeof( p ) );
return 0;
}
Its output might look like
sizeof( arr ) = 12
sizeof( p ) = 8
Arrays are non-modifiable lvalues. That means that you for example can not assign one array to another or apply an increment operator.
On the other hand, arrays used in expressions are implicitly converted (with rare exceptions) to pointers to their first elements.
From the C Standard (6.3.2.1 Lvalues, arrays, and function designators)
3 Except when it is the operand of the sizeof operator or the unary &
operator, or is a string literal used to initialize an array, an
expression that has type ‘‘array of type’’ is converted to an
expression with type ‘‘pointer to type’’ that points to the initial
element of the array object and is not an lvalue. If the array object
has register storage class, the behavior is undefined.
So for example in this declaration
int *p = arr;
the array arr used as an initializer expression is implicitly converted to pointer to its first element that has the type int *.
To set the pointer p to point to the second element of the array arr you could use the pointer arithmetic the following way
int *p = arr + 1;

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".

Pointers to array of pointers in c

Can someone please explain me what is the difference between the two following declarations:
char (*arr_a)[5];
char arr_b[20];
and why:
sizeof (*arr_b) = sizeof (char)
sizeof (*arr_a) = 5*sizeof(char)
char (*arr_a)[5];
declares a pointer to a 5-element array of char.
char arr_b[20];
declares just a 20-element array of char.
So, the output of
sizeof (*arr_a)
should be straight forward -- dereferencing the pointer to an array yields the array and it's size is 5.
The following:
sizeof (*arr_b)
gives 1, because dereferencing the identifier of an array yields the first element of that array, which is of type char.
One thing you need to know to fully understand this is how an array evaluates in an expression:
In most contexts, the array evaluates to a pointer to its first element. This is for example the case when you apply indexing to the array. a[i] is just synonymous to *(a+i). As the array evaluates to a pointer, this works as expected.
There are exceptions, notably sizeof, which gives you the storage size of the array itself. Also, _Alignof and & don't treat the array as a pointer.
arr_a is a pointer to an array of 5 char while arr_b is an array of 20 chars. arr_b is not a pointer unlike arr_a.
sizeof (*arr_b) equals to sizeof (char) because *arr_b is of type char (equivalent to arr_b[0]). For
sizeof (*arr_a) equals to 5*sizeof(char) because *arr_a refers to an array of 5 chars and sizeof returns the size of array which is 5.

Reference to Array vs reference to array pointer

void check(void* elemAddr){
char* word = *((char**)elemAddr);
printf("word is %s\n",word);
}
int main(){
char array[10] = {'j','o','h','n'};
char * bla = array;
check(&bla);
check(&array);
}
Output:
word is john
RUN FINISHED; Segmentation fault; core dumped;
First one works, but second not. I don't understand why this happens.
The problem is, when we do &array, we are getting a char (*)[10] from an char [10], instead of a char **.
Before we do our experiment, I will emphasize that, when we pass an array as an argument to a function, C actually casts the array to a pointer. The big bucket of data is not copied.
Thus, int main(int argc, char **argv) is identical to int main(int argc, char *argv[]) in C.
This made it available for us to print the address of an array with a simple printf.
Let's do the experiment:
char array[] = "john";
printf("array: %p\n", array);
printf("&array: %p\n", &array);
// Output:
array: 0x7fff924eaae0
&array: 0x7fff924eaae0
After knowing this, let's dig into your code:
char array[10] = "john";
char *bla = array;
check(&bla);
check(&array);
bla is char *, and &bla is char **.
However, array is char [10], and &array is char (*)[10] instead of char **.
So when you pass &array as an argument, char (*)[10] acts like a char * when passing as an argument, as is said above.
Therefore **(char **) &bla == 'j' while *(char *) &array == 'j'. Do some simple experiments and you will prove it.
And you are casting void *elemAddr to a char ** and try to deference it. This will only work with &bla since it is char **. &array will cause a segfault because "john" is interpreted as an address as you do the cast.
For check(&bla); you are sending pointer to pointer
void check(void* elemAddr){
char* word = *((char**)elemAddr); // works fine for pointer to pointer
printf("word is %s\n",word);
}
This is working fine.
But, for check(&array); you are passing pointer only
void check(void* elemAddr){
char* word = *((char**)elemAddr); // This is not working for pointer
char* word = *(char (*)[10])(elemAddr); // Try this for [check(&array);]
printf("word is %s\n",word);
}
Full Code--
Code for check(array);:
void check(void* elemAddr){
char* word = *(char (*)[10])(elemAddr);
printf("word is %s\n",word);
}
int main() {
char array[10] = {'j','o','h','n'};
check((char*)array);
return 0;
}
Code for check(&bla);:
void check(void* elemAddr){
char* word = *((char**)elemAddr);
printf("word is %s\n",word);
}
int main() {
char array[10] = {'j','o','h','n'};
char* bla = array;
check(&bla);
return 0;
}
The C specification says that array and &array are the same pointer address.
Using the name of an array when passing an array to a function will automatically convert the argument to a pointer per the C specification (emphasis mine).
6.3.2.1-4
Except when it is the operand of the sizeof operator or the unary &
operator, or is a string literal used to initialize an array, an
expression that has type ‘‘array of type’’ is converted to an
expression with type ‘‘pointer to type’’ that points to the initial
element of the array object and is not an lvalue. If the array object
has register storage class, the behavior is undefined.
So calling func(array) will cause a pointer to char[] to be passed to the function. But there is a special case for using the address-of operator on an array. Since array has type "array of type" it falls into the 'Otherwise' category of the specification (emphasis mine).
6.5.3.2-3
The unary & operator yields the address of its operand. If the operand
has type ‘‘type’’, the result has type ‘‘pointer to type’’. If the
operand is the result of a unary * operator, neither that operator nor
the & operator is evaluated and the result is as if both were omitted,
except that the constraints on the operators still apply and the
result is not an lvalue. Similarly, if the operand is the result of a
[] operator, neither the & operator nor the unary * that is implied by
the [] is evaluated and the result is as if the & operator were
removed and the [] operator were changed to a + operator. Otherwise,
the result is a pointer to the object or function designated by its
operand
So calling func(&array) will still cause a single pointer to be passed to the function just like calling func(array) does since both array and &array are the same pointer value.
Common-sense would lead you to believe that &array is a double pointer to the first element of the array because using the & operator typically behaves that way. But arrays are different. So when you de-reference the passed array pointer as a double pointer to the array you get a Segmentation fault.
This is not a direct answer to your question, but it might be helpful to you in the future.
Arrays are not pointers:
type arr[10]:
An amount of sizeof(type)*10 bytes is used
The values of arr and &arr are necessarily identical
arr points to a valid memory address, but cannot be set to point to another memory address
type* ptr = arr:
An additional amount of sizeof(type*) bytes is used
The values of ptr and &ptr are typically different, unless you set ptr = (type*)&ptr
ptr can be set to point to both valid and invalid memory addresses, as many times as you will
As with regards to your question: &bla != bla == array == &array, and therefore &bla != &array.
One problem is that your char array is NOT NECESSARILY going to be null-terminated. Since array is an automatic variable that is allocated locally on the stack, it is not guaranteed to be zeroed-out memory. So, even though you are initializing the first 4 chars, the latter 6 are left undefined.
However ...
The simple answer to your question is that &bla != &array so your check() function is assuming it will find null-terminated character arrays at 2 different addresses.
The following equations are true:
array == &array // while not the same types exactly, these are equivalent pointers
array == bla
&array == bla
*bla == array[0]
&bla is never going to equal anything you want because that syntax references the address of the bla variable on the local stack and has nothing to do with its value (or what it points to).
Hope that helps.

"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".

Resources