Có phải hành vi “tấn công cấu trúc” không được xác định về mặt kỹ thuật không?


111

Điều tôi đang hỏi là thủ thuật "thành viên cuối cùng của cấu trúc có độ dài thay đổi" nổi tiếng. Nó đi một cái gì đó như thế này:

struct T {
    int len;
    char s[1];
};

struct T *p = malloc(sizeof(struct T) + 100);
p->len = 100;
strcpy(p->s, "hello world");

Do cách cấu trúc được trình bày trong bộ nhớ, chúng ta có thể phủ cấu trúc lên một khối lớn hơn cần thiết và coi thành viên cuối cùng như thể nó lớn hơn khối đã 1 charchỉ định.

Vì vậy, câu hỏi đặt ra là: Liệu kỹ thuật này có phải là hành vi không xác định về mặt kỹ thuật không? . Tôi mong đợi điều đó đúng, nhưng tò mò không biết tiêu chuẩn nói gì về điều này.

Tái bút: Tôi biết về cách tiếp cận C99 cho điều này, tôi muốn câu trả lời cụ thể vào phiên bản của thủ thuật như được liệt kê ở trên.


33
Điều này có vẻ giống như một khá rõ ràng, hợp lý, và trên hết là có thể trả lời câu hỏi. Không thấy lý do cho việc đóng phiếu.
cHao 14/09/10

2
Nếu bạn đã giới thiệu trình biên dịch "ansi c" không hỗ trợ tấn công struct, hầu hết các lập trình viên c mà tôi biết sẽ không chấp nhận rằng trình biên dịch của bạn "hoạt động bình thường". Mặc dù vậy, họ sẽ chấp nhận đọc nghiêm ngặt tiêu chuẩn. Ủy ban chỉ đơn giản là bỏ lỡ một trong đó.
dmckee --- cựu điều hành kitten

4
@james Bản hack hoạt động bằng cách làm hỏng một đối tượng đủ lớn cho mảng mà bạn muốn, mặc dù đã khai báo một mảng tối thiểu. Vì vậy, bạn đang truy cập bộ nhớ được cấp phát bên ngoài định nghĩa chặt chẽ của cấu trúc. Việc viết trước phân bổ của bạn là một sai lầm không thể chối cãi, nhưng điều đó khác với việc viết trong phân bổ của bạn nhưng nằm ngoài "cấu trúc".
dmckee --- cựu điều hành kitten

2
@James: Malloc quá khổ là rất quan trọng ở đây. Nó đảm bảo rằng có bộ nhớ --- bộ nhớ với địa chỉ hợp pháp và 'thuộc sở hữu' của cấu trúc (tức là bất kỳ thực thể nào khác sử dụng nó là bất hợp pháp) --- qua phần cuối danh nghĩa của cấu trúc. Lưu ý rằng điều này có nghĩa là bạn không thể sử dụng struct hack trên các biến tự động: chúng phải được cấp phát động.
dmckee --- cựu điều hành kitten

5
@detly: Việc phân bổ / deallocate một thứ sẽ đơn giản hơn so với phân bổ / deallocate hai thứ, đặc biệt là vì sau này có hai cách thất bại mà bạn cần phải giải quyết. Điều này quan trọng với tôi hơn là tiết kiệm chi phí cận biên / tốc độ.
jamesdlin

Câu trả lời:


52

Như Câu hỏi thường gặp về C nói:

Không rõ nó hợp pháp hay xách tay, nhưng nó khá phổ biến.

và:

... một cách giải thích chính thức đã cho rằng nó không hoàn toàn phù hợp với Tiêu chuẩn C, mặc dù nó dường như hoạt động theo tất cả các cách triển khai đã biết. (Các trình biên dịch kiểm tra giới hạn mảng cẩn thận có thể đưa ra cảnh báo.)

Lý do đằng sau bit 'tuân thủ nghiêm ngặt' là trong thông số kỹ thuật, phần J.2 Hành vi không xác định , bao gồm trong danh sách các hành vi không xác định:

  • Một chỉ số con của mảng nằm ngoài phạm vi, ngay cả khi một đối tượng rõ ràng là có thể truy cập được với chỉ số con đã cho (như trong biểu thức giá trị a[1][7]được đưa ra khai báo int a[4][5]) (6.5.6).

Đoạn 8 của Phần 6.5.6 Các toán tử cộng có một đề cập khác rằng quyền truy cập vượt quá giới hạn mảng đã xác định là không xác định:

