Tại sao kích thước của một tham số mảng không giống như trong main?


104

Tại sao kích thước của một mảng được gửi dưới dạng tham số không giống như trong 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));
}

Câu trả lời:


103

Kiểu mảng được chuyển đổi hoàn toàn thành kiểu con trỏ khi bạn chuyển nó vào một hàm.

Vì thế,

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

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

là tương đương. Vì vậy, những gì bạn nhận được là giá trị củasizeof(int*)


1
Trong C ++, bạn có thể vượt qua mảng bằng cách tham chiếu đến hàm nhưng bạn không thể làm điều đó trong C.
Prasoon Saurav

13
Bạn cần phải chuyển kích thước của mảng dưới dạng một tham số riêng biệt. Sau đó, kích thước của mảng sẽ là sizeof (* p_someArray) * chiều dài
Aric TenEyck

13
Minor nit: sizeoftoán tử trả về một đối tượng kiểu size_t, vì vậy bạn nên in nó bằng %zu(C99) hoặc truyền nó tới intnếu bạn sử dụng %dnhư trên trong các printfcuộc gọi của mình .
Alok Singhal

4
Câu nói của Alok là đúng. Sử dụng định dạng không chính xác trong printf (..) là UB.
Prasoon Saurav

1
@ Chris_45: C không có tài liệu tham khảo, nhưng trong C bạn có thể vượt qua một mảng bằng con trỏ đến toàn bộ mảng như trong: void PrintSize(int (*p_someArray)[10]). Các bên trong hàm bạn có thể truy cập vào mảng bằng cách sử dụng toán tử tham chiếu *: sizeof(*p_someArray). Điều này sẽ có tác dụng tương tự như sử dụng tham chiếu trong C ++.
AnT

18

Đó là một con trỏ, đó là lý do tại sao cách triển khai phổ biến là truyền kích thước của mảng làm tham số thứ hai cho hàm


16

Như những người khác đã nêu, mảng phân rã thành con trỏ đến phần tử đầu tiên của chúng khi được sử dụng làm tham số hàm. Cũng cần lưu ý rằng sizeof không đánh giá biểu thức và không yêu cầu dấu ngoặc đơn khi được sử dụng với một biểu thức, do đó, tham số của bạn thực sự không được sử dụng, vì vậy bạn cũng có thể viết sizeof với kiểu thay vì giá trị.

#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

Vì vậy, bạn sẽ cần chuyển độ dài của mảng làm tham số thứ hai. Khi bạn đang viết mã, trong đó bạn vừa khai báo một mảng có kích thước không đổi, và sau đó truyền mảng đó cho một hàm, thật khó để hằng số độ dài mảng hiển thị một số vị trí trong mã của bạn ...

K&R để giải cứu:

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

Vì vậy, bây giờ bạn có thể làm ví dụ:

int a[10];
...
myfunction(a, N_ELEMENTS(a));

điều gì sẽ xảy ra nếu kích thước của mảng không khả dụng tại thời điểm mã hóa mà chỉ khả dụng tại thời điểm chạy? Có cách nào khác để tính kích thước của mảng mà không cần mã hóa kích thước của nó không?
frofwefwqg3

Phương thức được hiển thị chỉ hoạt động khi khai báo mảng là "trong chế độ xem". Đối với tất cả các trường hợp khác, bạn phải chuyển độ dài mảng theo cách thủ công.
SC Madsen

5

Bởi vì mảng phân rã thành con trỏ khi chúng được truyền dưới dạng tham số. Đây là cách C hoạt động, mặc dù bạn có thể truyền "mảng" trong C ++ bằng cách tham chiếu và khắc phục vấn đề này. Lưu ý rằng bạn có thể chuyển các mảng có kích thước khác nhau vào hàm này:

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

5

Hành vi bạn tìm thấy thực sự là một mụn cóc lớn trong ngôn ngữ C. Bất cứ khi nào bạn khai báo một hàm có tham số mảng, trình biên dịch sẽ bỏ qua bạn và thay đổi tham số thành con trỏ. Vì vậy, tất cả các khai báo này đều hoạt động giống như khai báo đầu tiên:

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

a sẽ là một con trỏ tới int trong cả bốn trường hợp. Nếu bạn chuyển một mảng cho func, nó sẽ ngay lập tức phân rã thành một con trỏ tới phần tử đầu tiên của nó. (Trên hệ thống 64 bit, con trỏ 64 bit lớn gấp đôi so với int 32 bit, vì vậy tỷ lệ sizeof của bạn trả về 2)

Mục đích duy nhất của quy tắc này là duy trì khả năng tương thích ngược với các trình biên dịch lịch sử không hỗ trợ chuyển các giá trị tổng hợp làm đối số hàm.

Điều này không có nghĩa là không thể truyền một mảng cho một hàm. Bạn có thể giải quyết vấn đề này bằng cách nhúng mảng vào một cấu trúc (về cơ bản đây là mục đích của std :: array của 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 */
}

