2010-03-08 27 views
10

Làm cách nào để nhận được kích thước của mảng kiểu C một cách đáng tin cậy? Phương pháp thường được đề nghị có vẻ là để sử dụng sizeof, nhưng nó không hoạt động trong foo chức năng, nơi x được thông qua tại:Cách nhận được kích thước đáng tin cậy của mảng kiểu C?

#include <iostream> 

void foo(int x[]) { 
    std::cerr << (sizeof(x)/sizeof(int)); // 2 
} 

int main(){ 
    int x[] = {1,2,3,4,5}; 
    std::cerr << (sizeof(x)/sizeof(int)); // 5        
    foo(x); 
    return 0; 
} 

Câu trả lời cho this question khuyên bạn nên sizeof nhưng họ không nói rằng nó (rõ ràng ?) không hoạt động nếu bạn truyền mảng xung quanh. Vì vậy, tôi có phải sử dụng một sentinel thay thế? (Tôi không nghĩ rằng người dùng của chức năng foo của tôi luôn có thể được tin cậy để đặt một sentinel ở cuối. Tất nhiên, tôi có thể sử dụng std::vector, nhưng sau đó tôi không nhận được cú pháp viết tắt tốt đẹp {1,2,3,4,5}.)

+1

http://stackoverflow.com/questions/1810083/c-pointers-pointing-to-an-array-of-fixed-size – AnT

+0

Nếu người dùng chức năng của bạn không làm những gì các tài liệu bảo họ làm, đó là vấn đề của họ không phải của bạn. Nó là không thể ngăn chặn các clueless từ lạm dụng một API. –

+0

'x' trong' foo' là một con trỏ, chứ không phải là một mảng. Xem http://stackoverflow.com/questions/232691/how-can-i-get-the-size-of-an-array-from-a-pointer-in-c, http://stackoverflow.com/questions/1975128/sizeof-an-array-in-the-c-ngôn ngữ lập trình. Ngoài ra, dup: http://stackoverflow.com/questions/37538/c-how-do-i-determine-the-size-of-my-array – outis

Trả lời

15

Trong tham số mảng C trong C thực sự chỉ là con trỏ để sizeof() sẽ không hoạt động. Bạn cần phải vượt qua kích thước như một tham số khác hoặc sử dụng một sentinel - tùy theo điều nào phù hợp nhất với thiết kế của bạn.

Một số tùy chọn khác:

Một số thông tin khác:

  • cho C++, thay vì đi qua một con trỏ mảng liệu, bạn có thể muốn có một cái gì đó sử dụng tham số mà kết thúc tốt đẹp mảng trong một lớp học mẫu theo dõi kích thước mảng và cung cấp các phương thức sao chép dữ liệu vào mảng theo cách an toàn. Một cái gì đó như STLSoft's array_proxy template hoặc Boost's boost::array có thể hữu ích. Tôi đã sử dụng mẫu array_proxy để có hiệu ứng tốt đẹp trước đây. Bên trong hàm sử dụng tham số, bạn nhận được std::vector như các hoạt động, nhưng người gọi hàm có thể sử dụng một mảng C đơn giản. Không có sao chép mảng - mẫu array_proxy xử lý việc đóng gói con trỏ mảng và kích thước của mảng gần như tự động.

  • một macro để sử dụng trong C để xác định số phần tử trong một mảng (ví khi sizeof() có thể giúp đỡ - ví dụ, bạn đang phải đối phó với một con trỏ đơn giản.): Is there a standard function in C that would return the length of an array?

3

Bạn có thể vượt qua kích thước xung quanh, sử dụng một sentinel hoặc thậm chí tốt hơn sử dụng std :: vector. Mặc dù std :: vector thiếu danh sách khởi tạo nó vẫn còn dễ dàng để xây dựng một véc tơ với một tập hợp các yếu tố (mặc dù không phải là khá đẹp)

static const int arr[] = {1,2,3,4,5}; 
vector<int> vec (arr, arr + sizeof(arr)/sizeof(arr[0])); 

Lớp std :: vector cũng làm cho những sai lầm xa khó khăn hơn, mà có giá trị trọng lượng bằng vàng. Một tiền thưởng khác là tất cả C++ nên quen thuộc với nó và hầu hết các ứng dụng C++ nên sử dụng một std :: vector chứ không phải là một mảng C nguyên.

Như ghi chú nhanh, C++ 0x thêm Initializer lists

std::vector<int> v = {1, 2, 3, 4}; 

Bạn cũng có thể sử dụng Boost.Assign để làm điều tương tự mặc dù cú pháp là phức tạp hơn một chút.

std::vector<int> v = boost::assign::list_of(1)(2)(3)(4); 

hoặc

std::vector<int> v; 
v += 1, 2, 3, 4; 
2

c không cung cấp hỗ trợ bản địa cho việc này. Khi một mảng được chuyển ra khỏi phạm vi được khai báo của nó, kích thước của nó bị mất.

