Tại sao tự động a = 1; biên dịch trong C?


125

Mật mã:

int main(void)
{
    auto a=1;
    return 0;
}

được biên dịch mà không có lỗi bởi trình biên dịch MS Visual Studio 2012, khi tệp có phần mở rộng .c. Tôi đã luôn nghĩ rằng khi bạn sử dụng phần mở rộng .c, việc biên dịch phải theo cú pháp C chứ không phải C ++. Hơn nữa, theo như tôi biết tự động không có loại chỉ được phép trong C ++ kể từ C ++ 11, điều đó có nghĩa là loại đó được suy ra từ bộ khởi tạo.

Điều đó có nghĩa là trình biên dịch của tôi không dính vào C, hay mã thực sự đúng trong ngôn ngữ C?


8
Hoặc bạn biên dịch với chế độ C ++ (có thể) hoặc MS vẫn bị kẹt trong thiên niên kỷ trước. Tiềm ẩn intđã bị xóa khỏi tiêu chuẩn C vào năm 1999.
Jens Gustyt

16
@JensGustyt MSVC ++ chỉ hỗ trợ C89 (và một vài tính năng từ C99). Nó là một trình biên dịch C ++.
ntoskrnl

3
@Brandin: Với tùy chọn / Wall, nó đưa ra cảnh báo C4431, nói rằng một trình xác định kiểu bị thiếu và int mặc định đó không còn được hỗ trợ trong C (xem bình luận của Jens). Đó là một chút mâu thuẫn vì rõ ràng trình biên dịch này hỗ trợ nó ...
lee77

4
@JensGustyt Theo biện pháp đó, GCC 4.7, phát hành năm 2012, (và các phiên bản mới hơn, tôi nghi ngờ - tôi không có những thứ đó trong tay) cũng bị "mắc kẹt trong thiên niên kỷ trước". Nó biên dịch mã của OP mà không cần thông báo khi không đưa ra bất kỳ cờ nào.

3
@delnan, tôi đã giả sử ít nhất là OP đã bật các mức cảnh báo. Tôi rõ ràng đã sai. Và theo một nghĩa nào đó thì điều này là đúng, gcc cũng vẫn bị kẹt ở đó, vì họ vẫn không có C99 (hoặc một biến thể) như mặc định. clang cảnh báo về việc xây dựng, thậm chí không có cờ.
Jens Gustyt

Câu trả lời:


240

autolà một từ khóa C cũ có nghĩa là "phạm vi địa phương". auto agiống như auto int avà bởi vì phạm vi cục bộ là mặc định cho một biến được khai báo bên trong hàm, nó cũng giống như int atrong ví dụ này.

Từ khóa này thực sự là một phần còn lại từ tiền thân B của C, nơi không có loại cơ sở: mọi thứ là int, con trỏ tới int, mảng của int. (*) Tuyên bố sẽ là autohoặc extrn[sic]. C được thừa hưởng "mọi thứ là int" như một quy tắc mặc định, vì vậy bạn có thể khai báo các số nguyên với

auto a;
extern b;
static c;

ISO C đã loại bỏ điều này, nhưng nhiều trình biên dịch vẫn chấp nhận nó để tương thích ngược. Nếu nó có vẻ lạ lẫm, thì bạn nên nhận ra rằng một quy tắc liên quan đang hoạt động

unsigned d;  // actually unsigned int

mà vẫn còn phổ biến trong mã hiện đại.

C ++ 11 đã sử dụng lại từ khóa, mà ít có bất kỳ lập trình viên C ++ nào đang sử dụng với nghĩa gốc, cho kiểu suy luận của nó. Điều này chủ yếu là an toàn vì intquy tắc "mọi thứ là " từ C đã bị loại bỏ trong C ++ 98; điều duy nhất bị phá vỡ là auto T a, điều mà không ai đang sử dụng. (Ở đâu đó trong các bài viết của mình về lịch sử của ngôn ngữ , Stroustrup bình luận về điều này, nhưng tôi không thể tìm thấy tài liệu tham khảo chính xác ngay bây giờ.)

(*) Xử lý chuỗi trong B rất thú vị: bạn sẽ sử dụng mảng intvà đóng gói nhiều ký tự trong mỗi thành viên. B thực sự là BCPL với cú pháp khác nhau.


