Что возвращает sizeof (& array)?

После вопроса: Почему адрес массива равен его значению в 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;
}

Вывод:

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

Я знаю, что только str2 является адресом первого элемента в массиве str2. И что когда str2 является аргументом sizeof, он возвращает размер всего массива str2.

Кроме того, &str2 также является адресом первого элемента в arr str2, но из другого типа (char (*)[N] == указатель на массив). Но как &str2 ведет себя, когда это аргумент sizeof?

Ответ 1

&str2 - указатель. Таким образом, вы просто видите размер указателя на своей платформе.

Ответ 2

Разница между &str и str, когда str объявляется как char str[10]?

Считать sizeof Оператор:

6.5.3.4 Оператор sizeof, 1125:
Когда вы применяете оператор sizeof к типу массива, результатом является общее количество байтов в массиве.

Итак, согласно вашему объявлению, sizeof(str2) дает полный размер массива, который равен 10 байтам (поскольку N определено 10, а размер char - 1-байтовый).
Если в выражении sizeof(&str2), &str2 - адрес массива и размер адреса, который равен 4 байтам в вашей системе. (размер адреса может составлять 8 байт в некоторых системах, например 64-разрядный).

Кроме того, &str2 также является адресом первого элемента в arr str2?

Нет, значения как &str2, так и str совпадают, но семантически оба отличаются. Один из них - адрес массива из 10 символов, а другой - адрес char.

Одна разница, которую вы видели в своем собственном примере, как они являются различиями (и @ouah объясняется в этом ответе).

  • Тип str - char[10]
  • Тип &str - char(*)[10]

Второе: Следующая диаграмма поможет вам увидеть другую разницу.

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

Для диаграммы выше вы можете написать код:

#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));
}  

Выход:

 0xbf67e142, 0xbf67e143

 0xbf67e142, 0xbf67e14c

A ссылка для кодостата:

Обратите внимание, что разность выходных адресов первой строки - один байт, но во второй строке разница составляет 10 байт, потому что указатель на ее массив (как показано выше на диаграмме).

В соответствии с правилами математики указателя при добавлении 1 в переменную-указатель он указывает на следующий элемент своего типа, что является причиной 10-байтовых различий, потому что &str2 - адрес массива.

Третья разница:

Выполняя *str2, вы можете получить доступ к первому элементу. В то время как *(&str2) не даст вам первый элемент, но это адрес первого элемента.

Пример поможет здесь:

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

вывод:

0xbf587046 H, 0xbf587046 H

Ссылка Codepad

В выводе

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

Это означает *(&str2) == str2, а значение - адрес. И, следовательно, значения *(str2) = **(&str2) H.

Изменить: Выше я показал разницу между &str и str, где str - массив типа char[10].

Разница между char *str и char str[] и то, как оба сохраняются в памяти

Предположим, что у нас есть два объявления, как показано ниже:

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

В вышеприведенных объявлениях str1 является указателем на char, который указывает на константный строковый литерал (путем размещения адреса первой строки char H в строке "hello").

Строка в C имеет тип char[N] (массив), поэтому sizeof("hello") дает 6, потому что строка "hello" имеет длину 6 символов (включено \0 nul, завершение строк, тип приветствия - char[6]).

В памяти вы сохраняете строку "hello", как показано ниже:

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

here address of hello string is first address = 23.  
str1: is pointer capable to store address. 
"hello" consists of 6 chars

char* str1 = "hello"; в основном хранит адрес строки hello переменной указателя str1, как показано выше на рисунке.

Примечание. Если вы хотите в последнее время в своем коде, вы меняете изменение str1, чтобы указать другую строку. Но вы не можете изменить строку hello. например, следующий код действителен:

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

Теперь str1 указывает на другой постоянный мир струн.

 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. 

Важно отметить: str1 указывает на константные строки, поэтому вы не можете изменять строку, обращаясь к местоположению/индексированию памяти, например str1[i] = 'A'; будет незаконным, потому что вы пишете только на чтение, а поведение этого - Undefined во время выполнения (хотя ошибка компиляции отсутствует, так как синтаксически корректно).

Снова, потому что str1 является указателем sizeof(str1), даст 4 на той же машине.

Мой следующий код и его запуск:

#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;
}  

Выход:

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

Ссылка Codepad

Итак, чтобы назначить новую строку, я просто назначаю адрес новой строки. Но я не могу назвать strcpy(), который будет пытаться писать на ячейке памяти только для чтения, и это незаконно.

Во втором объявлении char str2[] = "hello";, str2[] является \0 завершенным массивом символов (или строки), но НЕ указателем. Обратите внимание, потому что в этом объявлении размер не задан по умолчанию, мы будем иметь такой размер постоянной строки "hello", которая равна 6. Тип str2 - char[6].

Когда мы делаем char str2[] = "hello";, массив char created и hello string будет скопирован в этот массив So str2 - это не просто указатель, а массив, хранящий полную строку.

Его концептуально похоже.

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

И в этом случае в последнее время в вашем коде вы не разрешаете делать str2[] = "world"; или str2 = "world" заражать, это будет ошибка времени компиляции.

Пример кода:

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

Ошибки компиляции:

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

Ссылка Codescape

Если этот массив str2 не является постоянным, мы можем изменить его содержимое, например, сделать str2[2] = 'A' совершенно корректным. Мы также можем вызвать strcpy для изменения содержимого (и адресное пространство не изменится)

       strcpy(str2, "world");

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

      Note world coped into same memory space, address of world and hello
      string is name. 

Пример кода:

#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; 
}

Выход:

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

Ссылка Codepad

Примечание. Строковые значения различаются в одном и том же адресном пространстве. sizeof(str2)= 6 отлично понимается из более старого ответа, который представляет собой размер массива в байтах.

Чтобы прочитать аналогичное описание о 2-мерном массиве, прочитайте: Разница между char* str[] и char str[][] и как оба хранилища в памяти?

Ответ 3

str2 имеет тип char [10] (т.е. массив 10 of char `)

&str2 имеет тип char (*)[10] (т.е. указатель на массив 10 of char).

Итак, sizeof (&str2) дает размер объекта типа указателя char (*)[10]