Question regarding usage of array as a pointer in C - c

For eg. I have an array of structs 'a' as below:
struct mystruct{
int b
int num;
};
struct bigger_struct {
struct my_struct a[10];
}
struct bigger_struct *some_var;
i know that the name of an array when used as a value implicitly refers to the address of the first element of the array.(Which is how the array subscript operator works at-least)
Can i know do the other way around i.e
if i do:
some_var->a->b, it should be equivalent to some_var->a[0]->b, am i right? I have tested this and it seems to work , but is this semantically 100% correct?

Is some_var->a->b equivalent to some_var->a[0]->b?
No, it is equivalent to some_var->a[0].b.
The exact specification of the array-to-pointer conversion is actually quite straightforward:
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 (C99 6.3.2.1).
some_var->a has the type my_struct[10], which is an array type, and since it is not the operand of the sizeof or unary & operator and is not a string literal, it is converted to a pointer to the initial element of the array.

Assuming that you have allocated memory for some_var, its safe to do some_var->a->b (as arrays decay into pointer).

Yes, _var->a[0]->b and _var->a->b are equivalent
Because a[0] and a is representing the base address of the structure.

Related

About typecasting of an array to pointers

I want to understand where exactly in code an array gets converted to a pointer. For example:
void foo( int* pData, int len){}
int main(void){
char data[] = "Hello world";
foo( (int*)data, sizeof(data));
return 0;
}
I know that an array decays to a pointer to the first element if it is assigned to a pointer. However in the example above, I typecast the array data to int* first before passing it in function and assigning it to a pointer. Does the conversion/decay to pointer occurs at the typecasting point ? If so, isn't it true to say that the typecasting operation has the same effect as using the assignment operator with respect to array conversion/decay? Also would sizeof(data) be equal to the address length or array length?
Thank you for help!
The conversion of arrays to pointers in C is spelled out in section 6.3.2.1p3 of the C standard:
Except when it is the operand of the sizeof operator, the
_Alignof 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.
This means that the array is immediately converted to a pointer anywhere it is used except for the three cases listed above.
So applying the above to (int*)data, data is the operand of the typecast operator. Since this operator is not one of the ones listed above, data in this expression is converted from char [12] to char *, then the cast converts the char * to an int *.
Also, as mentioned above, the array is not converted when passed to sizeof. This means sizeof(data) evaluates to the size of char [12] which is 12.
Outwith the declaration, you can consider data to be equivalent to a pointer to the start of the array, but with the following exceptions:
sizeof(data) will give you the size of the array in bytes.
_Alignof(data) will be the same as _Alignof(*data) (and both give you the alignment of the type of the array elements)
&data has the same value as just data, but have a type of char (*)[sizeof(data] so arithmetic will use the full size of the array. Eg &data+1 will give you the address after the whole array rather than the address of the second element. See How come an array's address is equal to its value in C?
you can't change it's value (ie in this sense it is equivalent to a char *const).
When you call your function, you are taking the value of data (ie the address of the start of the array) and typecasting to an int *. The resulting int * behaves like any other int * and the exceptions don't apply, this is the 'decay'.

how a sizeof works when array name is passed

Why sizeof(array_name) is the size of the array and sizeof(&a[0]) is the size of pointer even though, when an array name is passed to a function, what is passed is the location of the beginning of the array.
In most expressions, when an array is used, it is automatically converted to a pointer to its first element.
There is a special rule for sizeof: When an array is an operand of sizeof, it is not automatically converted to a pointer. Therefore, sizeof array_name gives the size of the array, not the size of a pointer.
This rule also applies to the unary & operator : &array_name is the address of the array, not the address of a pointer.
Also, if an array is a string literal used to initialize an array, it is not converted to a pointer. The string literal is used to initialize the array.
The rule for this is C 2018 6.3.2.1 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.
Array parameters to functions don't exist. They turn into pointers (the innermost index does, if the array is multidimensional). It's just syntactic sugar inherited from the B language. void f(int *X) == void f(int X[]);
(Same for function params to functions. void g(void X(void)) == void g(void (*X)(void))).

if int A[5] is declared then only A is pointer to the A[0].which means A is just a pointer . then how come sizeof(A) gives answer as 20

Suppose if int A[5] is declared then variable A will be pointer to the A[0]. Which means A is just a pointer and A stores the base address of array A[5] . Then how come sizeof(A) gives answer as 20
A isn't a pointer to the first element. It is an int[5], or a five element array of ints (the size is part of the type). It can decay into a pointer to the first element when you do stuff like pass it to a function taking a pointer.
Contrary to what you might have heard, arrays are not the same as pointers.
In most contexts, the name of an array will decay into a pointer to the first element, such as being passed to a function or as the subject of pointer arithmetic.
One of the cases where this decay does not happen is when the array is the subject of the sizeof operator. In that case the operator returns the full size of the array in bytes.
This is detailed in section 6.3.2.1p3 of the C standard:
Except when it is the operand of the sizeof operator, the _Alignof
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.
Section 6.5.3.4p4, which details the sizeof operator, additionally states:
When sizeof is applied to an operand that has type char, unsigned char, or signed char, (or a qualified version thereof) the result is
1. When applied to an operand that has array type, the result is the total number of bytes in the array. When applied to an operand
that has structure or union type, the result is the total number of
bytes in such an object, including internal and trailing padding.
If you had some something like this:
int A[5];
int *B;
B = A;
printf("sizeof(B)=%zu\n", sizeof(B));
You would get the size of an int * on your system, most likely either 4 or 8.

What does address of a, which is an array, returns?

I thought when you try to get the address of an array, it returns the address of the first element it holds.
int *j;
int a[5]={1,5,4,7,8};
Now j=&a[0]; works perfectly fine.
Even j=a also does the same function.
But when I do j=&a it throws an error saying cannot convertint (*)[5]' to int*' in assignment
Why does it happen? &a should be the first element of the array a, so it should give &a[0].
But instead it throws an error. Can somebody explain why?
The C standard says the following regarding how arrays are used in expressions (taken from C99 6.3.2.1/3 "Lvalues, array, and function designators):
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
This is commonly known as "arrays decay to pointers".
So the sub-expression a in the following larger expressions evaluates to a pointer to int:
j=&a[0]
j=a
In the simpler expression, j=a, that pointer is simply assigned to j.
In the more complex expression, j=&a[0], the 'index' operator [] is applied to the pointer (which is an operation equivalent to *(a + 0)) and the 'address-of' operator is applied to that, resulting in another pointer to int that gets assigned to j.
In the expression j=&a, the address-of operator is applied directly to the array name, and we hit one of the exceptions in the above quoted clause: "Except when it is the operand of ... the unary & operator".
Now when we look at what the standard says about the unary & (address-of) operator (C99 6.5.3.2/3 "Address and indirection operators"):
The unary & operator returns the address of its operand. If the
operand has type "type", the result has type "pointer to type".
Since a has type "array of 5 int" (int [5]), the result of applying & to it directly has type "pointer to array of 5 int" (int (*)[5]), which is not assignable to int*.
The type of a and &a is not the same even though they contain the same value, i.e., base address of the array a.
j = a;
The array name a here gets converted to a pointer to its first element.
Try to see what values you get via these statements to understand where the difference lies:
printf("%p", a+1);
printf("%p", &a+1);
c is a strongly typed language. Assignment such as j=a; is allowed only if j and a are of the same type or the compiler can safely convert a to j. In your case, type of j is int * while the type of &a is int (*)[5]. The compiler does not know how to automatically convert an object of type int (*)[5] to an object of type int *. The compiler is telling you exactly that.
a is an array of 5 ints. The pointer to a is a pointer to an array of five integers, or int (*)[5]. This is not compatible with an int * because of pointer arithmetic: If you increment a variable of type int *, the address in the variable increases by 4 (assuming 4 byte integers), so that it points to the next integer. If you increment a variable that points to an array of 5 integers, the address in the variable increases by 20 (again assuming 4 byte integers), so that it points to the next array of five integers.
Perhaps what's confusing is that the value give by a and &a is the same, as you said. The value is the same but the type is different, and the difference is most obvious when you do arithmetic on the pointers.
I hope that helps.

C Struct & Strcpy, dot operator does it resolve into a pointer

suppose i have something like this
typedef char string[21]
struct planet_s
{
string name,orbits;
float distance;
string discoverer;
int yeardiscovered;
}one_planet;
next i need to initialize some information about the planet therefore my text book says
strcpy(one_planet.name, "Earth"); ?confused with these
strcpy(one_planet.orbits, "Sun"); ?
one_planet.distance = 150;
one_planet.mass = 6.00e+24;
strcpy(one_planet.discoverer, "me"); ?
one_planet.yeardiscovered = 1000;
my Confusion arises in the strcpy,let me put things in point form
Strcpy needs a pointer to a string as its first argument
does one_planet.name resolve into a pointer to the strcuts name array(does the dot operator resolve into an address)?
how come one_planet.distance =150 does not resolve into an address since we are assign it its value straight away? this is what i been taught, one_planet.distance directly access the struct element and assigns it. my confusion is with the strcpy, since it needs an address to store a string?
hope you understand where my confusion comes form thanks.
Except when it is the operand of the sizeof or unary & operator, or when it is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element in the array.
In the line
strcpy(one_planet.name, "Earth");
the expression one_planet.name has type "21-element array of char"; since the expression is not the operand of the sizeof or unary & operators, it is converted to an expression of type "pointer to char", and the address of the first element of the array is passed to strcpy.
The . operator doesn't make a difference in this case; what matters is the type of the member, regardless of how that member is accessed.
The dot operator has nothing to do with this.
Compare your code to this:
int main(void)
{
char string[21];
strcpy(string, "Earth");
return 0;
}
the above is 100% natural and fine C code, and it relies on the fact that in some contexts, the name of an array (string) evaluates to just a pointer to the first argument.
In other words, string above is evaluated to the exact same address value as the expression &string[0].
The fact that your string is embedded inside a struct has nothing to do with this.
No it's not the "dot operator" . that creates a pointer, it's because e.g. the orbits member is an array. All arrays decays to pointers when passed as arguments.

Resources