hoặc bằng cách chuyển một con trỏ đến mảng:

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

Trong trường hợp kích thước mảng không phải là hằng số thời gian biên dịch, bạn có thể sử dụng kỹ thuật con trỏ đến mảng với mảng có độ dài thay đổi C99:

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

4

Trong c ++, bạn có thể chuyển một mảng bằng tham chiếu cho mục đích này:

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

1
Điều này sẽ giúp ích như thế nào với một câu hỏi C?
alk

3

Trong ngôn ngữ C, không có phương pháp nào để xác định kích thước của một mảng chưa biết, vì vậy số lượng cần được truyền cũng như một con trỏ đến phần tử đầu tiên.


2
Nói chung, bạn phải luôn chuyển kích thước (số phần tử) của một mảng cùng với một mảng cho một hàm, trừ khi bạn có một số phương tiện khác để xác định kích thước của nó (ví dụ: dấu chấm dứt ký tự null ở cuối char[]mảng chuỗi).
David R Tribble

Xin vui lòng " một mảng không xác định " là gì?
alk

3

Bạn không thể chuyển mảng cho các hàm.

Nếu bạn thực sự muốn in kích thước, bạn có thể chuyển một con trỏ đến một mảng, nhưng nó sẽ không chung chung chút nào vì bạn cũng cần xác định kích thước mảng cho hàm.

#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

Hành vi là do thiết kế.

Cú pháp tương tự trong khai báo tham số hàm có nghĩa là hoàn toàn khác so với trong định nghĩa biến cục bộ.

Lý do được mô tả trong các câu trả lời khác.


0

Trong ngôn ngữ C, khi bạn truyền mảng làm đối số cho hàm, nó sẽ tự động được chuyển đổi thành con trỏ, mảng truyền từ một hàm này đến hàm khác được gọi là cuộc gọi bằng tham chiếu. Đó là lý do tại sao hàm được gọi chỉ nhận được con trỏ trỏ đến phần tử đầu tiên của hàm Đây là lý do

fun (int a []) tương tự như fun (int * a);

vì vậy khi bạn in kích thước của mảng, nó sẽ in kích thước của phần tử đầu tiên.


Trong C không có " cuộc gọi bằng cách tham chiếu ".
alk

" khi bạn in kích thước của mảng, nó sẽ in kích thước của phần tử đầu tiên. " Không, nó in kích thước của một con trỏ.
alk

-1

Trong lập trình 'C', uuange 'sizeof ()' là toán tử và trả về kích thước của đối tượng tính bằng byte. Đối số của toán tử 'sizeof ()' phải là kiểu giá trị bên trái (số nguyên, số thực, cấu trúc, mảng Vì vậy, nếu bạn muốn biết kích thước của một mảng tính bằng byte, bạn có thể làm điều đó rất đơn giản, chỉ cần sử dụng toán tử 'sizeof ()' và đối với đối số của anh ta, hãy sử dụng tên mảng. Ví dụ:

#include <stdio.h>

main(){

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

}

Kết quả đầu ra trên hệ thống 32 bit sẽ là: Kích thước của n là: 40 Vì trong hệ thống 32 bit là 4byte, trên 64x là 8byte, trong trường hợp này chúng ta có 10 số nguyên được khai báo trong một mảng. Vì vậy, kết quả là '10 * sizeof ( int) '.

Một số lời khuyên:

Nếu chúng ta có một mảng được khai báo như thế này 'int n [] = {1, 2, 3, ... 155 ..};'. Vì vậy, chúng tôi muốn biết có bao nhiêu phần tử được lưu trữ trong mảng này. Sử dụng thuật toán này:

sizeof (name_of_the_array) / sizeof (array_type)

Mã: #include

chủ yếu(){

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

}


2
Chào mừng bạn đến với StackOverflow và cảm ơn bạn đã viết câu trả lời. Thật không may, điều này không giải quyết được câu hỏi, đặc biệt là về sự khác biệt giữa sizeof(n)biến cục bộ và sizeof(arg)đối số của một hàm, mặc dù cả hai dường như đều thuộc kiểu int[10].
MicroVirus

-3

Mảng chỉ có kích thước lỏng lẻo. Đối với hầu hết các phần, một mảng là một con trỏ tới bộ nhớ. Kích thước trong khai báo của bạn chỉ cho trình biên dịch biết có bao nhiêu bộ nhớ cần phân bổ cho mảng - nó không được liên kết với kiểu, vì vậy sizeof () không có gì để tiếp tục.


3
Xin lỗi, câu trả lời này gây hiểu lầm. Mảng không phải là "có kích thước lỏng lẻo", cũng không phải là "con trỏ tới bộ nhớ". Mảng có kích thước rất chính xác và những vị trí mà tên mảng là viết tắt của một con trỏ đến phần tử đầu tiên của nó được chỉ định chính xác bởi tiêu chuẩn C.
Jens
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.