2010-02-22 9 views
10

Các ANSI C ngữ pháp từ -link- cho tôi các quy tắc sau đây để khai báo mảng:ANSI-C ngữ pháp - tờ khai mảng như [*] et Alii

(1) | direct_declarator '[' type_qualifier_list assignment_expression ']' 
(2) | direct_declarator '[' type_qualifier_list ']' 
(3) | direct_declarator '[' assignment_expression ']' 
(4) | direct_declarator '[' STATIC type_qualifier_list assignment_expression ']' 
(5) | direct_declarator '[' type_qualifier_list STATIC assignment_expression ']' 
(6) | direct_declarator '[' type_qualifier_list '*' ']' 
(7) | direct_declarator '[' '*' ']' 
(8) | direct_declarator '[' ']' 

Bây giờ tôi có một số câu hỏi về những:

  • Tôi có thể sử dụng (1) - (6) ngoại trừ (3) chỉ trong C99 không?
  • (4) và (5) là gì? Từ khóa 'tĩnh' gây nhầm lẫn cho tôi.
  • Nơi sử dụng (6)?
  • sự khác biệt giữa hai nguyên mẫu hàm sau là gì:

    void foo(int [*]);

    void foo(int []);

Cảm ơn bạn.

Trả lời

15

Bạn không thể sử dụng loại vòng loại hoặc static trong phần kích thước của khai báo mảng trong C89/90. Các tính năng này dành riêng cho C99.

static trong khai báo mảng cho trình biên dịch biết bạn hứa rằng số lượng phần tử được chỉ định sẽ luôn có mặt trong mảng được chuyển làm đối số thực tế. Điều này có thể giúp các trình biên dịch tạo ra mã hiệu quả hơn. Nếu bạn vi phạm lời hứa của bạn trong mã thực tế (ví dụ: vượt qua một mảng nhỏ hơn), hành vi không xác định. Ví dụ:

void foo(int a[static 3]) { 
    ... 
} 

int main() { 
    int a[4], b[2]; 
    foo(a); /* OK */ 
    foo(b); /* Undefined behavior */ 
} 

Phần khai báo mảng chỉ được sử dụng trong khai báo mẫu chức năng.Nó chỉ ra rằng mảng có độ dài biến đổi (VLA). Ví dụ, trong định nghĩa chức năng bạn có thể sử dụng một VLA với bê tông chạy theo thời gian kích thước

void foo(int n, int a[n]) /* `a` is VLA because `n` is not a constant */ 
{ 
    ... 
} 

Khi bạn khai báo nguyên mẫu bạn có thể làm tương tự

void foo(int n, int a[n]); /* `a` is VLA because `n` is not a constant */ 

nhưng nếu bạn không xác định các tên tham số (mà là OK trong nguyên mẫu), bạn không thể sử dụng n làm kích thước mảng của khóa học. Tuy nhiên, nếu bạn vẫn phải nói với trình biên dịch mà mảng sẽ là một VLA, bạn có thể sử dụng * cho mục đích đó

void foo(int, int a[*]); /* `a` is VLA because size is `*` */ 

Lưu ý, rằng ví dụ với một mảng 1D không phải là một tốt nhất. Thậm chí nếu bạn bỏ qua * và khai báo hàm trên như

void foo(int, int a[]); 

thì mã vẫn sẽ làm việc tốt, bởi vì trong chức năng khai báo kiểu tham số mảng được ngầm thay thế bằng kiểu con trỏ anyway. Nhưng một khi bạn bắt đầu sử dụng mảng đa chiều, việc sử dụng thích hợp * trở nên quan trọng. Ví dụ, nếu hàm được định nghĩa là

void bar(int n, int m[n][n]) { /* 2D VLA */ 
    ... 
} 

những nguyên mẫu có thể trông như sau

void bar(int n, int m[n][n]); /* 2D VLA */ 

hoặc như

void bar(int, int m[*][*]); /* 2d VLA */ 

Trong trường hợp sau là người đầu tiên * thể được bỏ qua (vì thay thế mảng-thành-con trỏ), nhưng không phải là số thứ hai *.

