What is the difference between (int*) arr[2] and int* arr[2]? - c

Does (int*) arr[2] have to with typecasting? If yes, where is it used?
This problem arose when I tried to compile the follwing code :
int* arr[2];
int arr1[] = { 1,2};
int arr2[] = { 1,6};
arr[0] = arr1;
arr[1] = arr2;
by replacing
int* arr[2] with (int*)arr[2]
Thanks.

First one makes arr as an array of pointers to int. So your arr is a variable. That is a declaration line.
The second one: assuming that arr is a an array (which was allready declared!), takes the value which is arr[2] and casts a pointer to int type on it.

The problem with replacing int *arr[2] with (int *) arr[2] in your context is that the latter no longer conforms to the C grammar for declarations. According to the structure of C grammar, in int *arr[2] the * is attached to arr, not to int. Forcing it towards int by using extra () violates the suffix of a declaration.
You are allowed to use () in C declarations as long as it doesn't violate the syntactic structure of a declaration imposed by C grammar. For example, you can do
int *(arr)[2]; // same as `int *arr[2]`
or
int *(arr[2]); // same as `int *arr[2]`
or
int (*arr)[2]; // different from `int *arr[2]`, but still valid
but not your (int *) arr[2] or (int) *arr[2]. The latter are not declarations.

Related

Use of & in declaring pointers to array

To make a pointer to a whole array we proceed like that:
int arr[3] = {1,2,3};
int (*p)[3] = &arr;
How come i get an incompatibility error when trying to do the same with a 2D array?
int arr[3][3] = {{12,10,45}, {44,55,66}, {79,85,91}};
int (*p)[3] = &arr;
The problem here is &.
I'm sure this is a simple question that might have already been answered but i don't find any answer to this specific issue concerning the use of &.
In C, "pointer" is a category of types. To get a particular type, you have to specify what type of object the pointer points to.
Accordingly, the unary & does not generically compute a pointer. It creates a pointer to the type of its operand.
Similarly, in C, "array" is a category of types. To get a particular type, you have to specify the element type, the number of dimensions, and the sizes of at least the last n-1 dimensions.
Thus, with
int arr[3] = {1,2,3};
int (*p)[3] = &arr;
arr is defined as an array of 3 int, therefore
&arr has type pointer to array of 3 int (spelled int (*)[3] as a C type name), and
p is declared with that type
so everything is consistent.
How come i get an incompatibility error when trying to do the same with a 2D array?
Because your "the same" is not analogous.
With
int arr[3][3] = {{12,10,45}, {44,55,66}, {79,85,91}};
int (*p)[3] = &arr;
, arr has type array of 3 array of 3 int, and &arr has type pointer to array of 3 array of three int (a.k.a. int (*)[3][3]). This is not the same type that p has. The appropriate declaration of p for this version of arr would be
int (*p)[3][3] = &arr;
You've got p in the second example as a pointer to a 1D array, not a pointer to a 2D array. For that, you need the following:
int (*p)[3][3] = &arr;
arr has type int [3][3], so &arr has type int (*)[3][3]. So you can use:
int arr[3][3] = {{12,10,45}, {44,55,66}, {79,85,91}};
int (*p)[3][3] = &arr;
You can then access p as (*p)[i][j].
But it would be more typical to use:
int arr[3][3] = {{12,10,45}, {44,55,66}, {79,85,91}};
int (*p)[3] = arr;
In this case, int [3][3] is compatible with an int (*)[3] pointer, and you can use p[i][j] when accessing it.
An analogous situation exists with one-dimensional arrays. If you have int x[3], then the usual pointer type to use is int *p, in which case you would do p = x and access it as p[i].
But if you really want the extra level of indirection, you could use:
int (*p)[3] = &x;
Then instead of p[i], you would now need to use (*p)[i] when accessing it.

Proper way to read 'int * ptr=&x'

