В моем коде:
char *str[] = {"forgs", "do", "not", "die"};
printf("%d %d", sizeof(str), sizeof(str[0]));
Я получаю вывод как 12 2
, поэтому мои сомнения:
- Почему существует разница?
- Оба
str
иstr[0]
являются указателями char, правильно?
В моем коде:
char *str[] = {"forgs", "do", "not", "die"};
printf("%d %d", sizeof(str), sizeof(str[0]));
Я получаю вывод как 12 2
, поэтому мои сомнения:
str
и str[0]
являются указателями char, правильно?В большинстве случаев имя массива будет распадаться на значение адреса его первого элемента и с типом, таким же, как указатель на тип элемента. Таким образом, вы ожидаете, что голый str
будет иметь значение, равное &str[0]
, с указателем типа на указатель на char
.
Однако это не относится к sizeof
. В этом случае имя массива поддерживает свой тип для sizeof
, который будет представлять собой массив из 4 указателей на char
.
Возвращаемым типом sizeof
является size_t
. Если у вас есть компилятор C99, вы можете использовать %zu
в строке формата для печати значения, возвращаемого sizeof
.
В этом вопросе уже был дан ответ и принят, но я добавляю еще несколько описаний (также отвечая на исходный вопрос), которые, я думаю, будут полезны для новых пользователей. (как я искал, это описание не объясняется нигде (по крайней мере, в stackoverflow), поэтому я добавляю сейчас.
Сначала прочитайте: sizeof
Оператор
6.5.3.4 Оператор sizeof, 1125:
Когда вы применяете операторsizeof
к типу массива, результатом является общее количество байтов в массиве.
В соответствии с этим, когда sizeof
применяется к имени статического идентификатора массива (не выделяется через malloc), результатом является размер в байтах всего массива, а не просто адрес. Это одно из немногих исключений из правила, что имя массива преобразуется/распадается на указатель на первый элемент массива, и это возможно просто потому что фактический размер массива фиксирован и известен во время компиляции, когда оператор sizeof
оценивает.
Чтобы лучше понять этот код ниже:
#include<stdio.h>
int main(){
char a1[6], // One dimensional
a2[7][6], // Two dimensional
a3[5][7][6]; // Three dimensional
printf(" sizeof(a1) : %lu \n", sizeof(a1));
printf(" sizeof(a2) : %lu \n", sizeof(a2));
printf(" sizeof(a3) : %lu \n", sizeof(a3));
printf(" Char : %lu \n", sizeof(char));
printf(" Char[6] : %lu \n", sizeof(char[6]));
printf(" Char[5][7] : %lu \n", sizeof(char[7][6]));
printf(" Char[5][7][6]: %lu \n", sizeof(char[5][7][6]));
return 1;
}
Его вывод:
sizeof(a1) : 6
sizeof(a2) : 42
sizeof(a3) : 210
Char : 1
Char[5] : 6
Char[5][7] : 42
Char[5][7][6]: 210
Проверьте выше, используя @codepad, размер уведомления char
- это один байт, вы заменяете char
на int
в вышеприведенной программе, то каждый выход будет умножен на sizeof(int)
на вашем компьютере.
char* str[]
и char str[][]
и то, как оба сохраняются в памяти Декларация-1: char *str[] = {"forgs", "do", "not", "die"};
В этом объявлении str[]
представляет собой массив указателей на char. Каждый индекс str[i]
указывает на первый char строк в {"forgs", "do", "not", "die"};
.
Логически str
должен располагаться в памяти следующим образом:
Array Variable: Constant Strings:
--------------- -----------------
str: 201 202 203 204 205 206
+--------+ +-----+-----+-----+-----+-----+-----+
343 | |= *(str + 0) | 'f' | 'o' | 'r' | 'g' | 's' | '\0'|
| str[0] |-------| +-----+-----+-----+-----+-----+-----+
| 201 | +-----------▲
+--------+ 502 503 504
| | +-----+-----+-----+
347 | str[1] |= *(str + 1) | 'd' | 'o' | '\0'|
| 502 |-------| +-----+-----+-----+
+--------+ +-----------▲
| | 43 44 45 46
351 | 43 | +-----+-----+-----+-----+
| str[2] |= *(str + 2) | 'n' | 'o' | 't' | '\0'|
| |-------| +-----+-----+-----+-----+
+--------+ +-----------▲
355 | |
| 9002 | 9002 9003 9004 9005
| str[3] | +-----+-----+-----+-----+
| |= *(str + 3) | 'd' | 'i' | 'e' | '\0'|
+--------+ | +-----+-----+-----+-----+
+-----------▲
Diagram: shows that str[i] Points to first char of each constant string literal.
Memory address values are assumption.
Примечание. str[]
хранится в распределениях продолжения памяти, и каждая строка сохраняется в памяти по случайному адресу (а не в продолжении).
[ОТВЕТ]
В соответствии с Codepad следующий код:
int main(int argc, char **argv){
char *str[] = {"forgs", "do", "not", "die"};
printf("sizeof(str): %lu, sizeof(str[0]): %lu\n",
sizeof(str),
sizeof(str[0])
);
return 0;
}
Выход:
sizeof(str): 16, sizeof(str[0]): 4
В этом коде str
находится массив для 4 char -addresses, где каждый char*
имеет размер 4 байта, поэтому в соответствии с вышеприведенным предложением общий размер массива равен 4 * sizeof(char*)
= 16 байт.
Тип данных str
равен char*[4]
.
str[0]
- это не что иное, как указатель на char, поэтому его четыре байта. Тип данных str[i]
- char*
.
(примечание: в некотором системном адресе могут быть 2-байтовые или 8-байтовые)
Относительно выхода следует также прочитать glglgl comment вопрос:
По какой бы архитектуре вы ни были, первое значение должно быть в 4 раза больше второго. На 32-битной машине вы должны получить 16 4, на 64-битной 32. На очень старой или во встроенной системе, вы даже можете получить 8 2, но никогда не 12 2, так как массив содержит 4 элемента одинакового размера
Дополнительные пункты:
str[i]
указывает на char*
(и строку) является переменной, str[i]
может быть назначен новый адрес строки, например: str[i] = "yournewname";
действителен для i = 0 to < 4
.Еще одно важное замечание:
В нашем примере выше str[i]
, указывающем на постоянный строковый литерал, который нельзя изменить; поэтому str[i][j] = 'A'
является недопустимым (мы не можем писать в памяти только для чтения), и это будет ошибкой во время выполнения.
Но предположим, что если str[i]
указывает на простой массив char, то str[i][j] = 'A'
может быть допустимым выражением.
Рассмотрим следующий код:
char a[] = "Hello"; // a[] is simple array
char *str[] = {"forgs", "do", "not", "die"};
//str[0][4] = 'A'; // is error because writing on read only memory
str[0] = a;
str[0][5] = 'A'; // is perfectly valid because str[0]
// points to an array (that is not constant)
Проверьте здесь рабочий код: Codepad
Декларация-2: char str[][6] = {"forgs", "do", "not", "die"};
:
Здесь str
представляет собой двумерный массив символов (где каждая строка равна по размеру) размером 4 * 6. (помните, что здесь вы должны явно указывать значение столбца в объявлении str
, но строка 4 из-за количества строк - 4)
В памяти str[][]
будет что-то вроде ниже в диаграмме:
str
+---201---202---203---204---205---206--+
201 | +-----+-----+-----+-----+-----+-----+|
str[0] = *(str + 0)--►| 'f' | 'o' | 'r' | 'g' | 's' | '\0'||
207 | +-----+-----+-----+-----+-----+-----+|
str[1] = *(str + 1)--►| 'd' | 'o' | '\0'| '\0'| '\0'| '\0'||
213 | +-----+-----+-----+-----+-----+-----+|
str[2] = *(str + 2)--►| 'n' | 'o' | 't' | '\0'| '\0'| '\0'||
219 | +-----+-----+-----+-----+-----+-----+|
str[3] = *(str + 3)--►| 'd' | 'i' | 'e' | '\0'| '\0'| '\0'||
| +-----+-----+-----+-----+-----+-----+|
+--------------------------------------+
In Diagram:
str[i] = *(str + i) = points to a complete i-row of size = 6 chars.
str[i] is an array of 6 chars.
Эта компоновка 2D-массива в памяти называется Row-Major: Многомерный массив в линейной памяти организован таким образом, что строки хранятся один за другим. Это подход, используемый языком программирования C.
Обратите внимание на различия в обеих диаграммах.
i = 0 to 2
, str[i]
и str[i + 1]
значение отличается на 6 байтов (то есть равно одной строке).str
представляет полный 6 * 4 = 24 символа.Теперь рассмотрим аналогичный код, который вы разместили в своем вопросе для 2-мерного массива char, проверьте Codepad:
int main(int argc, char **argv){
char str[][6] = {"forgs", "do", "not", "die"};
printf("sizeof(str): %lu, sizeof(str[0]): %lu\n",
sizeof(str),
sizeof(str[0])
);
return 0;
}
Выход:
sizeof(str): 24, sizeof(str[0]): 6
В соответствии с обработкой оператора sizeof
массивом, при применении размера массива 2-мерного размера должен возвращать весь размер массива, который равен 24 байтам.
Как нам известно, оператор sizeof
возвращает размер всего массива при применении имени массива. Таким образом, для sizeof(str)
он возвращает = 24, который является размером полного массива 2D char, состоит из 24 символов (6-cols * 4 строки).
В этом объявлении тип str
равен char[4][6]
.
Еще один интересный момент: str[i]
представляет чаты массивов, а тип - char[6]
. И sizeof(str[0])
- полный размер массива = 6 (длина строки).
Дополнительные пункты:
Во втором объявлении str[i][j]
не является постоянным, и его содержимое может быть изменено, например. str[i][j] = 'A'
- действительная операция.
str[i]
является именем char массив типа char[6]
является константой и присваивается str[i]
, например. str[i] = "newstring"
- незаконная операция (заражение будет ошибкой компиляции).
Еще одно важное различие между двумя объявлениями:
В Декларация-1: char *str[] = {"forgs", "do", "not", "die"};
тип &str
- это char*(*)[4]
, его адрес массива указателей char.
В Декларация-2: char str[][6] = {"forgs", "do", "not", "die"};
тип &str
- это char(*)[4][6]
, его адрес 2-D char массив из 4 строк и 6 столбцов.
Если вы хотите прочитать подобное описание для 1-D массива: Что возвращает sizeof(&array)
?
Это 16 4
на моем компьютере, и я могу это объяснить: str
- это массив char*
, поэтому sizeof(str)==sizeof(char*)*4
Я не знаю, почему вы получаете 12 2
, хотя.
Два указателя разные. str
- это array of char pointers
, в вашем примере это (char*[4]
), а str[0]
- char pointer
.
Первый sizeof
возвращает размер четырех указателей char, который содержит, а второй возвращает sizeof char*
.
В моих тестах результаты:
sizeof(str[0]) = 4 // = sizeof(char*)
sizeof(str) = 16
= sizeof(str[0]) + sizeof(str[1]) + sizeof(str[2]) + sizeof(str[3])
= 4 * sizeof(char*)
= 4 * 4
= 16