Подтвердить что ты не робот

Эксперименты с использованием sizeof с массивами и указателями

Для программы:

#include<stdio.h>
int main(void)
{

    int (*a)[2];
    int b[5];

    printf("sizeof(int) : %zu\n", sizeof(int)); 
    printf("sizeof(int*) : %zu\n", sizeof(int*));

    printf("sizeof(b) : %zu\n",sizeof(b));
    printf("sizeof((int*)b) : %zu\n",sizeof((int*)b));
    printf("sizeof(&b[0]) : %zu\n",sizeof(&b[0]));

    printf("sizeof(a) : %zu\n",sizeof(a));
    printf("sizeof(a[0]) : %zu\n",sizeof(a[0]));
    printf("sizeof(a[1]) : %zu\n",sizeof(a[1]));

    return 0;
}

Выход:

sizeof(int) : 4 -> Fact 1
sizeof(int*) : 8 -> Fact 2
sizeof(b) : 20 -> Case 1
sizeof((int*)b) : 8 -> Case 2
sizeof(&b[0]) : 8 -> Case 3
sizeof(a) : 8 -> Case 4
sizeof(a[0]) : 8 -> Case 5
sizeof(a[1]) : 8 -> Case 6

Вопросы/наблюдения (в случае заказа):

  • Выводится ли случай 1, потому что b был объявлен как массив целых чисел, т.е. int[]? Полный блок в байтах возвращается как подтверждено Fact1. Не правда ли?

  • Мне кажется, что отличное отбрасывание b до int*. Здесь b считается указателем. Я подтвердил это с помощью Fact2. Правильно или неправильно?

  • &b[0] распадается на указатель b. Выход совпадает с Fact2.

  • I ожидаемый 16 здесь, но я получил 8 в качестве вывода. Я пришел к выводу, что это связано с тем, что a является последующим указателем, а выход совпадает с Fact2. Я получил результат, похожий на вопрос 2.

  • a[0] - указатель. Выход совпадает с Fact2

  • a[1] - указатель. Выход совпадает с Fact2

Пожалуйста, ответьте на вопросы и исправьте меня, если какое-либо из замечаний неверно.

4b9b3361

Ответ 1

Пожалуйста, ответьте на вопросы и исправьте меня, если какое-либо из наблюдений ошибочны.

  • Выход 1-го случая 20, потому что b был объявлен как array из int egers, т.е. int[]? Полный блок в байтах возвращается как подтверждено Fact1. Не правда ли?

Да, результат показывает sizeof(int [5]). Итак, из Fact1 размер 5*4

  1. Я думаю, что отличное отбрасывание b до int*. Здесь b считается указателем. Я подтвердил это с помощью Fact2. Правильно или неправильно?

Right. Но добавив больше информации: sizeof нужен только тип выражения, а не оценивать выражение (для значения), если оно не является типом VLA. (Из раздела 6.5.3.4 Оператор sizeof Спецификации C99)

Поскольку вы применяете приведение к окончательному результату, все, что после этого не имеет значения.

  1. &b[0] распадается на указатель b. Выход совпадает с Fact2.

Нет и да. Тип b[0] равен int, и, таким образом, тип &b[0] уже int * (напомним, что [...] привязывается сильнее, чем &). Нет распада. И да, выход совпадает с Fact2.

  1. Я ожидал здесь 16, но я получил 8 в качестве выхода. Я пришел к выводу, что это происходит потому, что a после этого указатель, а выход совпадает с Fact2. Я получил результат, похожий на вопрос 2.

a как указатель на массив 2 из int. Поэтому напечатанный размер имеет указатель (в массив int).

int (*a)[2]; объявляет a как указатель на массив 2 из int. Таким образом, вы получите размер pointer to array.

Чтобы получить желаемый результат (размер массива 2 указателей на int), используйте: int *a[2];

int (*a)[2];

a           anonymous
+----+      +----+----+
| a  |----->|int |int |
+----+      +----+----+

int *b[2];

b  
+----+----+
|int*|int*|
+----+----+
b[0] b[1]
  1. a[0] - указатель. Выход совпадает с Fact2
  2. a[2] - указатель. Вывод совпадает с Fact2

Как указано выше, a является указателем на массив 2 из int. Итак, a[index] - это массив 2, если int. Итак, тип a[0] и a[1] - это массив 2 из int. Таким образом, выход 2*4 из факта 1.
Возможно, не имеет отношения к этому ответу, но a не инициализирован и использование его в выражении вызовет undefined поведение. Хотя это нормально использовать в sizeof


Чтобы понять результат, проанализируйте тип аргумента sizeof

printf("sizeof(b) : %zu\n",sizeof(b));             // int [5]
printf("sizeof((int*)b) : %zu\n",sizeof((int*)b)); // int *
printf("sizeof(&b[0]) : %zu\n",sizeof(&b[0]));     // int *