The following code is easily interpreted as
int x=4;
int *ptr; // ptr is a pointer to an int
ptr=&x; // the address of x is assigned to the pointer ptr
But the following equivalent code, may read misleadingly like
int *ptr=&x; // the address of x is assigned to *ptr, which is an integer
that is it seems to be interpreted as assigning the address of x to the pointed value *ptr (int *ptr )= &x
The correct interpretation as the one when declaration and initialization are separated should be written something like int *(ptr = &x),to make evident that the assignment is to the pointer and not to the pointed location but this gives an error, why is that? And what is the best way to read and think of int *ptr=&x?
This is, admittedly a weird part of C and it's mostly due to C's evolution.
In C's syntax, declarations aim to mirror use, so that after int *x;, *x resolves to an int and *x=42 assigns to that int. But initializations, which are started with = after the specifiers declarator part of a declaration, are syntactically and semantically different from an assignments.
(Initialization can "assign" to static/filescope variables and such an assignment generates no code: it simply contributes to the makeup of the resulting binary. Assignments always generate code unless optimizations can delete it)
Initializations and assignments used to be very differently looking in prehistoric C, where you'd initialize without the = sign as in int x 42;. (Even after the = was added to the syntax of initializations, it was long impossible to initialize local, nonstatic, variables which meant situations such as int *p = &x; didn't arise all that often.)
The old syntax had problems (would int x (42); also declare and initialize x or would it be a function declaration?) and that's why it was replaced, but I like how it emphasized that initializations are different from assignments.
Unfortunately with the new syntax (as in int x, *p = &x;) this distinction is not all that apparent, and you simply have to remember that when you have type specifiers (int) at the left end, then the = does not denote an assignment where you can just look at *p = &x but rather that it's an initialization where you have to look at the whole declaration and see what's declared (x as an int and p as a pointer to int) . The = in that context then initializes the declared identifier.
int * is the type, so it makes perfect sense. It's just that pointer notation in C can take some effort to get used to. But look at this code
typedef int* int_ptr;
int x;
int_ptr ptr = &x;
Same thing. However, it's often advised to NOT typedef pointers.
The confusion comes from that * serves two roles. It is BOTH to name a type AND to dereference a pointer. An example of the first is sizeof(int*)
If you declare several pointers at once it looks messier, but it's still the same. In general, it's recommended to not declare more than one pointer at once. Because if we would like to do the above thing with two pointers, it would look like this:
int *pa = &x, *pb = &x;
That's the same as
int *pa = &x;
int *pb = &x;
And this does something completely different and will generate a warning because you're assigning the address of a variable to the pb variable that has type int
int *pa = &x, pb = &x;
However, using the typedef from above, you can (but probably shouldn't) do this:
int_ptr pa=&x, pb = &x;
But one way to think of it is that it makes no sense at all in any situation to dereference an uninitialized pointer.
And what is the best way to read and think of int *ptr=&x?
Take the fact that it does not make sense to dereference an uninitialized pointer. And you're doing an initialization of a pointer, and therefore you should initialize it with a (valid) address.
Ok, I see that, another thing. If the type is int* why is it almost always written like with the * next to the pointer variable instead, like int *ptr?, it would make more sense, even if it is the same to write it like int* ptr.
Because, then it would be MUCH easier to forget the asterisk if you declare several pointers at once. That would give the impression that int* p,q; declares two pointers.
C pointer syntax is clunky. It's a very old language. Just get used to it. It will never change. Just for fun, here is a page that can tell what a declaration is https://cdecl.org/ so try these:
int (*p)[3]
int *p[3]
const int *p[3]
int *const p[3]
const int (*p)[3]
int (*const p)[3]
You could write
int ( *ptr ) = &x;
or even like
int ( * ( ptr ) ) = &x;
Though this record
int *ptr = &x;
is clear for each C programmer because it is a declaration and not an assignment statement. Moreover in C opposite to C++ declarations are not statements.
That is 1) you may not enclose a declaration in parentheses as you wrote (int *ptr )= &x and 2) you may enclose a declarator in parentheses.
If to follow your logic then you should write the declaration like
int *p = ( p = &x );
that makes the declaration more confusing.:)
Here is a demonstrative program that shows some examples of declarations and initializations of a pointer.
#include <stdio.h>
int main(void)
{
int x = 10;
int *p1 = &x;
int ( *p2 ) = &x;
int ( *( p3 ) ) = &x;
int typedef *T;
T ( p4 ) = &x;
printf( "*p1 = %d\n", *p1 );
printf( "*p2 = %d\n", *p2 );
printf( "*p3 = %d\n", *p3 );
printf( "*p4 = %d\n", *p4 );
return 0;
}
The program output is
*p1 = 10
*p2 = 10
*p3 = 10
*p4 = 10
Pay attention to that in C as in many other languages some symbols are overloaded and their meanings depend on the context. For example the symbol & can denote the address of operator and the bitwise AND operator and by the way in C++ this symbol also can denote a reference.
Compare the following.
int n = 5; // (a) defines 'int' variable 'n' and initializes it to '5'
int *p; // (b) defines 'int*' pointer variable 'p`
p = &n; // initializes 'p' to '&n'
int *p1 = p; // (c) defines 'int*' pointer variable 'p1' and initializes it to 'p'
// syntactically, it looks just like (a), but for a pointer type
int *p2 = &n; // (d) same as (b) and (c) combined into a one-liner
int n3 = 7, *p3 = &n3; // (e) same as (a), (b) and (c) combined into a one-liner
when declare a pointer, int p is equal to int p; int* is a type.

Passing array by ref causes compiler warnings - How to use pointer-arithmetic correct here?