Nếu cả toán hạng con trỏ và kết quả đều trỏ đến các phần tử của cùng một đối tượng mảng hoặc một phần tử vượt qua phần tử cuối cùng của đối tượng mảng, thì phép đánh giá sẽ không tạo ra lỗi tràn; nếu không, hành vi là không xác định.


1
Trong mã của OP, p->skhông bao giờ được sử dụng như một mảng. Nó được chuyển đến strcpy, trong trường hợp đó, nó phân rã thành đồng bằng char *, điều này xảy ra để trỏ đến một đối tượng có thể được hiểu hợp pháp là char [100];bên trong đối tượng được cấp phát.
R .. GitHub NGỪNG TRỢ GIÚP LÚC NỮA, 14/09/10

3
Có lẽ một cách khác để xem xét vấn đề này là ngôn ngữ có thể hạn chế một cách hình dung cách bạn truy cập các biến mảng thực tế như được mô tả trong J.2, nhưng không có cách nào nó có thể đưa ra các hạn chế như vậy đối với một đối tượng được cấp phát malloc, khi bạn chỉ chuyển đổi giá trị trả về void *tới một con trỏ tới [một cấu trúc chứa] một mảng. Nó vẫn hợp lệ để truy cập bất kỳ phần nào của đối tượng được cấp phát bằng cách sử dụng con trỏ tới char(hoặc tốt hơn là unsigned char).
R .. GitHub DỪNG TRỢ GIÚP LÚC NỮA, 14/09/10

@R. - Tôi có thể thấy J2 có thể không che điều này như thế nào, nhưng không phải nó cũng được che bởi 6.5.6?
detly

1
Chắc chắn nó có thể! Thông tin về loại và kích thước có thể được nhúng vào mọi con trỏ, và bất kỳ số học con trỏ sai nào sau đó đều có thể bị mắc bẫy - xem ví dụ: CCured . Ở một mức độ triết học hơn, không quan trọng là liệu việc triển khai khả thi có thể bắt bạn hay không, đó vẫn là hành vi chưa xác định (có những trường hợp hành vi không xác định sẽ cần một lời tiên tri cho Vấn đề tạm dừng để giải quyết - đó chính là lý do tại sao chúng là không xác định).
zwol

4
Đối tượng không phải là một đối tượng mảng vì vậy 6.5.6 không liên quan. Đối tượng là khối bộ nhớ được cấp phát bởi malloc. Tra cứu "đối tượng" trong tiêu chuẩn trước khi bạn nhổ bs.
R .. GitHub NGỪNG TRỢ GIÚP TỪ NGÀY

34

Tôi tin rằng về mặt kỹ thuật đó là hành vi không xác định. Tiêu chuẩn (có thể cho là) ​​không đề cập trực tiếp đến vấn đề này, vì vậy tiêu chuẩn này thuộc "hoặc do bỏ qua bất kỳ định nghĩa rõ ràng nào về hành vi." mệnh đề (§4 / 2 của C99, §3.16 / 2 của C89) nói rằng đó là hành vi không xác định.

"Có thể cho là" ở trên phụ thuộc vào định nghĩa của toán tử chỉ số mảng. Cụ thể, nó nói: "Một biểu thức hậu tố theo sau là một biểu thức trong dấu ngoặc vuông [] là một ký hiệu được chỉ định dưới dạng ký hiệu của một đối tượng mảng." (C89, §6.3.2.1 / 2).

Bạn có thể lập luận rằng "của một đối tượng mảng" đang bị vi phạm ở đây (vì bạn đang viết chỉ mục bên ngoài phạm vi đã xác định của đối tượng mảng), trong trường hợp đó, hành vi (một chút nữa) là không xác định rõ ràng, thay vì chỉ không xác định lịch sự của không có gì hoàn toàn xác định nó.

Về lý thuyết, tôi có thể tưởng tượng một trình biên dịch thực hiện kiểm tra giới hạn mảng và (ví dụ) sẽ hủy bỏ chương trình khi / nếu bạn cố gắng sử dụng chỉ số con nằm ngoài phạm vi. Trên thực tế, tôi không biết có một thứ như vậy đang tồn tại và với sự phổ biến của kiểu mã này, ngay cả khi một trình biên dịch cố gắng thực thi các chỉ số phụ trong một số trường hợp, thật khó để tưởng tượng rằng có ai đó sẽ làm như vậy trong tình huống này.