7
Không, đây không phải là C hợp pháp kể từ năm 1999. Không có trình biên dịch C hiện đại nào cho phép điều này.
Jens Gustyt

18
@JensGustyt VS không yêu cầu cung cấp trình biên dịch C hiện đại. Từ tất cả các lần xuất hiện, công việc trên trình biên dịch C đã dừng lại từ nhiều năm trước; họ chỉ cung cấp nó để mọi người có thể tiếp tục biên dịch mã kế thừa. (Và tất nhiên, bất kỳ trình biên dịch C hiện đại phong nha nào cũng sẽ có các tùy chọn để hỗ trợ mã kế thừa. Bao gồm một tùy chọn cho K & R C.)
James Kanze

23
@JensGustyt: bạn có chắc không? Cả GCC và Clang đều cảnh báo về điều đó trong chế độ C99, nhưng họ không coi đó là lỗi ngoại trừ -Werror.
Fred Foo

2
@larsman, vâng, trong 6.7.2 có một ràng buộc rõ ràng cho điều đó: Ít nhất một công cụ xác định loại sẽ được đưa ra trong các công cụ xác định khai báo trong mỗi tuyên bố ...
Jens Gustyt

40
@JensGustyt - lại Không, đây không phải là C hợp pháp kể từ năm 1999. Không có trình biên dịch C hiện đại đàng hoàng nào cho phép điều này. Phát biểu đầu tiên là chính xác; đó là bất hợp pháp kể từ năm 1999. IMHO, tuyên bố thứ hai là không chính xác. Bất kỳ trình biên dịch C hiện đại phong nha phải cho phép điều này. Nhìn vào tất cả các mã kế thừa sẽ phải viết lại nếu họ không cho phép. Tôi đã viết một câu trả lời mở rộng về nhận xét này.
David Hammen

35

Đây vừa là câu trả lời vừa là nhận xét mở rộng cho Không, đây không phải là C hợp pháp kể từ năm 1999. Không có trình biên dịch C hiện đại đàng hoàng nào cho phép điều này.

Có, auto a=1;là bất hợp pháp trong C1999 (và cả C2011). Chỉ vì điều này là bất hợp pháp không có nghĩa là trình biên dịch C hiện đại sẽ từ chối mã có chứa các cấu trúc như vậy. Tôi sẽ tranh luận chính xác điều ngược lại, rằng một trình biên dịch C hiện đại đàng hoàng vẫn phải cho phép điều này.

Cả clang và gcc chỉ làm điều đó khi biên dịch mã mẫu trong câu hỏi so với các phiên bản 1999 hoặc 2011 của tiêu chuẩn. Cả hai trình biên dịch đưa ra một chẩn đoán và sau đó tiếp tục như thể tuyên bố phản đối đã được auto int a=1;.

Theo tôi, đây là những gì một trình biên dịch đàng hoàng nên làm. Bằng cách đưa ra chẩn đoán, tiếng kêu và gcc hoàn toàn tuân thủ tiêu chuẩn. Tiêu chuẩn không nói rằng trình biên dịch phải từ chối mã bất hợp pháp. Tiêu chuẩn chỉ nói rằng việc triển khai tuân thủ phải tạo ra ít nhất một thông báo chẩn đoán nếu đơn vị dịch thuật có vi phạm bất kỳ quy tắc cú pháp hoặc ràng buộc nào (5.1.1.3).

Cho mã có chứa các cấu trúc bất hợp pháp, bất kỳ trình biên dịch tử tế nào cũng sẽ cố gắng hiểu ý nghĩa của mã bất hợp pháp để trình biên dịch có thể tìm thấy lỗi tiếp theo trong mã. Trình biên dịch dừng ở lỗi đầu tiên không phải là trình biên dịch rất tốt. Có một cách để hiểu auto a=1, đó là áp dụng quy tắc "ẩn int". Quy tắc này buộc trình biên dịch diễn giải auto a=1như thể auto int a=1khi trình biên dịch được sử dụng ở chế độ C90 hoặc K & R.

