Чому розмір параметра масиву не такий, як у main?


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


1
У C ++ ви можете передавати масив за посиланням на функцію, але цього не можете зробити у C.
Prasoon Saurav

13
Вам потрібно буде передати розмір масиву як окремий параметр. Тоді розмір масиву буде розміром (* p_someArray) * довжина
Aric TenEyck

13
Незначна нитка: sizeofоператор повертає об'єкт типу 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

Показаний метод працює лише тоді, коли оголошення масиву "переглянуто". Для всіх інших випадків вам потрібно вручну пропустити довжину масиву.
СК Мадсен

5

Тому що масиви розпадаються на покажчики, коли вони передаються як параметри. Так працює C, хоча ви можете передати "масиви" в C ++ за посиланням та подолати це питання. Зауважте, що ви можете передавати масиви різного розміру до цієї функції:

 // 10 is superfluous here! You can pass an array of different size!
void PrintSize(int p_someArray[10]);

5

Знайдена вами поведінка - це насправді велика бородавка мовою С. Кожного разу, коли ви оголошуєте функцію, яка приймає параметр масиву, компілятор ігнорує вас і змінює параметр на покажчик. Отже всі ці декларації поводяться як перші:

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

a буде вказівником на int у всіх чотирьох випадках. Якщо ви передасте масив у функцію, він негайно розкладеться на вказівник на його перший елемент. (У 64-бітовій системі 64-розрядний вказівник вдвічі більший, ніж 32-розрядний int, тому ваше співвідношення sizeof повертається 2.)

Єдиною метою цього правила є підтримка зворотної сумісності з історичними компіляторами, які не підтримували передачу сукупних значень як аргументів функції.

Це не означає, що неможливо передати масив функції. Ви можете обійти цю бородавку, вставивши масив у структуру (це в основному мета C ++ 11's std :: масив):

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 */
}

4

У c ++ ви можете передати масив за посиланням саме для цієї мети:

void foo(int (&array)[10])
{
    std::cout << sizeof(array) << "\n";
}

1
Як би це допомогло у питанні С?
алк

3

У мові С не існує методу визначення розміру невідомого масиву, тому кількість потрібно передати, а також вказівник на перший елемент.


2
Загалом, ви завжди повинні передавати розмір (кількість елементів) масиву разом з масивом функції, якщо ви не маєте інших засобів визначення його розміру (наприклад, термінал з нульовим символом в кінці char[]масивів рядків).
David R Tribble

Будь ласка, що таке " невідомий масив "?
алк

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 немає " дзвінка по довідці ".
алк

" коли ви друкуєте розмір масиву, він надрукує розмір першого елемента. " ні, він друкує розмір вказівника.
алк

-1

У програмі 'C' languange 'sizeof ()' є оператором, і він повертає розмір об'єкта в байтах. Аргумент оператора 'sizeof ()' повинен бути лівим типом (ціле число, число з плаваючою ознакою, структура, масив ). Отже, якщо ви хочете знати розмір масиву в байтах, ви можете зробити це дуже просто. Просто скористайтеся оператором 'sizeof ()' і для його аргументу використовуйте ім'я масиву. Наприклад:

#include <stdio.h>

main(){

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

}

Вихід на 32-бітну систему буде: Розмір n дорівнює: 40. Через те, що коефіцієнт у 32-й системі становить 4 байти. На 64x це 8 байт. У цьому випадку в одному масиві задекларовано 10 цілих чисел. Таким чином, результат дорівнює '10 * sizeof ( int) '.

Деякі поради:

Якщо у нас є такий масив, оголошений як цей 'int n [] = {1, 2, 3, ... 155 ..};'. Отже ми хочемо знати, скільки елементів зберігається в цьому масиві. Використовуйте цей алгоритм:

sizeof (name_of_the_array) / sizeof (тип масиву)

Код: #include

main () {

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 () не має що продовжувати.


3
Вибачте, ця відповідь вводить в оману. Ні масиви не мають "вільного розміру", ні вони "покажчики на пам'ять". Масиви мають дуже точний розмір, і місця, де ім'я масиву означає вказівник на його перший елемент, точно визначено стандартом C.
Єнс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.