printf("sizeof(a) : %zu\n",sizeof(a));             // int (*) [2]
printf("sizeof(a[0]) : %zu\n",sizeof(a[0]));       // int [2]
printf("sizeof(a[1]) : %zu\n",sizeof(a[1]));       // int [2]

A переносная программа (не надежная) для подтверждения типов выглядит следующим образом:

assert(sizeof(b) == sizeof(int [5]));
assert(sizeof((int*)b) == sizeof(int *));
assert(sizeof(&b[0]) == sizeof(int *));

assert(sizeof(a) == sizeof(int(*)[2]));
assert(sizeof(a[0]) == sizeof(int[2]));
assert(sizeof(a[1]) == sizeof(int[2]));

Ответ 2

Оператор sizeof - это одна из немногих вещей, которые могут различать массив (при условии, что он не является параметром функции) и указателем.

  • b распознается как массив из 5 элементов, каждый из которых имеет 4 байта, поэтому sizeof(b) оценивается до 20.
  • Листинг преобразует массив в указатель аналогично тому, как передать его функции. Размер 8.
  • На самом деле это не разлагает указатель. Это указатель. Вы берете адрес int, поэтому, конечно, тип int *. Обращаясь к одному из ваших комментариев, все равно неточно сказать, что выражение &b[0] распадается на указатель, если передать его функции, потому что на самом деле это указатель, а не массив.
  • Так как a является указателем на массив, размер представляет собой размер указателя, т.е. 8. Это отличается от int *c[2], который представляет собой массив указателей и имеет размер 16.
  • a[0] не является указателем, а массивом размера 2. Синтаксис a[0] эквивалентен *(a + 0). Так как a является указателем на массив, разыменование a дает нам массив. Поскольку каждый элемент имеет 4 байта, размер равен 8. Если a был определен как int (*a)[3], то sizeof(a[0]) оценивается до 12.
  • Подобно номеру 5, a[1] представляет собой массив размера 2. Таким образом, sizeof(a[1]) оценивается до 8, потому что это массив из двух элементов размером 4.

Пример использования a выглядит следующим образом:

int (*a)[2];
int d[3][2];

a=d;
d[0][0]=1;
d[0][1]=2;
d[1][0]=3;
d[1][1]=4;
d[2][0]=5;
d[3][1]=6;

printf("a00=%d\n",a[0][0]);
printf("a01=%d\n",a[0][1]);
printf("a10=%d\n",a[1][0]);
printf("a11=%d\n",a[1][1]);
printf("a20=%d\n",a[2][0]);
printf("a21=%d\n",a[3][1]);

Вывод:

a00=1
a01=2
a10=3
a11=4
a20=5
a21=6

Вы также используете это при передаче 2D-массива в функцию:

void f(int (*a)[2]) 
{
    ...
}

int main()
{
    int x[3][2];
    f(x);
}

Ответ 3

Вот несколько отдельных исследований по этому вопросу. Я проверил ваш тестовый код в четырех разных средах: двух 64-битных и двух 32-битных.
Я использовал три разных компилятора: llvm, gcc и mipsPro cc.
Вот прокомментированное сравнение результатов:

// 64-bit environment - all compilers
sizeof(int) :     4 -> Fact 1       -32 bit int -> 4 bytes   
sizeof(int*) :    8 -> Fact 2       -this and other pointers in a 64-bit system are 8-bytes long
sizeof(b) :      20 -> Case 1       -array of 5 32 bit ints -> 20 bytes
sizeof((int*)b) : 8 -> Case 2
sizeof(&b[0]) :   8 -> Case 3
sizeof(a) :       8 -> Case 4
sizeof(a[0]) :    8 -> Case 5       -array of two 4 byte ints
sizeof(a[1]) :    8 -> Case 6       -array of two 4 byte ints

// 32-bit environments - all compilers
sizeof(int) :     4 -> Fact 1       -32 bit int -> 4 bytes 
sizeof(int*) :    4 -> Fact 2       -this and other pointers in a 32-bit system are 4-bytes long
sizeof(b) :      20 -> Case 1       -array of 5 32 bit ints -> 20 bytes
sizeof((int*)b) : 4 -> Case 2
sizeof(&b[0]) :   4 -> Case 3
sizeof(a) :       4- > Case 4
sizeof(a[0]) :    8 -> Case 5       -array of two 4 byte ints
sizeof(a[1]) :    8 -> Case 6       -array of two 4 byte ints

Интерпретация - все результаты последовательно соответствуют следующему шаблону:

  • Размер int используется для зависимости от компилятора и, возможно, до сих пор, AFAIK. Это во всех тестируемых средах и компиляторах 4 байта (факт 1).
  • Размер всех указателей по умолчанию для среды - 64 бит или 32 бит (факт 2, случай 2, 3, 4).
  • Размер массива из двух четырехбайтовых ints равен 2*sizeof(int) (случай 5, 6).
  • a[0] можно переписать как *a; a[1] также может быть записано как *(a + 1). Следующий SO post подробно описывает его.

    Надеюсь, это может повлиять на вашу тему.