Что возвращает 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 of
char`)
&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
используется в этой операции.