2
Tôi cũng có thể tưởng tượng một trình biên dịch có thể quyết định rằng nếu một mảng xảy ra ở kích thước 1, thì arr[x] = y;có thể được viết lại thành arr[0] = y;; đối với mảng kích thước 2, arr[i] = 4;có thể được viết lại thành i ? arr[1] = 4 : arr[0] = 4; Mặc dù tôi chưa từng thấy trình biên dịch thực hiện tối ưu hóa như vậy, nhưng trên một số hệ thống nhúng, chúng có thể rất hiệu quả. Trên PIC18x, sử dụng kiểu dữ liệu 8 bit, mã cho câu lệnh đầu tiên sẽ là mười sáu byte, câu lệnh thứ hai, hai hoặc bốn, và câu lệnh thứ ba, tám hoặc mười hai. Không phải là một tối ưu hóa tồi nếu hợp pháp.
supercat

Nếu tiêu chuẩn xác định quyền truy cập mảng bên ngoài giới hạn mảng là hành vi không xác định, thì việc hack struct cũng vậy. Tuy nhiên, nếu tiêu chuẩn xác định quyền truy cập mảng là đường cú pháp cho con trỏ arithmetic ( a[2] == a + 2), thì nó không. Nếu tôi đúng, tất cả các tiêu chuẩn C đều định nghĩa truy cập mảng là số học con trỏ.
yyny

13

Vâng, đó là hành vi không xác định.

Báo cáo Lỗi Ngôn ngữ C # 051 đưa ra câu trả lời dứt khoát cho câu hỏi này:

Thành ngữ, trong khi phổ biến, không hoàn toàn phù hợp

http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_051.html

Trong tài liệu Cơ sở lý luận C99, Ủy ban C bổ sung:

Tính hợp lệ của cấu trúc này luôn bị nghi ngờ. Trong phản hồi cho một Báo cáo Lỗi, Ủy ban đã quyết định rằng đó là hành vi không xác định vì mảng p-> các mục chỉ chứa một mục, bất kể có tồn tại không gian hay không.


2
+1 cho việc tìm ra điều này, nhưng tôi vẫn khẳng định nó mâu thuẫn. Hai con trỏ đến cùng một đối tượng (trong trường hợp này là byte đã cho) là bằng nhau và một con trỏ tới nó (con trỏ vào mảng biểu diễn của toàn bộ đối tượng được lấy bởi malloc) là hợp lệ trong phép cộng, vậy làm cách nào để con trỏ giống hệt nhau, thu được thông qua một con đường khác, không hợp lệ trong việc bổ sung? Ngay cả khi họ muốn khẳng định đó là UB, điều đó khá vô nghĩa, bởi vì không có cách tính toán nào để triển khai phân biệt giữa cách sử dụng được xác định rõ và cách sử dụng được cho là không xác định.
R .. GitHub DỪNG TRỢ GIÚP LÚC NỮA,

Thật tệ khi trình biên dịch C bắt đầu cấm khai báo mảng có độ dài bằng không; không phải vì sự cấm đoán đó, nhiều trình biên dịch sẽ không phải thực hiện bất kỳ xử lý đặc biệt nào để làm cho chúng hoạt động như chúng "nên làm", nhưng vẫn có thể mã chữ hoa đặc biệt cho các mảng một phần tử (ví dụ: nếu *foochứa mảng đơn phần tử boz, biểu thức foo->boz[biz()*391]=9;có thể được đơn giản hóa thành biz(),foo->boz[0]=9;). Thật không may, việc từ chối mảng 0 phần tử của trình biên dịch có nghĩa là rất nhiều mã sử dụng mảng một phần tử để thay thế và sẽ bị phá vỡ bởi sự tối ưu hóa đó.
supercat

11

Cách thực hiện cụ thể đó không được xác định rõ ràng trong bất kỳ tiêu chuẩn C nào, nhưng C99 có bao gồm "struct hack" như một phần của ngôn ngữ. Trong C99, thành viên cuối cùng của một cấu trúc có thể là một "thành viên mảng linh hoạt", được khai báo là char foo[](với bất kỳ kiểu nào bạn muốn thay thế char).


Nói một cách dễ hiểu, đó không phải là cách hack cấu trúc. Cấu trúc hack sử dụng một mảng có kích thước cố định, không phải là một thành viên mảng linh hoạt. Câu hỏi về việc hack struct là gì và là UB. Các thành viên mảng linh hoạt có vẻ như là một nỗ lực để xoa dịu loại dân gian được thấy trong chủ đề này phàn nàn về thực tế đó.
underscore_d

