Làm cách nào để xác định kích thước của mảng trong C?
Đó là, số phần tử mà mảng có thể giữ?
Làm cách nào để xác định kích thước của mảng trong C?
Đó là, số phần tử mà mảng có thể giữ?
Câu trả lời:
Tóm tắt điều hành:
int a[17];
size_t n = sizeof(a)/sizeof(a[0]);
Câu trả lời đầy đủ:
Để xác định kích thước của mảng theo byte, bạn có thể sử dụng sizeof
toán tử:
int a[17];
size_t n = sizeof(a);
Trên máy tính của tôi, ints dài 4 byte, vì vậy n là 68.
Để xác định số lượng phần tử trong mảng, chúng ta có thể chia tổng kích thước của mảng cho kích thước của phần tử mảng. Bạn có thể làm điều này với loại, như thế này:
int a[17];
size_t n = sizeof(a) / sizeof(int);
và nhận được câu trả lời thích hợp (68/4 = 17), nhưng nếu loại
a
thay đổi, bạn sẽ gặp một lỗi khó chịu nếu bạn quên thay đổi sizeof(int)
.
Vì vậy, ước số được ưu tiên là sizeof(a[0])
hoặc tương đương sizeof(*a)
, kích thước của phần tử đầu tiên của mảng.
int a[17];
size_t n = sizeof(a) / sizeof(a[0]);
Một ưu điểm khác là bây giờ bạn có thể dễ dàng tham số hóa tên mảng trong macro và nhận:
#define NELEMS(x) (sizeof(x) / sizeof((x)[0]))
int a[17];
size_t n = NELEMS(a);
ARRAYSIZE
makro được xác định trong WinNT.h
(được kéo bởi các tiêu đề khác). Vì vậy, người dùng WinAPI không cần xác định makro của riêng họ.
static int a[20];
. Nhưng nhận xét của bạn rất hữu ích cho những người đọc có thể không nhận ra sự khác biệt giữa một mảng và một con trỏ.
Các sizeof
cách là con đường đúng khi và chỉ khi bạn đang đối phó với mảng chưa nhận được như các thông số. Một mảng được gửi dưới dạng tham số cho hàm được coi là một con trỏ, do đó sizeof
sẽ trả về kích thước của con trỏ, thay vì của mảng.
Vì vậy, bên trong các chức năng phương pháp này không hoạt động. Thay vào đó, luôn luôn vượt qua một tham số bổ sung size_t size
cho biết số lượng phần tử trong mảng.
Kiểm tra:
#include <stdio.h>
#include <stdlib.h>
void printSizeOf(int intArray[]);
void printLength(int intArray[]);
int main(int argc, char* argv[])
{
int array[] = { 0, 1, 2, 3, 4, 5, 6 };
printf("sizeof of array: %d\n", (int) sizeof(array));
printSizeOf(array);
printf("Length of array: %d\n", (int)( sizeof(array) / sizeof(array[0]) ));
printLength(array);
}
void printSizeOf(int intArray[])
{
printf("sizeof of parameter: %d\n", (int) sizeof(intArray));
}
void printLength(int intArray[])
{
printf("Length of parameter: %d\n", (int)( sizeof(intArray) / sizeof(intArray[0]) ));
}
Đầu ra (trong HĐH Linux 64 bit):
sizeof of array: 28
sizeof of parameter: 8
Length of array: 7
Length of parameter: 2
Đầu ra (trong hệ điều hành windows 32 bit):
sizeof of array: 28
sizeof of parameter: 4
Length of array: 7
Length of parameter: 1
length of parameter:2
nếu chỉ một con trỏ đến phần tử mảng 1 được thông qua?
(sizeof array / sizeof *array)
.
Điều đáng chú ý là sizeof
không giúp ích gì khi xử lý một giá trị mảng đã phân rã thành một con trỏ: mặc dù nó trỏ đến phần bắt đầu của một mảng, nhưng trình biên dịch nó giống như một con trỏ tới một phần tử của mảng đó . Một con trỏ không "nhớ" bất cứ điều gì khác về mảng đã được sử dụng để khởi tạo nó.
int a[10];
int* p = a;
assert(sizeof(a) / sizeof(a[0]) == 10);
assert(sizeof(p) == sizeof(int*));
assert(sizeof(*p) == sizeof(int));
char
32 bit. Tất cả các tiêu chuẩn nói là các giá trị nguyên từ 0 đến 127 có thể được biểu diễn và phạm vi của nó ít nhất là -127 đến 127 (ký hiệu char) hoặc 0 đến 255 (ký tự không dấu).
"Thủ thuật" sizeof là cách tốt nhất mà tôi biết, với một điều nhỏ nhưng (với tôi, đây là một tiểu cảnh thú cưng lớn) thay đổi quan trọng trong việc sử dụng dấu ngoặc đơn.
Vì mục nhập Wikipedia cho thấy rõ, C's sizeof
không phải là một chức năng; đó là một nhà điều hành . Vì vậy, nó không yêu cầu dấu ngoặc đơn xung quanh đối số của nó, trừ khi đối số là tên loại. Điều này rất dễ nhớ, vì nó làm cho đối số trông giống như một biểu thức truyền, cũng sử dụng dấu ngoặc đơn.
Vì vậy: Nếu bạn có những điều sau đây:
int myArray[10];
Bạn có thể tìm thấy số lượng phần tử với mã như thế này:
size_t n = sizeof myArray / sizeof *myArray;
Điều đó, với tôi, đọc dễ dàng hơn nhiều so với thay thế bằng dấu ngoặc đơn. Tôi cũng thích sử dụng dấu hoa thị ở phần bên phải của bộ phận, vì nó ngắn gọn hơn là lập chỉ mục.
Tất nhiên, đây cũng là thời gian biên dịch, vì vậy không cần phải lo lắng về việc phân chia ảnh hưởng đến hiệu suất của chương trình. Vì vậy, sử dụng hình thức này bất cứ nơi nào bạn có thể.
Tốt nhất là luôn sử dụng sizeof trên một đối tượng thực tế khi bạn có một đối tượng, thay vì trên một loại, từ đó bạn không cần phải lo lắng về việc gây ra lỗi và nêu sai loại.
Chẳng hạn, giả sử bạn có một hàm xuất một số dữ liệu dưới dạng luồng byte, ví dụ trên toàn mạng. Chúng ta hãy gọi hàm send()
và làm cho nó lấy làm đối số một con trỏ tới đối tượng cần gửi và số byte trong đối tượng. Vì vậy, nguyên mẫu trở thành:
void send(const void *object, size_t size);
Và sau đó bạn cần gửi một số nguyên, vì vậy bạn mã hóa nó như thế này:
int foo = 4711;
send(&foo, sizeof (int));
Bây giờ, bạn đã giới thiệu một cách tinh tế để tự bắn vào chân mình, bằng cách chỉ định loại foo
ở hai nơi. Nếu cái này thay đổi nhưng cái kia thì không, mã bị hỏng. Vì vậy, luôn luôn làm điều đó như thế này:
send(&foo, sizeof foo);
Bây giờ bạn được bảo vệ. Chắc chắn, bạn nhân đôi tên của biến, nhưng điều đó có xác suất phá vỡ cao theo cách trình biên dịch có thể phát hiện, nếu bạn thay đổi nó.
sizeof(int)
yêu cầu hướng dẫn ít hơn sizeof(foo)
?
int x = 1+1;
so với int x = (1+1);
. Ở đây, dấu ngoặc đơn hoàn toàn chỉ là thẩm mỹ.
sizeof
sẽ luôn là hằng số trong C ++ và C89. Với mảng chiều dài thay đổi của C99, nó có thể được đánh giá trong thời gian chạy.
sizeof
có thể là một nhà điều hành nhưng nó nên được coi là một chức năng theo Linus Torvalds. Tôi đồng ý. Đọc lý lẽ của anh ấy ở đây: lkml.org/lkml/2012/7/11/103
int size = (&arr)[1] - arr;
Kiểm tra liên kết này để giải thích
ptrdiff_t
. (Thông thường trên hệ thống 64 bit, đây sẽ là loại lớn hơn int
). Ngay cả khi bạn thay đổi int
thành ptrdiff_t
mã này, nó vẫn có lỗi nếu arr
chiếm hơn một nửa không gian địa chỉ.
/3G
tùy chọn bạn có phân chia người dùng / hạt nhân 3G / 1G, cho phép bạn có kích thước mảng lên tới 75% kích thước không gian địa chỉ.
foo buf1[80]; foo buf2[sizeof buf1/sizeof buf1[0]]; foo buf3[(&buf1)[1] - buf1];
như là biến toàn cầu. buf3[]
tuyên bố thất bại như (&buf1)[1] - buf1
không phải là một hằng số.
Bạn có thể sử dụng toán tử sizeof nhưng nó sẽ không hoạt động cho các hàm vì nó sẽ lấy tham chiếu của con trỏ mà bạn có thể làm như sau để tìm độ dài của một mảng:
len = sizeof(arr)/sizeof(arr[0])
Mã ban đầu được tìm thấy ở đây: Chương trình C để tìm số lượng phần tử trong một mảng
Nếu bạn biết kiểu dữ liệu của mảng, bạn có thể sử dụng một cái gì đó như:
int arr[] = {23, 12, 423, 43, 21, 43, 65, 76, 22};
int noofele = sizeof(arr)/sizeof(int);
Hoặc nếu bạn không biết kiểu dữ liệu của mảng, bạn có thể sử dụng một cái gì đó như:
noofele = sizeof(arr)/sizeof(arr[0]);
Lưu ý: Điều này chỉ hoạt động nếu mảng không được xác định trong thời gian chạy (như malloc) và mảng không được truyền trong hàm. Trong cả hai trường hợp, arr
(tên mảng) là một con trỏ.
int noofele = sizeof(arr)/sizeof(int);
chỉ là một nửa tốt hơn so với mã hóa int noofele = 9;
. Sử dụng sizeof(arr)
duy trì tính linh hoạt nên kích thước mảng thay đổi. Tuy nhiên, sizeof(int)
cần một bản cập nhật nên loại arr[]
thay đổi. Tốt hơn để sử dụng sizeof(arr)/sizeof(arr[0])
ngay cả khi loại được biết đến. Không rõ tại sao sử dụng int
cho noofele
vs. size_t
, loại được trả về bởi sizeof()
.
Macro ARRAYELEMENTCOUNT(x)
mà mọi người đang sử dụng đánh giá không chính xác . Trên thực tế, đây chỉ là một vấn đề nhạy cảm, bởi vì bạn không thể có các biểu thức dẫn đến loại 'mảng'.
/* Compile as: CL /P "macro.c" */
# define ARRAYELEMENTCOUNT(x) (sizeof (x) / sizeof (x[0]))
ARRAYELEMENTCOUNT(p + 1);
Thực tế đánh giá là:
(sizeof (p + 1) / sizeof (p + 1[0]));
Trong khi
/* Compile as: CL /P "macro.c" */
# define ARRAYELEMENTCOUNT(x) (sizeof (x) / sizeof (x)[0])
ARRAYELEMENTCOUNT(p + 1);
Nó đánh giá chính xác để:
(sizeof (p + 1) / sizeof (p + 1)[0]);
Điều này thực sự không liên quan nhiều đến kích thước của mảng một cách rõ ràng. Tôi vừa nhận thấy rất nhiều lỗi từ việc không thực sự quan sát cách bộ xử lý C hoạt động. Bạn luôn bao bọc tham số macro, không thể tham gia vào biểu thức.
Chính xác; ví dụ của tôi là một điều xấu. Nhưng đó thực sự là chính xác những gì sẽ xảy ra. Như tôi đã đề cập trước đây p + 1
sẽ kết thúc dưới dạng một con trỏ và làm mất hiệu lực toàn bộ macro (giống như khi bạn cố gắng sử dụng macro trong một hàm với tham số con trỏ).
Vào cuối ngày, trong trường hợp cụ thể này , lỗi không thực sự quan trọng (vì vậy tôi chỉ lãng phí thời gian của mọi người; huzzah!), Bởi vì bạn không có biểu thức với một loại 'mảng'. Nhưng thực sự quan điểm về các đánh giá tiền xử lý tôi nghĩ là một điều quan trọng.
(sizeof (x) / sizeof (*x))
?
Đối với mảng nhiều chiều, nó phức tạp hơn một chút. Mọi người thường định nghĩa các hằng vĩ mô rõ ràng, nghĩa là
#define g_rgDialogRows 2
#define g_rgDialogCols 7
static char const* g_rgDialog[g_rgDialogRows][g_rgDialogCols] =
{
{ " ", " ", " ", " 494", " 210", " Generic Sample Dialog", " " },
{ " 1", " 330", " 174", " 88", " ", " OK", " " },
};
Nhưng các hằng số này có thể được đánh giá tại thời gian biên dịch với sizeof :
#define rows_of_array(name) \
(sizeof(name ) / sizeof(name[0][0]) / columns_of_array(name))
#define columns_of_array(name) \
(sizeof(name[0]) / sizeof(name[0][0]))
static char* g_rgDialog[][7] = { /* ... */ };
assert( rows_of_array(g_rgDialog) == 2);
assert(columns_of_array(g_rgDialog) == 7);
Lưu ý rằng mã này hoạt động trong C và C ++. Đối với các mảng có nhiều hơn hai chiều sử dụng
sizeof(name[0][0][0])
sizeof(name[0][0][0][0])
vv, quảng cáo vô hạn.
sizeof(array) / sizeof(array[0])
array
có, bạn không cần sử dụng sizeof(array) / sizeof(array[0])
nếu array
là một mảng của một trong hai char
, unsigned char
hoặc signed char
- Trích dẫn từ C18,6.5.3.4 / 4: "Khi sizeof được áp dụng cho toán hạng có kiểu char, char không dấu hoặc char đã ký , (hoặc một phiên bản đủ điều kiện) kết quả là 1. " Trong trường hợp này, bạn có thể chỉ cần làm sizeof(array)
như được giải thích trong câu trả lời dành riêng của tôi .
Kích thước của một mảng trong C:
int a[10];
size_t size_of_array = sizeof(a); // Size of array a
int n = sizeof (a) / sizeof (a[0]); // Number of elements in array a
size_t size_of_element = sizeof(a[0]); // Size of each element in array a
// Size of each element = size of type
size_t size_of_element
nhưng int
với int n = sizeof (a) / sizeof (a[0]);
và khôngsize_t n = sizeof (a) / sizeof (a[0]);
char a[INT_MAX + 1u];
, int n
được sử dụng trong int n = sizeof (a) / sizeof (a[0]);
là không đủ (đó là UB). Sử dụng size_t n = sizeof (a) / sizeof (a[0]);
không phát sinh vấn đề này.
Tôi khuyên bạn không bao giờ sử dụng sizeof
(ngay cả khi nó có thể được sử dụng) để có được bất kỳ hai kích thước khác nhau của một mảng, về số lượng phần tử hoặc theo byte, đó là hai trường hợp cuối cùng tôi trình bày ở đây. Đối với mỗi một trong hai kích thước, các macro hiển thị bên dưới có thể được sử dụng để làm cho nó an toàn hơn. Lý do là để làm rõ ý định của mã đối với các nhà bảo trì và sự khác biệt sizeof(ptr)
so sizeof(arr)
với cái nhìn đầu tiên (được viết theo cách này là không rõ ràng), do đó các lỗi sau đó rõ ràng đối với mọi người đọc mã.
TL; DR:
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + must_be_array(arr))
#define ARRAY_SSIZE(arr) ((ptrdiff_t)ARRAY_SIZE(arr))
#define ARRAY_BYTES(arr) (sizeof((arr)[0]) * ARRAY_SIZE(arr))
must_be_array(arr)
(được định nghĩa dưới đây) IS cần thiết -Wsizeof-pointer-div
là lỗi (kể từ tháng 4 năm 2020):
#define is_same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a) (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a) _Static_assert(is_array(a), "Not a `[]` !")
#define must_be_array(a) ( \
0 * (int)sizeof( \
struct { \
Static_assert_array(a); \
char ISO_C_forbids_a_struct_with_no_members__; \
} \
) \
)
Đã có những lỗi quan trọng liên quan đến chủ đề này: https://lkml.org/lkml/2015/9/3/428
Tôi không đồng ý với giải pháp mà Linus cung cấp, đó là không bao giờ sử dụng ký hiệu mảng cho các tham số của hàm.
Tôi thích ký hiệu mảng là tài liệu cho thấy một con trỏ đang được sử dụng như một mảng. Nhưng điều đó có nghĩa là một giải pháp chống lừa đảo cần được áp dụng để không thể viết mã lỗi.
Từ một mảng, chúng ta có ba kích thước mà chúng ta có thể muốn biết:
Cái đầu tiên rất đơn giản và không thành vấn đề nếu chúng ta đang xử lý một mảng hoặc một con trỏ, bởi vì nó được thực hiện theo cùng một cách.
Ví dụ về cách sử dụng:
void foo(ptrdiff_t nmemb, int arr[static nmemb])
{
qsort(arr, nmemb, sizeof(arr[0]), cmp);
}
qsort()
cần giá trị này như là đối số thứ ba của nó.
Đối với hai kích thước khác, là chủ đề của câu hỏi, chúng tôi muốn đảm bảo rằng chúng tôi đang xử lý một mảng và phá vỡ phần biên dịch nếu không, vì nếu chúng tôi xử lý một con trỏ, chúng tôi sẽ nhận được các giá trị sai . Khi quá trình biên dịch bị hỏng, chúng ta sẽ có thể dễ dàng thấy rằng chúng ta không xử lý một mảng, nhưng thay vào đó bằng một con trỏ và chúng ta sẽ phải viết mã bằng một biến hoặc một macro lưu trữ kích thước của mảng phía sau con trỏ.
Đây là câu hỏi phổ biến nhất và nhiều câu trả lời đã cung cấp cho bạn macro ARRAY_SIZE điển hình:
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
Cho rằng kết quả của ARRAY_SIZE thường được sử dụng với các biến kiểu đã ký ptrdiff_t
, nên xác định một biến thể đã ký của macro này:
#define ARRAY_SSIZE(arr) ((ptrdiff_t)ARRAY_SIZE(arr))
Các mảng có nhiều PTRDIFF_MAX
thành viên sẽ cung cấp các giá trị không hợp lệ cho phiên bản macro đã ký này, nhưng từ khi đọc C17 :: 6.5.6.9, các mảng như thế đã chơi với lửa. Chỉ ARRAY_SIZE
và size_t
nên được sử dụng trong những trường hợp.
Các phiên bản gần đây của trình biên dịch, chẳng hạn như GCC 8, sẽ cảnh báo bạn khi bạn áp dụng macro này cho con trỏ, vì vậy nó an toàn (có các phương pháp khác để làm cho nó an toàn với trình biên dịch cũ hơn).
Nó hoạt động bằng cách chia kích thước theo byte của toàn bộ mảng cho kích thước của từng phần tử.
Ví dụ về việc sử dụng:
void foo(ptrdiff_t nmemb)
{
char buf[nmemb];
fgets(buf, ARRAY_SIZE(buf), stdin);
}
void bar(ptrdiff_t nmemb)
{
int arr[nmemb];
for (ptrdiff_t i = 0; i < ARRAY_SSIZE(arr); i++)
arr[i] = i;
}
Nếu các hàm này không sử dụng mảng, nhưng lấy chúng làm tham số thay vào đó, mã cũ sẽ không biên dịch, do đó không thể có lỗi (do sử dụng phiên bản trình biên dịch gần đây hoặc sử dụng một số thủ thuật khác) và chúng ta cần thay thế cuộc gọi macro bằng giá trị:
void foo(ptrdiff_t nmemb, char buf[nmemb])
{
fgets(buf, nmemb, stdin);
}
void bar(ptrdiff_t nmemb, int arr[nmemb])
{
for (ptrdiff_t i = 0; i < nmemb; i++)
arr[i] = i;
}
ARRAY_SIZE
thường được sử dụng như một giải pháp cho trường hợp trước, nhưng trường hợp này hiếm khi được viết an toàn, có thể vì nó ít phổ biến hơn.
Cách phổ biến để có được giá trị này là sử dụng sizeof(arr)
. Vấn đề: giống như với cái trước; nếu bạn có một con trỏ thay vì một mảng, chương trình của bạn sẽ hoạt động.
Giải pháp cho vấn đề liên quan đến việc sử dụng cùng một macro như trước đây, mà chúng ta biết là an toàn (nó phá vỡ quá trình biên dịch nếu nó được áp dụng cho một con trỏ):
#define ARRAY_BYTES(arr) (sizeof((arr)[0]) * ARRAY_SIZE(arr))
Cách thức hoạt động rất đơn giản: nó hoàn tác phân chia ARRAY_SIZE
, vì vậy sau khi hủy bỏ toán học, bạn kết thúc chỉ với một sizeof(arr)
, nhưng với sự an toàn bổ sung của ARRAY_SIZE
công trình.
Ví dụ về cách sử dụng:
void foo(ptrdiff_t nmemb)
{
int arr[nmemb];
memset(arr, 0, ARRAY_BYTES(arr));
}
memset()
cần giá trị này như là đối số thứ ba của nó.
Như trước đây, nếu mảng được nhận dưới dạng tham số (con trỏ), nó sẽ không biên dịch và chúng ta sẽ phải thay thế lệnh gọi macro bằng giá trị:
void foo(ptrdiff_t nmemb, int arr[nmemb])
{
memset(arr, 0, sizeof(arr[0]) * nmemb);
}
-Wsizeof-pointer-div
là lỗi :Hôm nay tôi phát hiện ra rằng cảnh báo mới trong GCC chỉ hoạt động nếu macro được xác định trong tiêu đề không phải là tiêu đề hệ thống. Nếu bạn xác định macro trong một tiêu đề được cài đặt trong hệ thống của bạn (thường là /usr/local/include/
hoặc /usr/include/
) ( #include <foo.h>
), trình biên dịch sẽ KHÔNG phát ra cảnh báo (Tôi đã thử GCC 9.3.0).
Vì vậy, chúng tôi có #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
và muốn làm cho nó an toàn. Chúng tôi sẽ cần C11 _Static_assert()
và một số tiện ích mở rộng GCC: Tuyên bố và Tuyên bố trong Biểu thức , __builtin_types_compiverse_p :
#define is_same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a) (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a) _Static_assert(is_array(a), "Not a `[]` !")
#define ARRAY_SIZE(arr) ( \
{ \
Static_assert_array(arr); \
sizeof(arr) / sizeof((arr)[0]); \
} \
)
Bây giờ ARRAY_SIZE()
là hoàn toàn an toàn, và do đó tất cả các dẫn xuất của nó sẽ an toàn.
__arraycount()
:Libbsd cung cấp macro __arraycount()
trong <sys/cdefs.h>
, không an toàn vì nó thiếu một cặp dấu ngoặc đơn, nhưng chúng ta có thể tự thêm các dấu ngoặc đơn đó và do đó chúng ta thậm chí không cần phải viết phân chia trong tiêu đề của mình (tại sao chúng ta sẽ sao chép mã đã tồn tại? ). Macro đó được xác định trong tiêu đề hệ thống, vì vậy nếu chúng ta sử dụng nó, chúng ta buộc phải sử dụng các macro ở trên.
#include <sys/cdefs.h>
#define is_same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a) (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a) _Static_assert(is_array(a), "Not a `[]` !")
#define ARRAY_SIZE(arr) ( \
{ \
Static_assert_array(arr); \
__arraycount((arr)); \
} \
)
#define ARRAY_SSIZE(arr) ((ptrdiff_t)ARRAY_SIZE(arr))
#define ARRAY_BYTES(arr) (sizeof((arr)[0]) * ARRAY_SIZE(arr))
Một số hệ thống cung cấp nitems()
trong <sys/param.h>
thay vào đó, và một số hệ thống cung cấp cả hai. Bạn nên kiểm tra hệ thống của mình và sử dụng hệ thống bạn có và có thể sử dụng một số điều kiện tiền xử lý cho tính di động và hỗ trợ cả hai.
Thật không may, ({})
phần mở rộng gcc không thể được sử dụng ở phạm vi tệp. Để có thể sử dụng macro ở phạm vi tệp, xác nhận tĩnh phải ở bên trong sizeof(struct {})
. Sau đó, nhân nó 0
với để không ảnh hưởng đến kết quả. Một diễn viên (int)
có thể là tốt để mô phỏng một hàm trả về (int)0
(trong trường hợp này là không cần thiết, nhưng sau đó nó có thể tái sử dụng cho những thứ khác).
#include <sys/cdefs.h>
#define is_same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a) (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a) _Static_assert(is_array(a), "Not a `[]` !")
#define must_be_array(a) ( \
0 * (int)sizeof( \
struct { \
Static_assert_array(a); \
char ISO_C_forbids_a_struct_with_no_members__; \
} \
) \
)
#define ARRAY_SIZE(arr) (__arraycount((arr)) + must_be_array(arr))
#define ARRAY_SSIZE(arr) ((ptrdiff_t)ARRAY_SIZE(arr))
#define ARRAY_BYTES(arr) (sizeof((arr)[0]) * ARRAY_SIZE(arr))
sizeof(arr)
) không được hiển thị ở nơi khác : ARRAY_BYTES(arr)
.
sizeof
, mà sử dụng công trình này thay thế; nếu bạn cảm thấy muốn viết các công trình này mỗi lần, bạn có thể sẽ mắc lỗi (rất phổ biến nếu bạn sao chép dán và cũng rất phổ biến nếu bạn viết chúng mỗi lần vì chúng có nhiều dấu ngoặc đơn) ...
sizeof
rõ ràng là không an toàn (lý do là trong câu trả lời) và không sử dụng macro nhưng sử dụng các công trình tôi cung cấp, mỗi lần, thậm chí còn không an toàn hơn, vì vậy cách duy nhất để đi là macro.
"bạn đã giới thiệu một cách tinh tế để tự bắn vào chân mình"
Mảng C 'bản địa' không lưu trữ kích thước của chúng. Do đó, nên lưu chiều dài của mảng trong một biến / const riêng biệt và truyền nó bất cứ khi nào bạn vượt qua mảng, đó là:
#define MY_ARRAY_LENGTH 15
int myArray[MY_ARRAY_LENGTH];
Bạn NÊN luôn luôn tránh các mảng bản địa (trừ khi bạn không thể, trong trường hợp đó, chú ý đến bàn chân của bạn). Nếu bạn đang viết C ++, hãy sử dụng bộ chứa 'vectơ' của STL . "So với các mảng, chúng cung cấp hiệu năng gần như giống nhau" và chúng hữu ích hơn nhiều!
// vector is a template, the <int> means it is a vector of ints
vector<int> numbers;
// push_back() puts a new value at the end (or back) of the vector
for (int i = 0; i < 10; i++)
numbers.push_back(i);
// Determine the size of the array
cout << numbers.size();
enum
khai báo.
#define SIZE_OF_ARRAY(_array) (sizeof(_array) / sizeof(_array[0]))
Nếu bạn thực sự muốn làm điều này để vượt qua mảng của mình, tôi khuyên bạn nên thực hiện một cấu trúc để lưu trữ một con trỏ đến loại bạn muốn một mảng và một số nguyên biểu thị kích thước của mảng. Sau đó, bạn có thể chuyển điều đó xung quanh chức năng của bạn. Chỉ cần gán giá trị biến mảng (con trỏ cho phần tử đầu tiên) cho con trỏ đó. Sau đó, bạn có thể đi Array.arr[i]
lấy phần tử thứ i và sử dụng Array.size
để lấy số phần tử trong mảng.
Tôi bao gồm một số mã cho bạn. Nó không hữu ích lắm nhưng bạn có thể mở rộng nó với nhiều tính năng hơn. Thành thật mà nói, nếu đây là những điều bạn muốn, bạn nên ngừng sử dụng C và sử dụng ngôn ngữ khác với các tính năng này được tích hợp.
/* Absolutely no one should use this...
By the time you're done implementing it you'll wish you just passed around
an array and size to your functions */
/* This is a static implementation. You can get a dynamic implementation and
cut out the array in main by using the stdlib memory allocation methods,
but it will work much slower since it will store your array on the heap */
#include <stdio.h>
#include <string.h>
/*
#include "MyTypeArray.h"
*/
/* MyTypeArray.h
#ifndef MYTYPE_ARRAY
#define MYTYPE_ARRAY
*/
typedef struct MyType
{
int age;
char name[20];
} MyType;
typedef struct MyTypeArray
{
int size;
MyType *arr;
} MyTypeArray;
MyType new_MyType(int age, char *name);
MyTypeArray newMyTypeArray(int size, MyType *first);
/*
#endif
End MyTypeArray.h */
/* MyTypeArray.c */
MyType new_MyType(int age, char *name)
{
MyType d;
d.age = age;
strcpy(d.name, name);
return d;
}
MyTypeArray new_MyTypeArray(int size, MyType *first)
{
MyTypeArray d;
d.size = size;
d.arr = first;
return d;
}
/* End MyTypeArray.c */
void print_MyType_names(MyTypeArray d)
{
int i;
for (i = 0; i < d.size; i++)
{
printf("Name: %s, Age: %d\n", d.arr[i].name, d.arr[i].age);
}
}
int main()
{
/* First create an array on the stack to store our elements in.
Note we could create an empty array with a size instead and
set the elements later. */
MyType arr[] = {new_MyType(10, "Sam"), new_MyType(3, "Baxter")};
/* Now create a "MyTypeArray" which will use the array we just
created internally. Really it will just store the value of the pointer
"arr". Here we are manually setting the size. You can use the sizeof
trick here instead if you're sure it will work with your compiler. */
MyTypeArray array = new_MyTypeArray(2, arr);
/* MyTypeArray array = new_MyTypeArray(sizeof(arr)/sizeof(arr[0]), arr); */
print_MyType_names(array);
return 0;
}
strcpy(d.name, name);
không xử lý tràn.
Cách tốt nhất là bạn lưu thông tin này, ví dụ, trong một cấu trúc:
typedef struct {
int *array;
int elements;
} list_s;
Thực hiện tất cả các chức năng cần thiết như tạo, hủy, kiểm tra sự bình đẳng và mọi thứ khác bạn cần. Nó là dễ dàng hơn để vượt qua như là một tham số.
int elements
vs size_t elements
?
Hàm sizeof
trả về số byte được sử dụng bởi mảng của bạn trong bộ nhớ. Nếu bạn muốn tính số lượng phần tử trong mảng của mình, bạn nên chia số đó với sizeof
loại biến của mảng. Giả sử int array[10];
, nếu số nguyên loại biến trong máy tính của bạn là 32 bit (hoặc 4 byte), để có được kích thước của mảng, bạn nên làm như sau:
int array[10];
int sizeOfArray = sizeof(array)/sizeof(int);
Bạn có thể sử dụng &
toán tử. Đây là mã nguồn:
#include<stdio.h>
#include<stdlib.h>
int main(){
int a[10];
int *p;
printf("%p\n", (void *)a);
printf("%p\n", (void *)(&a+1));
printf("---- diff----\n");
printf("%zu\n", sizeof(a[0]));
printf("The size of array a is %zu\n", ((char *)(&a+1)-(char *)a)/(sizeof(a[0])));
return 0;
};
Đây là đầu ra mẫu
1549216672
1549216712
---- diff----
4
The size of array a is 10
ptrdiff_t
. sizeof()
kết quả trong size_t
. C không xác định cái nào rộng hơn hoặc cao hơn / cùng thứ hạng. Vì vậy, loại thương số ((char *)(&a+1)-(char *)a)/(sizeof(a[0]))
không chắc chắn size_t
và do đó in với z
có thể dẫn đến UB. Đơn giản chỉ cần sử dụng printf("The size of array a is %zu\n", sizeof a/sizeof a[0]);
là đủ.
(char *)(&a+1)-(char *)a
không phải là hằng số và có thể được tính vào thời gian chạy, ngay cả với kích thước cố định a[10]
. sizeof(a)/sizeof(a[0])
là hằng số được thực hiện tại thời gian biên dịch trong trường hợp này.
Một giải pháp thanh lịch hơn sẽ là
size_t size = sizeof(a) / sizeof(*a);
Bên cạnh các câu trả lời đã được cung cấp, tôi muốn chỉ ra một trường hợp đặc biệt bằng cách sử dụng
sizeof(a) / sizeof (a[0])
Nếu a
là một mảng của char
, unsigned char
hoặc signed char
bạn không cần sử dụng sizeof
hai lần vì một sizeof
biểu thức với một toán hạng của các loại này luôn luôn dẫn đến 1
.
Trích dẫn từ C18,6.5.3.4 / 4:
" Khi
sizeof
được áp dụng cho một toán hạng có kiểuchar
,unsigned char
hoặcsigned char
, (hoặc một phiên bản chất lượng của chúng) kết quả là1
."
Do đó, sizeof(a) / sizeof (a[0])
sẽ tương đương với NUMBER OF ARRAY ELEMENTS / 1
nếu a
là một mảng kiểu char
, unsigned char
hoặc signed char
. Sự phân chia qua 1 là dư thừa.
Trong trường hợp này, bạn chỉ cần viết tắt và làm:
sizeof(a)
Ví dụ:
char a[10];
size_t length = sizeof(a);
Nếu bạn muốn một bằng chứng, đây là một liên kết đến GodBolt .
Tuy nhiên, bộ phận duy trì sự an toàn, nếu loại thay đổi đáng kể (mặc dù những trường hợp này rất hiếm).
Lưu ý: Điều này có thể cung cấp cho bạn hành vi không xác định như được chỉ ra bởi MM trong bình luận.
int a[10];
int size = (*(&a+1)-a) ;
*
nhà điều hành có thể không được áp dụng cho một con trỏ qua-the-end
*(&a+1) - a;
khác với (&a)[1] - a;
ở trên, không cả hai *(&a+1)
và (&a)[1]
được tính là 1 quá khứ?
x[y]
được định nghĩa là*(x + (y))