Ответ 1
&str2
- указатель. Таким образом, вы просто видите размер указателя на своей платформе.
После вопроса: Почему адрес массива равен его значению в 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
?
&str2
- указатель. Таким образом, вы просто видите размер указателя на своей платформе.
&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
также является адресом первого элемента в arrstr2
?
Нет, значения как &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
Обратите внимание, что разность выходных адресов первой строки - один байт, но во второй строке разница составляет 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
, который указывает на константный строковый литерал (путем размещения адреса первой строки 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
Итак, чтобы назначить новую строку, я просто назначаю адрес новой строки. Но я не могу назвать 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
Если этот массив 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 of
char `)
&str2
имеет тип char (*)[10]
(т.е. указатель на массив 10
of char
).
Итак, sizeof (&str2)
дает размер объекта типа указателя char (*)[10]