Что означает "имя массива" в случае массива указателей на символы?
В моем коде:
char *str[] = {"forgs", "do", "not", "die"};
printf("%d %d", sizeof(str), sizeof(str[0]));
Я получаю вывод как 12 2
так что мои сомнения:
- Почему есть разница?
- И то и другое
str
а такжеstr[0]
это указатели на символы, верно?
4 ответа
В большинстве случаев имя массива уменьшается до значения адреса его первого элемента, а его тип совпадает с указателем на тип элемента. Таким образом, вы ожидаете голого str
иметь значение, равное &str[0]
с указателем типа на указатель на char
,
Однако это не относится к sizeof
, В этом случае имя массива сохраняет свой тип для sizeof
, который будет массивом из 4 указателей на char
,
Тип возврата sizeof
это size_t
, Если у вас есть компилятор C99, вы можете использовать %zu
в строке формата для печати значения, возвращенного sizeof
,
На этот вопрос уже дан ответ и он принят, но я добавляю еще несколько описаний (также отвечаю на исходный вопрос), которые, я думаю, будут полезны для новых пользователей. (как я искал, это описание нигде не объяснено (по крайней мере, на stackru), поэтому я добавляю сейчас.
Сначала прочитайте: sizeof
оператор
6.5.3.4 Размер оператора, 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]
указывает на первый символ строки в {"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*
размер 4 байта, поэтому согласно приведенной выше цитате общий размер массива4 * sizeof(char*)
= 16 байт.Тип данных
str
являетсяchar*[4]
,str[0]
это не что иное, как указатель на символ, поэтому его четыре байта. Тип датыstr[i]
являетсяchar*
,
(примечание: в некоторых системных адресах может быть 2 байта или 8 байтов)
Что касается вывода, следует также прочитать комментарий glglgl к вопросу:
На какой бы архитектуре вы ни находились, первое значение должно быть в 4 раза больше второго. На 32-битной машине вы должны получить 16 4, на 64-битной 32 8. На очень старой или во встроенной системе вы можете даже получить 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]
указывает на простой массив символов, то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.
Это расположение двумерного массива в памяти называется Row-Major: многомерный массив в линейной памяти организован так, что строки хранятся одна за другой. Это подход, используемый языком программирования C.
Обратите внимание на различия в обеих диаграммах.
- Во втором случае полный двумерный массив символов размещается в памяти продолжения.
- Для любого
i = 0 to 2
,str[i]
а такжеstr[i + 1]
значение отличается на 6 байтов (то есть равняется длине одной строки). - Двойная граница на этой диаграмме означает
str
представляет собой полный 6 * 4 = 24 символа.
Теперь рассмотрим аналогичный код, который вы разместили в своем вопросе для двумерного массива символов, проверьте на 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, то есть размер полного массива двумерных символов состоит из 24 символов (6 строк * 4 строки).В этой декларации тип
str
являетсяchar[4][6]
,Еще один интересный момент
str[i]
представляет собой массив чатов и его типchar[6]
, А такжеsizeof(str[0])
полный размер массива = 6 (длина строки).
Дополнительные баллы:
Во второй декларации
str[i][j]
не является постоянным, и его содержание может быть изменено, напримерstr[i][j] = 'A'
является действительной операцией.str[i]
это имя массива символов типаchar[6]
является константой и присваиваниемstr[i]
напримерstr[i] = "newstring"
является недопустимой операцией (заражение будет ошибкой времени компиляции).
Еще одно важное отличие двух объявлений:
В декларации-1: char *str[] = {"forgs", "do", "not", "die"};
, тип &str
является char*(*)[4]
, его адрес массива указателей на символы.
В Декларации-2: char str[][6] = {"forgs", "do", "not", "die"};
, тип &str
является char(*)[4][6]
, его адрес двумерного массива из 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(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