Почему размер параметра массива не такой же, как у основного?

104

Почему размер массива, отправляемого в качестве параметра, не такой же, как в main?

#include <stdio.h>

void PrintSize(int p_someArray[10]);

int main () {
    int myArray[10];
    printf("%d\n", sizeof(myArray)); /* As expected, 40 */
    PrintSize(myArray);/* Prints 4, not 40 */
}

void PrintSize(int p_someArray[10]){
    printf("%d\n", sizeof(p_someArray));
}
Chris_45
источник

Ответы:

103

Тип массива неявно преобразуется в тип указателя, когда вы передаете его функции.

Так,

void PrintSize(int p_someArray[10]) {
    printf("%zu\n", sizeof(p_someArray));
}

и

void PrintSize(int *p_someArray) {
    printf("%zu\n", sizeof(p_someArray));
}

эквивалентны. Итак, вы получаете ценностьsizeof(int*)

Prasoon Saurav
источник
2
В C ++ вы можете передать массив по ссылке на функцию, но не можете сделать это в C.
Prasoon Saurav
13
Вам нужно будет передать размер массива как отдельный параметр. Тогда размер массива будет sizeof (* p_someArray) * length
Aric TenEyck
14
Minor nit: sizeofoperator возвращает объект типа size_t, поэтому вы должны распечатать его с помощью %zu(C99) или привести его, intесли вы используете %dв своих printfвызовах, как указано выше .
Алок Сингхал,
4
Утверждение Алока верно. Использование неверного спецификатора формата в printf (..) - UB.
Prasoon Saurav
1
@ Chris_45: C не имеют ссылок, но в C вы можете передать массив по указателю на весь массив как в: void PrintSize(int (*p_someArray)[10]). Внутри функции вы можете получить доступ к массиву с помощью оператора разыменования *: sizeof(*p_someArray). Это будет иметь тот же эффект, что и использование ссылок в C ++.
AnT
18

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

user240312
источник
16

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

#include <stdio.h>

void PrintSize1 ( int someArray[][10] );
void PrintSize2 ( int someArray[10] );

int main ()
{
    int myArray[10];
    printf ( "%d\n", sizeof myArray ); /* as expected 40 */
    printf ( "%d\n", sizeof ( int[10] ) ); /* requires parens */
    PrintSize1 ( 0 ); /* prints 40, does not evaluate 0[0] */
    PrintSize2 ( 0 ); /* prints 40, someArray unused */
}

void PrintSize1 ( int someArray[][10] )
{
    printf ( "%d\n", sizeof someArray[0] );
}

void PrintSize2 ( int someArray[10] )
{
    printf ( "%d\n", sizeof ( int[10] ) );
}
Пит Киркхэм
источник
12

Итак, вам нужно будет передать длину массива в качестве второго параметра. Когда вы пишете код, в котором вы оба объявляете массив постоянного размера, а затем передаете этот массив функции, очень сложно, чтобы константа длины массива отображалась в нескольких местах вашего кода ...

K&R спешат на помощь:

#define N_ELEMENTS(array) (sizeof(array)/sizeof((array)[0])) 

Итак, теперь вы можете сделать, например:

int a[10];
...
myfunction(a, N_ELEMENTS(a));
СК Мадсен
источник
что, если размер массива недоступен во время кодирования, а доступен только во время выполнения? Есть ли другой способ рассчитать размер массива без жесткого кодирования его размера?
weefwefwqg3
Показанный метод работает только тогда, когда объявление массива «на виду». Во всех остальных случаях вам нужно вручную передать длину массива.
SC Madsen
5

Поскольку массивы распадаются на указатели, когда они передаются в качестве параметров. Вот как работает C, хотя вы можете передавать «массивы» в C ++ по ссылке и решить эту проблему. Обратите внимание, что в эту функцию можно передавать массивы разного размера:

 // 10 is superfluous here! You can pass an array of different size!
void PrintSize(int p_someArray[10]);
Арак
источник
5

В C ++ вы можете передать массив по ссылке именно для этой цели:

void foo(int (&array)[10])
{
    std::cout << sizeof(array) << "\n";
}
Александр К.
источник
1
Как это поможет с вопросом на C?
alk
5

Обнаруженное вами поведение на самом деле является большой бородавкой на языке C. Каждый раз, когда вы объявляете функцию, которая принимает параметр массива, компилятор игнорирует вас и изменяет параметр на указатель. Итак, все эти объявления ведут себя как первое:

void func(int *a)
void func(int a[])
void func(int a
typedef int array_plz[5];
void func(array_plz a)

a будет указателем на int во всех четырех случаях. Если вы передадите массив в func, он немедленно превратится в указатель на свой первый элемент. (В 64-битной системе 64-битный указатель вдвое больше 32-битного int, поэтому ваше соотношение sizeof возвращает 2.)

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

Это не означает, что невозможно передать массив функции. Вы можете обойти эту бородавку, встроив массив в структуру (в основном это цель std :: array в C ++ 11):

struct array_rly {
int a[5];
};
void func(struct array_rly a)
{
printf("%zd\n", sizeof(a.a)/sizeof(a.a[0]));  /* prints 5 */
}

или передав указатель на массив:

void func(const int (*a)[5])
{
printf("%zd\n", sizeof(*a)/sizeof((*a)[0]));  /* prints 5 */
}

Если размер массива не является константой времени компиляции, вы можете использовать технику указателя на массив с массивами переменной длины C99:

void func(int n, const int (*a)[n])
{
printf("%zd\n", sizeof(*a)/sizeof((*a)[0]));  /* prints n */
}
Уолтер
источник
3

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


источник
2
В общем, вы всегда должны передавать размер (количество элементов) массива вместе с массивом в функцию, если только у вас нет других средств определения его размера (например, терминатор нулевого символа в конце char[]строковых массивов).
Дэвид Р. Триббл,
Пожалуйста, что такое " неизвестный массив "?
alk
3

Вы не можете передавать массивы функциям.

Если вы действительно хотите напечатать размер, вы можете передать указатель на массив, но он не будет универсальным, поскольку вам также необходимо определить размер массива для функции.

#include <stdio.h>

void PrintSize(int (*p_anArray)[10]);

int main(void) {
    int myArray[10];
    printf("%d\n", sizeof(myArray)); /* as expected 40 */
    PrintSize(&myArray);/* prints 40 */
}

void PrintSize(int (*p_anArray)[10]){
    printf("%d\n", (int) sizeof(*p_anArray));
}
Г-Н.
источник
0

Такое поведение было задумано.

Одинаковый синтаксис в объявлении параметра функции означает совершенно другое, чем в определении локальной переменной.

Причина описана в других ответах.

Павел Радзивиловский
источник
0

В языке C, когда вы передаете массив в качестве аргумента функции, он автоматически преобразуется в указатель, а массив, передаваемый из одной функции, другая функция называется вызовом по ссылке. По этой причине вызываемая функция получает только указатель, указывающий на первый элемент функции. Это причина

fun (int a []) похож на fun (int * a);

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

даграваль
источник
В C нет « вызова по ссылке ».
alk
« когда вы печатаете размер массива, он печатает размер первого элемента». Нет, он печатает размер указателя.
alk
-1

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

#include <stdio.h>

main(){

 int n[10];
 printf("Size of n is: %d \n", sizeof(n)); 

}

Вывод в 32-битной системе будет: Размер n: 40. Так как в 32-битной системе размер равен 4 байта. В 64-битной системе это 8 байтов. В этом случае у нас объявлено 10 целых чисел в одном массиве. Таким образом, результат будет '10 * sizeof ( int) '.

Несколько советов:

Если у нас есть массив, объявленный как этот 'int n [] = {1, 2, 3, ... 155 ..};'. Итак, мы хотим знать, сколько элементов хранится в этом массиве. Используйте этот алгоритм:

sizeof (имя_массива) / sizeof (тип_массива)

Код: #include

основной(){

int n[] = { 1, 2, 3, 44, 6, 7 };
printf("Number of elements: %d \n", sizeof(n) / sizeof(int));
return 0;

}

Ричард
источник
2
Добро пожаловать в StackOverflow и спасибо, что написали ответ. К сожалению, это не решает вопрос, который, в частности, касается разницы между sizeof(n)локальной переменной и sizeof(arg)аргументом функции, хотя оба они кажутся типовыми int[10].
MicroVirus
-3

Размеры массивов невелики. По большей части массив - это указатель на память. Размер в вашем объявлении только сообщает компилятору, сколько памяти выделить для массива - он не связан с типом, поэтому sizeof () нечего делать.

плинтус
источник
4
Извините, этот ответ вводит в заблуждение. Массивы также не являются «произвольными по размеру» и не «указателями на память». Массивы имеют очень точный размер, и места, где имя массива обозначает указатель на его первый элемент, точно определены стандартом C.
Йенс