Как указатель массива хранит свой размер?

#include "stdio.h" #define COUNT(a) (sizeof(a) / sizeof(*(a))) void test(int b[]) { printf("2, count:%d\n", COUNT(b)); } int main(void) { int a[] = { 1,2,3 }; printf("1, count:%d\n", COUNT(a)); test(a); return 0; } 

Результат очевиден:

 1, count:3 2, count:1 

Мои вопросы:

  1. Где хранится информация о длине (счет / размер), когда объявляется «a»?
  2. Почему информация о длине (счет / размер) теряется, когда «a» передается функции test ()?

В языке C нет такой вещи, как «указатель массива».

Размер не сохраняется нигде. a не является указателем, a является объектом типа int[3] , который является фактом, хорошо известным компилятору во время компиляции. Итак, когда вы попросите компилятор рассчитать sizeof(a) / sizeof(*a) во время компиляции, компилятор знает, что ответ равен 3 .

Когда вы передаете свою a вы намеренно просите компилятор преобразовать тип массива в тип указателя (поскольку вы объявили параметр функции как указатель). Для указателей выражение sizeof дает совершенно другой результат.

  1. Где хранится информация о длине (счет / размер), когда объявляется «a»?

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

  1. Почему информация о длине (счет / размер) теряется, когда «a» передается функции test ()?

К сожалению, в C параметры массива для функций являются фикцией. Массивы не передаются в функции; параметр обрабатывается как указатель, а аргумент массива, переданный в вызове функции, получает «затухает» в простой указатель. Оператор sizeof возвращает размер указателя, который не имеет корреляции с размером массива, который использовался в качестве аргумента.

В качестве дополнительной заметки в C ++ вы можете иметь параметр функции, являющийся ссылкой на массив, и в этом случае полный тип массива становится доступным для функции (т. Е. Аргумент не распадается на указатель, а sizeof вернется размер полного массива). Однако в этом случае аргумент должен точно соответствовать типу массива (включая количество элементов), что делает технику в основном полезной только с шаблонами.

Например, следующая программа на C ++ будет делать то, что вы ожидаете:

 #include "stdio.h" #define COUNT(a) (sizeof(a) / sizeof(*(a))) template  void test(int (&b)[T]) { printf("2, count:%d\n", COUNT(b)); } int main(int argc, char *argv[]) { int a[] = { 1,2,3 }; printf("1, count:%d\n", COUNT(a)); test(a); return 0; } 
  1. Нигде.
  2. Потому что он не был сохранен в первую очередь.

Когда вы ссылаетесь на массив в main() , фактическое определение объявления массива видимо, поэтому sizeof(a) задает размер массива в байтах.

Когда вы ссылаетесь на массив в функции, параметр эффективно « void test(int *b) , а размер указателя, деленный на размер вещи, на которую он указывает, составляет 1 на 32-битной платформе, тогда как это было бы 2 на 64-битной платформе с архитектурой LP64 (или, действительно, на платформе LLP64, такой как Windows-64), поскольку указатели составляют 8 байтов, а int – 4 байта.

Существует не универсальный способ определения размера массива, переданного в функцию; вы должны передать его явно и вручную.


Из комментария:

У меня все еще есть два вопроса:

  1. Что вы подразумеваете под «.. фактическое объявление видно …»? [T] компилятор (или ОС) мог получить информацию о длине через функцию sizeof (a)?
  2. Почему указатель & (a [0]) не содержит информацию о длине как указатель «a»?
  1. Я думаю, вы изучили Java, прежде чем научились C, или какой-то другой более современный язык. В конечном счете, это сводится к «потому что именно так определяется C». ОС не задействована; это чисто компилятор.

    • sizeof() является оператором, а не функцией. Если вы не имеете дело с VLA (массив переменной длины), он вычисляется во время компиляции и является постоянным значением.

    Внутри main() определение массива (я имею в виду, когда я сказал «объявление»), есть и когда оператор sizeof() применяется к имени фактического массива – в отличие от параметра массива к функции, – тогда Возвращаемый размер – это размер массива в байтах.

  2. Потому что это C, а не Algol, Pascal, Java, C #, …

    C не сохраняет размер массива – период. Это факт жизни. И, когда массив передается функции, информация о размере не передается функции; массив «распадается» на указатель на нулевой элемент массива – и передается только этот указатель.

 1. Where is the length(count/size) info stored when "a" is declared? 

Он не сохраняется. Компилятор знает, что есть, и поэтому знает, что это размер. Таким образом, компилятор может заменить sizeof() на фактический размер.

 2. Why is the length(count/size) info lost when "a" is passed to the test() function? 

В этом случае b объявляется как указатель (хотя он может указывать на a). Учитывая указатель, компилятор не знает размер указанных данных.

  1. Где хранится информация о длине (счет / размер), когда объявляется «a»?

Нигде. Вопрос не имеет смысла BTW.

  1. Почему информация о длине (счет / размер) теряется, когда «a» передается функции test ()?

Массив распадается на указатель (на первый элемент) при передаче функции. Таким образом, ответ «нигде» и подобен предыдущему вопросу, этот снова не имеет никакого смысла.

Указатель массива не сохраняет размер. Однако тип [] самом деле не является указателем. Это другой тип. Когда вы говорите int a[] = {1,2,3}; вы определяете массив из 3 элементов, и поскольку он определен так, sizeof (a) дает вам размер всего массива.

Однако, если вы объявляете параметр как int a [], он почти такой же, как int * a, а sizeof (a) будет размером указателя (который по совпадению может быть таким же, как размер int , но не всегда) ,

В C нет способа сохранить размер в типе указателя, поэтому, если вам нужен размер, вам придется передать его в качестве дополнительного аргумента или использовать struct .