Hầu hết các trình biên dịch thường từ chối mã (từ chối: từ chối tạo tệp đối tượng hoặc tệp thực thi) có chứa cú pháp bất hợp pháp. Đây là một trường hợp mà các tác giả biên dịch đã quyết định rằng việc không biên dịch không phải là lựa chọn tốt nhất. Điều tốt nhất để làm là đưa ra chẩn đoán, sửa mã và tiếp tục. Có quá nhiều mã kế thừa được tạo ra bằng các cấu trúc như register a=1;. Trình biên dịch sẽ có thể biên dịch mã đó ở chế độ C99 hoặc C11 (tất nhiên là có chẩn đoán).


1
@larsmans - Tôi có thể thấy bạn đến từ đâu. Bạn muốn một -ffs-please-stop-allowing-constructs-from-some-previous-millenniumtùy chọn trình biên dịch, hoặc ngắn gọn hơn, một -fstrict-compliancetùy chọn. Càu nhàu với trình biên dịch: "Khi tôi sử dụng -std = c11, tôi không ngờ rằng Kru R cổ đại sẽ biên dịch. Thực tế, tôi muốn nó không biên dịch!"
David Hammen

1
Trên thực tế không có, tôi muốn phải bật trên một lá cờ để có được những cruft tồi tệ nhất để biên dịch. Nhưng việc -std=c99nghiêm ngặt hơn sẽ là một bước đi đúng hướng :)
Fred Foo

1
Nếu bạn sử dụng gcc -g -O3 -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror(đó là những gì tôi thường xuyên sử dụng, thậm chí trên mã từ các câu hỏi trên SO), thì bạn sẽ khá gần với những gì bạn muốn. Tôi muốn GCC mặc định ít nhất -std=c99và tốt nhất là -std=c11(hoặc, -std=gnu11họ có nhiều khả năng sẽ làm điều đó), nhưng cho đến lúc đó, bạn có thể điều chỉnh các tùy chọn đó; -pedantic, -Wshadow, -Wold-style-declarationVà một số người khác có thể hữu ích, nhưng đây là một tốt bắt đầu từ thiết lập các tùy chọn.
Jonathan Leffler

3
@DavidHammen: Không phức tạp theo chu kỳ, người quản lý dự án, cũng không phải chính sách của công ty là các yếu tố của ngôn ngữ.
Jerry B

3
Lá cờ để có được những hành vi mà bạn muốn trong GCC là-pedantic-errors
τεκ

29

autocó ý nghĩa trong CC++trước Tiêu chuẩn 2011. Nó có nghĩa là một biến có tuổi thọ tự động, nghĩa là tuổi thọ được xác định bởi phạm vi . Điều này trái ngược với, ví dụ, statictrọn đời, trong đó một biến tồn tại "mãi mãi", bất kể phạm vi. autolà thời gian sống mặc định và gần như không bao giờ được đánh vần rõ ràng. Đây là lý do tại sao nó an toàn để thay đổi ý nghĩa trong C++.

Bây giờ C, trước Tiêu chuẩn 99, nếu bạn không chỉ định loại biến, nó sẽ mặc định int.

Vì vậy, với auto a = 1;bạn đang khai báo (và xác định) một intbiến, với tuổi thọ được xác định bởi phạm vi.

("trọn đời" được gọi đúng hơn là "thời lượng lưu trữ", nhưng tôi nghĩ rằng có lẽ ít rõ ràng hơn).


Được rồi, vì vậy thực sự tự động a = 1 được cho phép trong C và có nghĩa là một biến int có thời gian lưu trữ tự động.
lee77

1
Chính xác, "thời lượng lưu trữ" lấy một trong danh sách các giá trị, "tự động", "tĩnh", "động", "luồng". "Trọn đời" là thời gian sống thực tế của đối tượng. Vì vậy, biến có thời lượng lưu trữ "tự động" và trọn đời "thời lượng của phạm vi của mainhàm".
Steve Jessop

@Steve vâng, tôi không có ý ám chỉ điều đó autostaticlà hai khả năng duy nhất. Tôi đã cố gắng viết câu trả lời của mình theo cách nhắm vào người hỏi, người dường như khá mới đối với C++(và C), vì vậy tôi đã xem qua chi tiết một chút. Có lẽ đó là một ý tưởng tồi; họ cần được bảo hiểm sớm hay muộn.
BoBTFish