void foo(int **arr)
{
**arr = 5; // works fine, no warnings and myArray[0] is 5 after call.
*(arr+5) = 5; //warning - assignment makes pointer from integer without a cast - why?
*(arr)[5] = 5; //No warnig but programm would crash
}
int main()
{
int *myArray = (int*)calloc(10,sizeof(int));
foo(&myArray); //no warning but myArray[5] would be still 0
foo(myArray); //warning - passing argument 1 of 'foo' from incompatible pointer type (but works fine)
printf("%d",myArray[5]);
return 0;
}
How to pass the array correctly to my function and access myArray[5] without any warnings?
As written, the proper way to index into arr would be
(*arr)[5] = 5;
Since arr is a pointer to a pointer to your array, you don’t want to index into arr, you want to index into what arr points to. You need to explicitly group the * with arr since postfix operators like [] have higher precedence than unary *.
Having said that, the only reason to pass a pointer to myArray is if you expect to change the value of myArray itself, such as with a call to realloc. If that’s not the intent, then it’s better to write the function as Antti and Peter have shown.
Since foo takes a pointer to a pointer to integer, calling foo(&myArray) is correct here. But you don't need to do that at all. Simply pass in myArray and have foo take a pointer to int instead:
void foo(int *arr)
{
arr[5] = 5;
}
int main()
{
int *myArray = calloc(10, sizeof(int)); // no need to cast here unless compiling with a C++ compiler
foo(myArray);
printf("%d", myArray[5]); // prints 5
return 0;
}
I know pointers can be confusing, but there seems to be a very fundamental misunderstanding here so I recommend carefully reading the pointer section of any good C textbook again.
Like this:
#include <stdio.h>
#include <stdlib.h>
void foo(int *arr) {
arr[5] = 5;
}
int main(void) {
int *myArray = calloc(10, sizeof(int));
foo(myArray);
printf("%d", myArray[5]);
}
You only need to pass the pointer by reference if you want to change the value of the original pointer object (i.e. if you want to make the pointer stored in myArray point to another allocated memory block after calling foo).

Weird pointer to array syntax

What is the logic motivating int (*arr)[5]; to declare arr as a pointer to an array of 5 elements, whereas int *arr[5] declares arr as an array of 5 pointers ? Especially given that * has low priority (unsure about this part since it isn't used as the dereferencing operator) ?
In both declarations and expressions, postfix operators like [] have higher precedence than unary operators like *, so a declaration like
T *a[N];
is parsed as
T *(a[N]);
Thus
a -- a is
a[N] -- an N-element array of
*a[N] -- pointer to
T *a[N]; -- T
If you want to declare a as a pointer to an array, then you need to explicitly group the * operator with a:
T (*a)[N];
Thus:
a -- a is
(*a) -- a pointer to
(*a)[N] -- an N-element array of
T (*a)[N]; -- T
As luck would have it, you're far more likely to be using an arrays of pointers than pointers to arrays, so it makes sense that the "simpler" form of declaration results in that type.
You've already stated the reason: * has lower precedence than [].
So
int *a[5];
is
int *(a[5]);
meaning that a is an array first, and what it's an array of is pointers.
To me, this is a fine result. I declare arrays of pointers all the time, and int a[5] is convenient syntax. Trying to type int (*a)[5] feels very strange, but that's okay, because I never declare pointers to arrays (because they're virtually never useful).
What is the logic motivating int (*arr)[5];
It depends on the context. For example if you have a two-dimensional array then you can use a pointer of such a type as an iterator.
Here is a demonstrative program
#include <stdio.h>
int main( void )
{
int a[2][5] =
{
{ 0, 1, 2, 3, 4 },
{ 5, 6, 7, 8, 9 }
};
for (int(*row)[5] = a; row != a + 2; ++row)
{
for (int *col = *row; col != *row + 5; ++col)
{
printf("%d ", *col);
}
putchar('\n');
}
return 0;
}
Or if you have a function that declares its parameter as a two-dimensional array as for example
void f( int a[][5], size_t n );
then the parameter implicitly is adjusted to pointer of the type int ( * )[5]
Take into account that if you have an array like this
int * a[5];
then a pointer to the array will look
int * ( *p )[5] = &a;
While if you have an array like this
int a[5];
then a pointer to the array will look like
int ( *p )[5] = &a;

Are arrays just the same as constant pointers or are there any another differences? [duplicate]

