Что означает "имя массива" в случае массива указателей на символы?

В моем коде:

 char *str[] = {"forgs", "do", "not", "die"};
 printf("%d %d", sizeof(str), sizeof(str[0]));  

Я получаю вывод как 12 2так что мои сомнения:

  1. Почему есть разница?
  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
Другие вопросы по тегам