What does sizeof(&array) return?
Asked Answered
S

4

35

Following the question: How come an array's address is equal to its value in C?

#include <stdio.h>
#define N 10    
char str2[N]={"Hello"};
int main(){
    printf("sizeof(str2): %d bytes\n", sizeof(str2));
    printf("sizeof(&str2): %d bytes\n", sizeof(&str2));
    return 0;
}

Output:

sizeof(str2): 10 bytes
sizeof(&str2): 4 bytes

I know that str2 alone is the address of the first element in array str2. And that when str2 is an argument of sizeof it returns the size of the whole array str2.

In addition, &str2 is also the address of the first element in arr str2 but from different type (char (*)[N] == pointer to array). But how does &str2 behave when it's an argument of sizeof?

Salmonoid answered 2/3, 2013 at 17:36 Comment(6)
"I know that str2 alone is the adress of the first element in array str2" - well, it isn't. It is converted to a pointer to its first element in most contexts, but sizeof is one of the exceptions.Ike
@DanielFischer so why printf("%p %p",str2, str2+1); prints the adress of the first and second element respectively?Salmonoid
@Salmonoid Because that is not one of the exceptions. Unless it is the operand of sizeof, _Alignof or the address operator &, or, in the case of string literals, as an initializer for a char[], an expression of array type is converted to a pointer to the array's first element. So in most contexts, the name of an array evaluates to a pointer to its first element, but it is something different (the name of an array).Ike
@DanielFischer: _Alignof is not one of the exceptions, since _Alignof can only be applied to a parenthesized type name, not to an expression. That was an error in the N1570 draft, corrected in the published ISO C11 standard. (As for why _Alignof can't be applied to an expression, that's a different question.)Rehnberg
@KeithThompson Meanwhile, I know that. I think it was you who told me, but it could also have been someone else. But thanks anyway, even if it's a repeated thanks.Ike
@DanielFischer: Your second comment helps a lot, that's why I like a statement made by mathematician.Stalactite
D
22

&str2 is a pointer. So you're just seeing the sizeof a pointer on your platform.

Ding answered 2/3, 2013 at 17:38 Comment(1)
It is not necessarily the case that all pointer types are the same size, though it's true for most implementations.Rehnberg
O
119

Difference between &str and str, when str is declared as char str[10]?

Read sizeof Operator:

6.5.3.4 The sizeof operator, 1125:
When you apply the sizeof operator to an array type, the result is the total number of bytes in the array.

So, according to your declaration, sizeof(str2) gives the complete array size that is 10 bytes (because N is defined as 10, and char size is 1-byte).
Whereas in the expression sizeof(&str2), &str2 is an address of an array and the size of that address is 4 bytes on your system. (address size may 8 bytes in some systems e.g 64-bit).

In addition, &str2 is also the address of the first element in arr str2?

No, value-wise both &str2 and str are the same, but semantically both are different. One is an address of an array of 10 chars while the other is an address of a char.

One difference you have seen in your own example between them is (@ouah explained this in an answer).

  • type of str is char[10]
  • type of &str is char(*)[10]

Second: Following a diagram will help you observe the other difference.

for declaration: 
#define N 10
char str2[N] = {"Hello"};

str2 Array in memory is something like:
----------------------------------------

str
+----+----+----+----+----+----+----+----+----+----++----+
|'H' |'e' |'l' |'l' |'o' |'\0'|'\0'|'\0'|'\0'|'\0'|| '@'|
+----+----+----+----+----+----+----+----+----+----++----+
 201   202  203 204  205   206  207  208  209  210   211
▲ ▲     ▲                                             ▲
| |     |                                             |
|(str2) (str2 + 1)                                    | 
|                                                     |
|-----------------------------------------------------|
|201                                                  | 
|                                                     |
|                                                     |
(&str2) = 201                           (&str2 + 1) = 211


* assuming str address start from 201
* str[N] is 10 char long 201-210, partially initialized
* at uninitialized position, str2[i] = '\0'
* location 211 is unallocated, having garbage value,
  access to this location is illegal-Undefined Behavior

For the above diagram you can write code like:

#include <stdio.h>
#define N 10    
int main(){
   char str2[N]={"Hello"};
  
   printf("\n %p, %p\n",str2, str2+1);
   printf("\n %p, %p\n",(&str2), (&str2+1));
}  

Output:

 0xbf67e142, 0xbf67e143

 0xbf67e142, 0xbf67e14c

A link for codepad:

Notice that at the first line, the output address differ by one byte, but in the second, the difference is 10 bytes because it's the array's pointer (as shown in the diagram above).

According to the rules of pointer arithmetics, when you add 1 to a pointer variable, it starts to point to the next element of its own type. That's the reason for the 10 byte difference because &str2 is an address pointing to an array.

Third difference:

By doing *str2 you can access the first element. Whereas *(&str2) will not give you the first element, instead, it will give the address of the first element.

An example will help here:

#include <stdio.h>
#define N 10    
int main(){
   char str2[N]={"Hello"};
   printf("\n%p %c, %p %c\n",str2, *(str2), *(&str2), **(&str2));
}  

output:

0xbf587046 H, 0xbf587046 H

Codepad link

In output

str2 gives  0xbf587046 
*(str2)     H 
*(&str2)    0xbf587046 
**(&str2)   H 

That means *(&str2) == str2 and value is address. And hence *(str2) = **(&str2) values is H.

Edit: Above I shown difference between &str and str where str is an array of type char[10].

Difference between char *str and char str[] and how both are stored in memory

Suppose we have two declarations like below:

char *str1 = "hello";   
char str2[] = "hello";  

