Что возвращает 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 также адрес первого элемента в обр str2 но из другого типа (char (*)[N] == указатель на массив). Но как &str2 ведет себя, когда это аргумент sizeof?

4 ответа

Решение

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

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

Читать sizeof Оператор:

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

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

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

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

Одно отличие, которое вы видели в своем собственном примере, состоит в том, как они различаются (и @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

Ссылка для кодовой панели:

Обратите внимание, что разница выходных адресов в первой строке составляет один байт, но во второй строке разница составляет 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

Ссылка на кодовую панель

На выходе

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, который указывает на константу строкового литерала (удерживая адрес первого символа h в "hello" строка).

Строка в C имеет char[N] (массив) тип, поэтому sizeof("hello") дает 6, потому что "hello" строка длиной 6 символов \0 nul, завершение строки, тип hello is 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"; в основном хранит адрес строки привет переменной указателя 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'; будет недопустимым, потому что вы пишете только для чтения, и поведение этого параметра не определено во время выполнения (хотя нет ошибки компиляции, потому что синтаксически это правильно).

Опять потому что 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

Ссылка на кодовую панель

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

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

Когда мы делаем char str2[] = "hello"; создан массив символов char и привет строка будет скопирована в этот массив 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

Ссылка на кодекс

Где этот массив 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

Ссылка на кодовую панель

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

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

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

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

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

Читателю в 2021 году:

Можно думать о char arr[10] как объявление двух объектов за сценой:

  • Константный указатель на первый элемент массива. (Таким образом, вы не можете переназначить)
  • Размер, связанный с объектом, поэтому вы можете получить предупреждение, когда индекс выходит за пределы.

Сказанное выше приводит соответственно к двум фактам:

  • Арифметика указателя предназначена для перемещения внутри массива, поэтому при выполнении арифметики указателя используется только информация.

    Таким образом, арифметика указателя на нем будет смещена за один char, не в char [10], т.е. arr+1 указать на arr[1].

  • Некоторые операции, например, и &arr, используйте всю информацию , char * const arr,.

    Так sizeof(arr) возвращается sizeof(char) * 10(или же sizeof(*arr) * 10, более портативный), т.е. размер [10] связанный с arr используется в этой операции.

Другие вопросы по тегам