7

Đó không phải là hành vi không xác định , bất kể bất kỳ ai, quan chức hay người khác , nói gì, bởi vì nó được định nghĩa bởi tiêu chuẩn. p->s, ngoại trừ khi được sử dụng làm giá trị, đánh giá một con trỏ giống hệt với (char *)p + offsetof(struct T, s). Đặc biệt, đây là một charcon trỏ hợp lệ bên trong đối tượng malloc'd và có 100 (hoặc nhiều hơn, phụ thuộc vào việc cân nhắc căn chỉnh) địa chỉ kế tiếp ngay sau nó, cũng có giá trị như charcác đối tượng bên trong đối tượng được cấp phát. Thực tế là con trỏ được tạo ra bằng cách sử dụng ->thay vì thêm rõ ràng phần bù vào con trỏ được trả về malloc, ép kiểu char *, là không liên quan.

Về mặt kỹ thuật, p->s[0]là một phần tử duy nhất của charmảng bên trong cấu trúc, một vài phần tử tiếp theo (ví dụ: p->s[1]thông qua p->s[3]) có thể là các byte đệm bên trong cấu trúc, có thể bị hỏng nếu bạn thực hiện gán cho toàn bộ cấu trúc nhưng không phải nếu bạn chỉ truy cập riêng lẻ các thành viên và phần còn lại là không gian bổ sung trong đối tượng được cấp phát mà bạn có thể tự do sử dụng theo cách bạn muốn, miễn là bạn tuân theo các yêu cầu về căn chỉnh (và charkhông có yêu cầu về căn chỉnh).

Nếu bạn lo lắng rằng khả năng chồng chéo với các byte đệm trong cấu trúc bằng cách nào đó có thể gọi ra quỷ mũi, bạn có thể tránh điều này bằng cách thay thế 1in [1]bằng một giá trị đảm bảo rằng không có phần đệm ở cuối cấu trúc. Một cách đơn giản nhưng lãng phí để làm điều này là tạo một cấu trúc với các thành viên giống hệt nhau ngoại trừ không có mảng nào ở cuối và sử dụng s[sizeof struct that_other_struct];cho mảng. Sau đó, p->s[i]được xác định rõ ràng là một phần tử của mảng trong struct for i<sizeof struct that_other_structvà như một đối tượng char tại một địa chỉ theo sau phần cuối của struct for i>=sizeof struct that_other_struct.

Chỉnh sửa: Trên thực tế, trong thủ thuật trên để có được kích thước phù hợp, bạn cũng có thể cần đặt một liên hợp chứa mọi kiểu đơn giản trước mảng, để đảm bảo rằng bản thân mảng bắt đầu với căn chỉnh tối đa thay vì ở giữa phần đệm của một số phần tử khác . Một lần nữa, tôi không tin rằng bất kỳ điều nào trong số này là cần thiết, nhưng tôi đang cung cấp nó cho những luật sư ngôn ngữ hoang tưởng nhất ngoài kia.

Chỉnh sửa 2: Sự chồng chéo với các byte đệm chắc chắn không phải là vấn đề, do một phần khác của tiêu chuẩn. C yêu cầu rằng nếu hai cấu trúc đồng ý trong một dãy con ban đầu của các phần tử của chúng, các phần tử ban đầu chung có thể được truy cập thông qua một con trỏ tới một trong hai kiểu. Do đó, nếu một cấu trúc giống hệt struct Tnhưng có mảng cuối cùng lớn hơn được khai báo, thì phần tử s[0]sẽ phải trùng với phần tử s[0]trong struct Tvà sự hiện diện của các phần tử bổ sung này không thể ảnh hưởng hoặc bị ảnh hưởng khi truy cập các phần tử chung của cấu trúc lớn hơn bằng cách sử dụng một con trỏ tới struct T.


4
Bạn nói đúng rằng bản chất của số học con trỏ là không liên quan, nhưng bạn sai khi truy cập vượt quá kích thước đã khai báo của mảng. Xem N1494 (bản nháp C1x công khai mới nhất) phần 6.5.6 đoạn 8 - bạn thậm chí không được phép thực hiện phép cộng có con trỏ nhiều hơn một phần tử vượt qua kích thước đã khai báo của mảng và bạn không thể bỏ qua nó ngay cả khi nó chỉ là một yếu tố quá khứ.
zwol