Bạn có thể chuyển kích thước với mảng. Bạn thậm chí có thể gói chúng vào một cấu trúc nếu bạn luôn luôn giữ kích thước, mặc dù bạn sẽ có một số chi phí bookkeepping với điều đó.

1

Làm thế nào về điều này? ..

template <int N> 
void foo(int (&x)[N]) { 
    std::cerr << N; 
}
+0

-1: Đây là một ý tưởng khủng khiếp. – Brian

+1

@Brian: Có thể hoặc không phải là một ý tưởng tồi, tùy thuộc vào ngữ cảnh. Tại sao bạn nghĩ rằng nó là khủng khiếp? Đây là một ý tưởng hay vì nó sẽ KHÔNG TÍNH nếu bạn vượt qua một con trỏ. Sẽ rất tệ nếu hàm lớn và được gọi với nhiều kích cỡ mảng khác nhau - nó sẽ tạo ra một loạt các hàm. Một thay thế có thể là cho foo này sau đó quay lại và gọi foo_internal riêng/nội bộ (int * x, size_t length); Nó phụ thuộc vào sử dụng. Tôi đã tìm thấy kỹ thuật này hữu ích với các hàm chuỗi có xu hướng tràn khác nếu sử dụng không đúng cách. Nhận xét không có giải thích là một ý tưởng khủng khiếp. – tony

0

Bạn cần phải vượt qua quy mô cùng với mảng, giống như nó được thực hiện trong nhiều chức năng thư viện, ví dụ strncpy(), strncmp(), vv Xin lỗi, đây chỉ là cách nó hoạt động trong C :-).

Hoặc bạn có thể tung ra cấu trúc riêng của bạn như:

struct array { 
    int* data; 
    int size; 
}; 

và vượt qua nó xung quanh mã của bạn.

Tất nhiên bạn vẫn có thể sử dụng std::list hoặc std::vector nếu bạn muốn có nhiều C++-hơn.

4

Một common idiom đề cập trong GNU libstdC++ tài liệu là lengthof chức năng:

template<typename T, unsigned int sz> 
inline unsigned int lengthof(T (&)[sz]) { return sz; } 

Bạn có thể sử dụng nó như

int x[] = {1,2,3,4,5}; 
std::cerr << lengthof(x) << std::endl; 

Cảnh báo: điều này sẽ chỉ làm việc khi mảng có không decayed into a pointer.

1

Biểu thức mảng sẽ được chuyển đổi từ loại "thành phần chữ N thành" thành "con trỏ đến T" và giá trị của nó sẽ là địa chỉ của phần tử đầu tiên trong mảng, trừ khi biểu thức mảng là toán hạng của các toán tử sizeof hoặc địa chỉ-of (&) hoặc nếu biểu thức mảng là chuỗi ký tự được sử dụng để khởi tạo mảng khác trong khai báo. Tóm lại, bạn không thể chuyển mảng tới hàm dưới dạng mảng; hàm nhận được là giá trị con trỏ, không phải là giá trị mảng.

Bạn phải vượt qua kích thước mảng dưới dạng tham số riêng biệt.

Vì bạn đang sử dụng C++, hãy sử dụng các vectơ (hoặc một số vùng chứa STL phù hợp khác) thay vì các mảng kiểu C. Có, bạn mất cú pháp viết tắt tiện dụng, nhưng sự cân bằng thì đáng giá hơn nó. Nghiêm túc.

2

Tôi cũng đồng ý rằng phương pháp của Corwin ở trên rất tốt.

template <int N> 
void foo(int (&x)[N]) 
{ 
    std::cerr << N; 
} 

Tôi không nghĩ ai đã đưa ra lý do thực sự tốt vì sao đây không phải là ý hay.
Trong java, ví dụ, chúng ta có thể viết những thứ như:

int numbers [] = {1, 2, 3, 4}; 
for(int i = 0; i < numbers.length(); i++) 
{ 
    System.out.println(numbers[i]+"\n"); 
} 

Trong C++ chúng ta sẽ được tốt đẹp thay vì nói

int numbers [] = {1, 2, 3, 4}; 
int size = sizeof(numbers)/sizeof(int); 
for(int i = 0; i < size; i++) 
{ 
    cout << numbers[i] << endl; 
} 

Chúng ta có thể mang nó một bước xa hơn và đi

template <int N> 
int size(int (&X)[N]) 
{ 
    return N; 
} 

Hoặc nếu điều đó gây ra sự cố, tôi cho rằng bạn có thể viết rõ ràng:

template < int N > 
int size(int (&X)[N]) 
{ 
    int value = (sizeof(X)/sizeof(X[0])); 
    return value; 
} 

Sau đó, chúng tôi chỉ phải đi ở chính:

int numbers [] = {1, 2, 3, 4}; 
for(int i = 0; i < size(numbers); i++) 
{ 
    cout << numbers[i] << endl; 
} 

có ý nghĩa với tôi :-)

0

Kể từ C++ 11, có một cách rất thuận tiện:

static const int array[] = { 1, 2, 3, 6 }; 
int size = (int)std::distance(std::begin(array), std::end(array))+1; 
Các vấn đề liên quan