This question already has answers here:
C: differences between char pointer and array [duplicate]
(14 answers)
Closed 7 years ago.
I've read various articles and questions here on SO about pointers and arrays equivalency. Nearly each article explains it different. I know that arrays and pointers are strongly related and bellow are my experiments with pointers and arrays equivalency, including comments which explain given behavior (feel free to correct me if I'm wrong somewhere). My question is: are arrays are just constant pointers or is there also another differences?
#include <stdio.h>
int main ()
{
// declaring array this way in fact declares a pointer with name "a" which points to the first element in the array:
int a[] = {0,1,2,3,4};
// assigning an array to the pointer in fact assigns the address of the first array element to the pointer, those two are thus equivalents:
int *pa1 = a;
int *pa2 = &a[0];
printf("########################\n");
// REFERENCING: arrays can use pointer syntax (following are equivalents)
printf("%p\n", (a+0)); // a+0 == 0+a
printf("%p\n", a);
printf("%p\n", &a[0]);
printf("%p\n", &0[a]); // a+0 == 0+a
printf("########################\n");
// DEREFERENCING: arrays can use pointer syntax (following are equivalents)
printf("%d\n", *(a+0)); // a+0 == 0+a
printf("%d\n", *a);
printf("%d\n", a[0]);
printf("%d\n", 0[a]); // a+0 == 0+a
printf("########################\n");
// REFERENCING: arrays can use pointer syntax (following are equivalents)
printf("%p\n", (a+1));
printf("%p\n", &a[1]);
// REFERENCING: pointers can use array syntax (following are equivalents)
printf("%p\n", (pa1+1));
printf("%p\n", &pa1[1]);
printf("########################\n");
// DEREFERENCING: assigning values via pointers using pointer/array syntax (following are equivalents)
*(pa1+1) = *(pa1+1) + 10;
pa2[1] = pa2[1] + 10;
// DEREFERENCING: arrays can use pointer syntax (following are equivalents)
printf("%d\n", *(a+1));
printf("%d\n", a[1]);
printf("%d\n", 1[a]);
// DEREFERENCING: assigning values via arrays using pointer/array syntax (following are equivalents)
*(a+2) = *(a+2) + 10;
a[2] = a[2] + 10;
// DEREFERENCING: pointers can use array syntax (following are equivalents)
printf("%d\n", *(pa1+2));
printf("%d\n", pa1[2]);
printf("%d\n", 2[pa1]);
printf("########################\n");
// REFERENCING: those two pointers points to the same address
printf("%p\n", pa1);
printf("%p\n", pa2);
// DEREFERENCING: those two pointers points to the same address
printf("%d\n", *pa1);
printf("%d\n", *pa2);
printf("########################\n");
// This is correct:
pa1++;
printf("%p\n", pa1);
printf("%d\n", *pa1);
printf("%p\n", pa2);
printf("%d\n", *pa2);
printf("########################\n");
return 0;
}
I would say that arrays are just constant pointers, the only thing that misleads me is that the error messages are different when I try to increment array and constant pointer, here is what I mean:
#include <stdio.h>
int main (){
int var1=0;
int * const ptr;
int a[] = {0,1,2,3,4,5};
// This gives an error:
// error: increment of read-only variable ‘ptr’
ptr++;
// This gives an error:
// error: lvalue required as increment operand
a++;
return 0;
}
If they are not the same can you please post some scenario where this difference is obvious?
Arrays and pointers are completely different animals.
Under most circumstances, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", but the array object itself is not a pointer.
This behavior has its roots in the B language, from which C was derived. In B, an array object was a pointer, and the subscript operation a[i] was interpreted as *(a + i) (offset i elements from address stored in a, dereference the result).
Ritchie kept the *(a + i) semantics, but got rid of the explicit pointer; C converts the array expression to a pointer expression instead (except when the array expression is the operand of the unary & or sizeof operators).
You may pretend, that arrays are the same as constant pointers, but in fact they are different types. One notable difference is a result of the sizeof operator. For instance:
#include <stdio.h>
int main(void)
{
int a[] = {0, 1, 2, 3, 4, 5};
int * const p = a;
printf("sizeof(a) = %zu\n", sizeof(a));
printf("sizeof(p) = %zu\n", sizeof(p));
}
For the former, you are getting total size of array. Assuming that sizeof(int) = 4, it prints 24. OTOH, for the latter, you just get the same as size of int pointer.
Also, you cannot use array initializer for pointer variable:
int * const p = {0, 1, 2, 3, 4, 5};
results into compiler's error if you set it with -pedantic-errors flag, e.g.:
error: excess elements in scalar initializer
Another important difference is that arrays are copied for each element during a struct assignment:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct threeNumbers {
int t[3];
} a = {{1, 2, 3}}, b = {{4, 5, 6}};
int main(void)
{
a = b;
a.t[0] = 100;
printf("%d\n", a.t[0]); // prints 100
printf("%d\n", b.t[0]); // prints 4
}
This is not the case for pointers. Structures with const pointers cannot be assigned to each other. For non-const pointers members, only an adress is copied. If pointers were set with malloc, then this might result into memory leak.

Resources