1
@Zack: điều đó đúng nếu đối tượng là một mảng. Sẽ không đúng nếu đối tượng là một đối tượng được cấp phát bởi mallocnó đang được truy cập dưới dạng một mảng hoặc nếu đó là một cấu trúc lớn hơn đang được truy cập thông qua một con trỏ đến một cấu trúc nhỏ hơn có các phần tử là tập con ban đầu của các phần tử của cấu trúc lớn hơn, trong số khác các trường hợp.
R .. GitHub NGỪNG TRỢ GIÚP TỪ NGÀY

6
+1 Nếu mallockhông phân bổ phạm vi bộ nhớ có thể được truy cập bằng số học con trỏ, nó sẽ được sử dụng như thế nào? Và nếu p->s[1]được tiêu chuẩn định nghĩa là đường cú pháp cho số học con trỏ, thì câu trả lời này chỉ đơn thuần là cảnh báo lại rằng mallochữu ích. Điều gì còn lại để thảo luận? :)
Daniel Earwicker

3
Bạn có thể lập luận rằng nó được xác định rõ ràng bao nhiêu tùy thích, nhưng điều đó không thay đổi thực tế là không phải vậy. Tiêu chuẩn rất rõ ràng về quyền truy cập vượt ra ngoài giới hạn của một mảng và giới hạn của mảng này là 1. Nó chính xác là đơn giản như vậy.
Các cuộc đua ánh sáng trong quỹ đạo

3
@R .., tôi nghĩ, giả định của bạn rằng hai con trỏ so sánh bằng nhau phải hoạt động giống nhau là sai. Hãy xem xét int m[1]; int n[1]; if(m+1 == n) m[1] = 0;giả sử ifchi nhánh được nhập. Đây là UB (và không được đảm bảo khởi tạo n) theo 6.5.6 trang8 (câu cuối cùng), khi tôi đọc nó. Liên quan: 6.5.9 p6 với chú thích 109. (Tài liệu tham khảo là C11 n1570.) [...]
mafso

7

Vâng, đó là hành vi không xác định về mặt kỹ thuật.

Lưu ý rằng có ít nhất ba cách để thực hiện "struct hack":

(1) Khai báo mảng theo sau với kích thước 0 (cách "phổ biến" nhất trong mã kế thừa). Đây rõ ràng là UB, vì khai báo mảng kích thước 0 luôn không hợp lệ trong C. Ngay cả khi nó biên dịch, ngôn ngữ này không đảm bảo về hành vi của bất kỳ mã vi phạm ràng buộc nào.

(2) Khai báo mảng với kích thước hợp pháp tối thiểu - 1 (trường hợp của bạn). Trong trường hợp này, bất kỳ nỗ lực nào để lấy con trỏ đến p->s[0]và sử dụng nó cho số học con trỏ vượt ra ngoài p->s[1]là hành vi không xác định. Ví dụ: triển khai gỡ lỗi được phép tạo ra một con trỏ đặc biệt với thông tin phạm vi được nhúng, con trỏ này sẽ mắc bẫy mỗi khi bạn cố gắng tạo một con trỏ vượt quá p->s[1].

(3) Khai báo mảng có kích thước "rất lớn" như 10000 chẳng hạn. Ý tưởng là kích thước được khai báo phải lớn hơn bất cứ thứ gì bạn có thể cần trong thực tế. Phương pháp này không có UB liên quan đến phạm vi truy cập mảng. Tuy nhiên, trong thực tế, tất nhiên, chúng tôi sẽ luôn phân bổ lượng bộ nhớ nhỏ hơn (chỉ nhiều khi thực sự cần thiết). Tôi không chắc về tính hợp pháp của điều này, tức là tôi tự hỏi việc phân bổ ít bộ nhớ cho đối tượng hơn kích thước đã khai báo của đối tượng là hợp pháp như thế nào (giả sử chúng ta không bao giờ truy cập vào các thành viên "không được cấp phát").


1
Trong (2), s[1]không phải là hành vi không xác định. Nó giống như *(s+1), giống như *((char *)p + offsetof(struct T, s) + 1), là một con trỏ hợp lệ đến a chartrong đối tượng được cấp phát.
R .. GitHub NGỪNG TRỢ GIÚP LÚC NỮA, 14/09/10