1
@BoBTFish: oh, tôi đã không phàn nàn về điều đó. Tôi chỉ có ý định mở rộng về sự khác biệt về ngữ nghĩa giữa "thời gian tồn tại", đó là thời lượng và "thời lượng lưu trữ" có thể được gọi chính xác hơn là "danh mục thời lượng lưu trữ".
Steve Jessop

Công intcụ ngầm này được xóa khỏi C từ năm 1999.
Jens Gustyt

8

Trong C và các phương ngữ lịch sử của C ++, autolà một từ khóa có nghĩa là alưu trữ tự động. Vì nó chỉ có thể được áp dụng cho các biến cục bộ, được tự động theo mặc định, không ai sử dụng nó; đó là lý do tại sao C ++ hiện đã sử dụng lại từ khóa.

Trong lịch sử, C đã cho phép khai báo biến mà không có chỉ định kiểu; loại mặc định là int. Vì vậy, tuyên bố này là tương đương với

int a=1;

Tôi nghĩ rằng điều này không được chấp nhận (và có thể bị cấm) trong C hiện đại; nhưng một số trình biên dịch phổ biến mặc định là C90 (theo tôi nghĩ là không cho phép điều đó) và thật khó chịu, chỉ bật cảnh báo nếu bạn yêu cầu cụ thể. Biên dịch với GCC và chỉ định C99 với -std=c99hoặc bật cảnh báo bằng -Wallhoặc -Wimplicit-int, đưa ra cảnh báo:

warning: type defaults to int in declaration of a

4
Nó thực sự bị cấm ở C kể từ năm 1999.
Jens Gustyt

5

Trong C, autocó nghĩa là điều tương tự registertrong C ++ 11: có nghĩa là một biến có thời lượng lưu trữ tự động.

Và trong C trước C99 (và trình biên dịch của Microsoft không hỗ trợ C99 hoặc C11, mặc dù nó có thể hỗ trợ các phần của nó), loại này có thể được bỏ qua trong nhiều trường hợp, trong đó nó sẽ được mặc định int.

Nó hoàn toàn không lấy kiểu từ trình khởi tạo. Bạn chỉ tình cờ chọn một trình khởi tạo tương thích.


1
Không phải là từ khóa đăng ký không dùng nữa trong C ++ 11 sao?
bẩn thỉu

@sordid Vâng, đúng vậy. Trước C ++ 11 autoregistercó cùng ý nghĩa chính xác (trước đó tôi đã nhận xét rằng có những hạn chế trong việc lấy registerđịa chỉ của biến -qualified, nhưng điều đó không chính xác đối với C ++). register, trong khi không tán thành, vẫn giữ ý nghĩa cũ của nó cho đến bây giờ.

5
@JensGustyt: Câu trả lời không nói rằng họ là. Nó nói rằng autotrong C có nghĩa giống như registertrong C ++, nó cũng vậy (cả hai đều có nghĩa là thời gian lưu trữ tự động và không có gì khác).
Mike Seymour

3

Visual loại hình biên dịch có sẵn tại right click on file -> Properties -> C/C++ -> Advanced -> Compile As. Để chắc chắn rằng nó được biên dịch dưới dạng tùy chọn lực C. Sau đó, /TCtrong trường hợp này, đó là những gì larsmans đã nói ( autotừ khóa C cũ ). Nó có thể được biên dịch thành C ++ mà bạn không biết.


3

Một lớp lưu trữ xác định phạm vi (mức độ hiển thị) và thời gian sống của các biến và / hoặc các hàm trong Chương trình C.

Có các lớp lưu trữ sau đây có thể được sử dụng trong Chương trình C

auto
register
static
extern

auto là lớp lưu trữ mặc định cho tất cả các biến cục bộ.

{
        int Count;
        auto int Month;
}

Ví dụ trên định nghĩa hai biến có cùng một lớp lưu trữ. auto chỉ có thể được sử dụng trong các hàm, tức là các biến cục bộ.

intlà loại mặc định cho automã bên dưới:

auto Month;
/* Equals to */
int Month;

Mã dưới đây là hợp pháp quá:

/* Default-int */
main()
{
    reurn 0;
}
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.