+0

Tôi cho rằng nơi điều này thực sự quan trọng là khi bạn thực hiện 'f (int, int, int a [*] [*])' hoặc tương tự ... – dmckee

+0

@dmckee: Tôi vừa thêm vào câu trả lời của mình :) – AnT

+0

Rất nhiều người suy nghĩ dọc theo cùng một dòng quanh đây. Tâm trí tuyệt vời và tất cả ... – dmckee

0

My K & R2nd (bao gồm và bao gồm tiêu chuẩn ANSI) dường như không nói bất cứ điều gì về [*] hoặc trong văn bản hoặc trong chính tiêu chuẩn. Tôi cũng không thể làm cho ngữ pháp chính thức trong tiêu chuẩn chấp nhận cú pháp đó.

Nó có thể liên quan đến K & R c (mặc dù tôi dường như không nhớ lại), có thể là một phần mở rộng chung, hoặc là một đề xuất cuối cùng không đưa ra tiêu chuẩn.

Tôi sẽ giả sử nó làm cho thứ nguyên của mảng không được chỉ định rõ ràng. Nhưng tôi đoán vậy.


Hmm ... gcc chấp nhận

#include <stdio.h> 

void f(int s, int a[*]); 

int main(void){ 
    int a[2] = {0}; 
    f(2,a); 
    return 0; 
} 

void f(int s, int a[]){ 
    int i; 
    for (i=0; i<s; ++i){ 
    printf("%d\n",a[i]); 
    } 
} 

trong ansi, chế độ C89 và C99; không đưa ra cảnh báo nào ngay cả với -Wall. Lưu ý rằng nó không thích cú pháp [*] trong định nghĩa hàm. Thêm -pedantic làm cho nó phàn nàn về cú pháp [*] ở chế độ c89 và ansi, nhưng nó tiếp tục chấp nhận ở chế độ c99.

+0

-Wall sẽ không đưa ra cảnh báo cho tất cả cú pháp trong C89 được coi là phần mở rộng của GNU. Tôi nghĩ rằng bạn cũng sẽ cần -pantic cho điều đó. K & R lần thứ 2 được viết dựa trên tiêu chuẩn ANSI C89 (Hiện tại ISO C90), vì vậy sẽ không bao gồm ngữ pháp C99. C99 thêm các mảng chiều dài biến đổi và các cú pháp liên quan đến mảng khác. – Clifford

+0

Về không thích [*] trong định nghĩa hàm, tiêu chuẩn cho biết [*] "là loại mảng có độ dài thay đổi của kích thước không xác định, chỉ có thể được sử dụng trong các khai báo với * chức năng nguyên mẫu * phạm vi" – Clifford

1

Tôi hy vọng bạn không cố gắng học ngữ pháp C từ thông số kỹ thuật yacc !? Liên kết bạn đã đăng có vẻ như được dựa trên the ISO C99 draft. Phần liên quan là 6.7.5.2. Các từ ngữ là phức tạp (nhưng ít hơn so với cú pháp yacc có lẽ!)

+0

Không, tôi ' m không học C với điều đó ;-) – tur1ng

+0

@ tur1ng: Nó chỉ là bạn nói "ngữ pháp ANSI C từ -link-cung cấp cho * tôi * các quy tắc sau cho khai báo mảng". Tôi đề nghị rằng những quy tắc này dành cho tiêu thụ yacc, không phải tiêu dùng của con người, và tiêu chuẩn ISO dành cho con người (và là tài liệu nguồn cho yacc). Và tôi đã nói "C ngữ pháp" thay vì chỉ "C"; bạn có thể học C đến một trình độ làm việc mà không cần hiểu biết sâu sắc về ngữ pháp. Có lẽ bạn đang viết một trình biên dịch hoặc phân tích tĩnh sau đó? – Clifford

+0

Xin lỗi, tôi đã đọc quá nhanh. Vâng, đó là cho một máy phân tích tĩnh. – tur1ng