In the above declarations str1 is a pointer to char, that points to a constant string literal (by holding address of the first char h in "hello" string).

A string in C is of char[N] (array) type that is why sizeof("hello") gives 6 because "hello" string is 6 chars long array (included \0 nul, strings termination, type of hello is char[6]).

In memory your "hello" string is stored like this:

 str1         23   24   25   26   27   28
+----+      +----+----+----+----+----+----+
| 23 |      | h  | e  |  l | l  | o  | \0 |    
+----+      +----+----+----+----+----+----+
   +-----------▲

here the address of the hello string is the first address = 23.  
str1: is a pointer capable of storing an address. 
"hello" consists of 6 chars

char* str1 = "hello"; basically stores an address of a string hello to a pointer variable str1 as I show in the above figure.

Note: If you want, you can change str1 to point some other string. But you can't modify hello string. for example following code is valid:

 char* str1 = "hello";  // str1 points to hello  str1-->"hello"
 str1 = "world";  //Now, str1 points to world  str1-->"world"

Now str1 points to other constant string world.

 str1         93   94   95   96   97   98 
+----+      +----+----+----+----+----+----+
| 93 |      | w  | o  |  r | l  | d  | \0 |    
+----+      +----+----+----+----+----+----+
   +-----------▲

here address of world string is first address = 93.  
str1: value change to point string world. 

Important to note: str1 points to constant strings hence you can't modify the string by accessing/indexing memory location for example str1[i] = 'A'; will be illegal because you are writing on read only memory and behavior of this is undefined at runtime (although no compilation error because syntactically it's correct).

Again, because str1 is a pointer sizeof(str1) will give 4 on same machine.

My following code and its run:

#include <stdio.h>
int main(){
   char* str1="Hello";
   printf("\nstr1: %s, address: %p, sizeof(str1): %u", str1, str1, sizeof(str1));
   str1 = "world";
   printf("\nstr1: %s, address: %p, sizeof(str1): %u", str1, str1, sizeof(str1));
   return 1;
}  

Output:

str1: Hello, address: 0x80485e8, sizeof(str1): 4
str1: world, address: 0x8048619, sizeof(str1): 4

Codepad link

So, to assign a new string I simply assign an address of a new string. But I can't call strcpy() that will try to write on a read only memory location and that is illegal.

In the second declaration char str2[] = "hello";, str2[] is an \0 terminated array of chars (or string) but NOT a pointer. Notice because in this declaration size is not given default size, we see that the size of the constant string "hello" that is 6. Type of str2 is char[6].

When we do char str2[] = "hello"; an array of char is created and hello string will be copied into that array. So str2 is not simply a pointer, but an array storing a complete string.

Conceptually it's like

       str2:
       103  104  105  106  107  108
      +----+----+----+----+----+----+
      | h  | e  |  l | l  | o  | \0 |    
      +----+----+----+----+----+----+

In this case lately in your code you are not allowed to do str2[] = "world"; or str2 = "world", it will be compile time error.

Example Code:

#include<stdio.h>
int main(){
 char str2[] = "hello";
 str2[] = "world";
 str2 = "world"; 
 return 1; 
}

Compilation errors:

In function 'main':
Line 4: error: expected expression before ']' token
Line 5: error: incompatible types in assignment

Codescape link

Where this array str2 is not a constant, we can modify its content for example doing str2[2] = 'A' is perfectly valid. We can also call strcpy to change content (and address space will not change)

       strcpy(str2, "world");

       str2:
       103  104  105  106  107  108
      +----+----+----+----+----+----+
      | w  | o  |  r | l  | d  | \0 |    
      +----+----+----+----+----+----+

      Note that when "world" is copied into a same memory space, the addresses of both "world" and "hello"
      string are the same.  

Code Example:

#include<stdio.h>
int main(){
 char str2[] = "hello";
 printf("\nstr2: %s, address: %p, sizeof(str2): %u", str2, str2, sizeof(str2));
 str2[2] = 'A';
 printf("\nstr2: %s, address: %p, sizeof(str2): %u", str2, str2, sizeof(str2));
 strcpy(str2, "world");
 printf("\nstr2: %s, address: %p, sizeof(str2): %u", str2, str2, sizeof(str2));
 return 1; 
}

Output:

str2: hello, address: 0xbf58d056, sizeof(str2): 6
str2: heAlo, address: 0xbf58d056, sizeof(str2): 6
str2: world, address: 0xbf58d056, sizeof(str2): 6

Codepad link

Note: string values are different on same address space. sizeof(str2) = 6 perfectly understood from older answer that is size of array in bytes.

To read similar description about 2-dimensional array, read: Difference between char* str[] and char str[][] and how both stores in memory?

Ornithorhynchus answered 2/3, 2013 at 17:45 Comment(0)
D
22

&str2 is a pointer. So you're just seeing the sizeof a pointer on your platform.

Ding answered 2/3, 2013 at 17:38 Comment(1)
It is not necessarily the case that all pointer types are the same size, though it's true for most implementations.Rehnberg
C
12

str2 is of type char [10] (i.e, array 10 ofchar`)

&str2 is of type char (*)[10] (i.e., pointer to an array 10 of char).

So sizeof (&str2) yields the size of an objet of pointer type char (*)[10]

Credo answered 2/3, 2013 at 17:38 Comment(0)
S
2

If you simply use the variable arr (as defined char arr[10]), it always decays to a const pointer char *const arr pointing to the first element. While &arr results in a pointer char (*)[10] that includes the size of arr, i.g. 10.

Stalactite answered 1/5, 2021 at 18:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.