Mặt khác, tôi gần như chắc chắn (3) là hành vi không xác định. Bất cứ khi nào bạn thực hiện bất kỳ thao tác nào phụ thuộc vào cấu trúc như vậy nằm tại địa chỉ đó, trình biên dịch có thể tự do tạo mã máy đọc từ bất kỳ phần nào của cấu trúc. Nó có thể là vô dụng hoặc nó có thể là một tính năng an toàn để kiểm tra phân bổ nghiêm ngặt, nhưng không có lý do gì mà một triển khai không thể làm được.
R .. GitHub DỪNG TRỢ GIÚP

R: Nếu một mảng được khai báo có kích thước (không chỉ là foo[]đường cú pháp cho *foo), thì bất kỳ truy cập nào vượt quá kích thước nhỏ hơn đã khai báo và kích thước được phân bổ của nó là UB, bất kể số học con trỏ được thực hiện như thế nào.
zwol

1
@Zack, bạn đã sai một số thứ. foo[]trong một cấu trúc không phải là đường cú pháp cho *foo; nó là một thành viên mảng linh hoạt C99. Phần còn lại, hãy xem câu trả lời của tôi và nhận xét về các câu trả lời khác.
R .. GitHub NGỪNG TRỢ GIÚP ICE

6
Vấn đề là một số thành viên của ủy ban rất muốn "hack" này là UB, bởi vì họ hình dung ra một thế giới thần tiên nào đó mà việc triển khai C có thể thực thi giới hạn con trỏ. Tuy nhiên, dù tốt hơn hay tệ hơn, làm như vậy sẽ mâu thuẫn với các phần khác của tiêu chuẩn - những thứ như khả năng so sánh các con trỏ để bình đẳng (nếu các giới hạn được mã hóa trong chính con trỏ) hoặc yêu cầu bất kỳ đối tượng nào cũng có thể truy cập được thông qua một unsigned char [sizeof object]mảng tưởng tượng phủ . Tôi ủng hộ tuyên bố của mình rằng thành viên mảng linh hoạt "hack" cho trước C99 có hành vi được xác định rõ.
R .. GitHub DỪNG TRỢ GIÚP LÚC NỮA,

3

Tiêu chuẩn khá rõ ràng rằng bạn không thể truy cập những thứ bên cạnh phần cuối của một mảng. (và đi qua con trỏ không giúp ích gì, vì bạn không được phép tăng thậm chí con trỏ qua một sau khi kết thúc mảng).

Và để "làm việc trong thực tế". Tôi đã thấy trình tối ưu hóa gcc / g ++ sử dụng phần này của tiêu chuẩn, do đó tạo ra mã sai khi gặp C không hợp lệ này.


bạn có thể đưa ra một ví dụ không?
Tal,

1

Nếu một trình biên dịch chấp nhận một cái gì đó như

typedef struct {
  int len;
  char dat [];
};

Tôi nghĩ khá rõ ràng rằng nó phải sẵn sàng chấp nhận một chỉ số phụ trên 'dat' vượt quá độ dài của nó. Mặt khác, nếu ai đó mã một cái gì đó như:

typedef struct {
  int bất cứ điều gì;
  char dat [1];
} HƯỚNG DẪN CỦA TÔI;

và sau đó truy cập somestruct-> dat [x]; Tôi sẽ không nghĩ rằng trình biên dịch có bất kỳ nghĩa vụ nào phải sử dụng mã tính toán địa chỉ sẽ hoạt động với các giá trị lớn của x. Tôi nghĩ nếu một người muốn thực sự an toàn, mô hình phù hợp sẽ giống như:

#define LARGEST_DAT_SIZE 0xF000
typedef struct {
  int bất cứ điều gì;
  char dat [LARGEST_DAT_SIZE];
} HƯỚNG DẪN CỦA TÔI;

và sau đó thực hiện malloc gồm (sizeof (MYTHER) -LARGEST_DAT_SIZE + mong muốn_array_length) byte (lưu ý rằng nếu mong muốn_array_length lớn hơn LARGEST_DAT_SIZE, kết quả có thể không được xác định).

Ngẫu nhiên, tôi nghĩ quyết định cấm mảng độ dài bằng 0 là một điều không may (một số phương ngữ cũ hơn như Turbo C hỗ trợ nó) vì mảng độ dài bằng 0 có thể được coi là dấu hiệu cho thấy trình biên dịch phải tạo mã sẽ hoạt động với các chỉ số lớn hơn .

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.