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


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));
}

Ответы:


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*)


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

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


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.
— Йенс
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.