Cách xử lý mảng của C rất khác so với Java và bạn sẽ phải điều chỉnh suy nghĩ của mình cho phù hợp. Mảng trong C không phải là đối tượng hạng nhất (nghĩa là một biểu thức mảng không giữ được "mảng" trong hầu hết các ngữ cảnh). Trong C, một biểu thức kiểu "mảng phần tử N T
" sẽ được chuyển đổi hoàn toàn ("phân rã") thành biểu thức của kiểu "con trỏ thành T
", ngoại trừ khi biểu thức mảng là toán hạng của sizeof
toán tử hoặc toán tử đơn nguyên &
hoặc nếu biểu thức mảng là một chuỗi ký tự được sử dụng để khởi tạo một mảng khác trong khai báo.
Trong số những thứ khác, điều này có nghĩa là bạn không thể truyền biểu thức mảng cho hàm và nhận nó dưới dạng kiểu mảng ; Hàm thực sự nhận được một loại con trỏ:
void foo(char *a, size_t asize)
{
// do something with a
}
int bar(void)
{
char str[6] = "Hello";
foo(str, sizeof str);
}
Trong lệnh gọi đến foo
, biểu thức str
được chuyển đổi từ loại char [6]
thành char *
, đó là lý do tại sao tham số đầu tiên foo
được khai báo char *a
thay vì char a[6]
. Trong sizeof str
, vì biểu thức mảng là toán hạng của sizeof
toán tử, nên nó không được chuyển đổi thành loại con trỏ, do đó bạn nhận được số byte trong mảng (6).
Nếu bạn thực sự quan tâm, bạn có thể đọc Sự phát triển của ngôn ngữ C của Dennis Ritchie để hiểu cách điều trị này đến từ đâu.
Kết quả cuối cùng là các hàm không thể trả về các kiểu mảng, điều này cũng tốt vì các biểu thức mảng cũng không thể là mục tiêu của một phép gán.
Phương pháp an toàn nhất là để người gọi xác định mảng và chuyển địa chỉ và kích thước của nó cho hàm được cho là ghi vào nó:
void returnArray(const char *srcArray, size_t srcSize, char *dstArray, char dstSize)
{
...
dstArray[i] = some_value_derived_from(srcArray[i]);
...
}
int main(void)
{
char src[] = "This is a test";
char dst[sizeof src];
...
returnArray(src, sizeof src, dst, sizeof dst);
...
}
Một phương thức khác là cho hàm phân bổ mảng một cách linh hoạt và trả về con trỏ và kích thước:
char *returnArray(const char *srcArray, size_t srcSize, size_t *dstSize)
{
char *dstArray = malloc(srcSize);
if (dstArray)
{
*dstSize = srcSize;
...
}
return dstArray;
}
int main(void)
{
char src[] = "This is a test";
char *dst;
size_t dstSize;
dst = returnArray(src, sizeof src, &dstSize);
...
free(dst);
...
}
Trong trường hợp này, người gọi có trách nhiệm sắp xếp mảng với free
chức năng thư viện.
Lưu ý rằng dst
trong đoạn mã trên là một con trỏ đơn giản char
, không phải là con trỏ tới một mảng char
. Con trỏ và ngữ nghĩa mảng của C sao cho bạn có thể áp dụng toán tử con []
cho một biểu thức của kiểu mảng hoặc kiểu con trỏ; cả hai src[i]
và dst[i]
sẽ truy cập i
phần tử thứ của mảng (mặc dù chỉ src
có kiểu mảng).
Bạn có thể khai báo một con trỏ tới một mảng phần tử N T
và làm một cái gì đó tương tự:
char (*returnArray(const char *srcArr, size_t srcSize))[SOME_SIZE]
{
char (*dstArr)[SOME_SIZE] = malloc(sizeof *dstArr);
if (dstArr)
{
...
(*dstArr)[i] = ...;
...
}
return dstArr;
}
int main(void)
{
char src[] = "This is a test";
char (*dst)[SOME_SIZE];
...
dst = returnArray(src, sizeof src);
...
printf("%c", (*dst)[j]);
...
}
Một số nhược điểm với các bên trên. Trước hết, các phiên bản cũ hơn của C dự kiến SOME_SIZE
là hằng số thời gian biên dịch, có nghĩa là hàm đó sẽ chỉ hoạt động với một kích thước mảng. Thứ hai, bạn phải hủy đăng ký con trỏ trước khi áp dụng chỉ mục con, mã này sẽ làm xáo trộn mã. Con trỏ tới mảng hoạt động tốt hơn khi bạn xử lý mảng đa chiều.