Tại sao argc không phải là một hằng số?


104
int main( const int argc , const char[] const argv)

Vì mục C ++ hiệu quả # 3 tuyên bố "Sử dụng const bất cứ khi nào có thể", tôi bắt đầu nghĩ "tại sao không tạo các tham số 'không đổi' này const" ?.

Có tình huống nào mà giá trị của argcđược sửa đổi trong một chương trình không?


38
Bạn có thể tự do khai báo argcconst.
Oliver Charlesworth 13/1213

14
Tôi đã thấy nhiều mã chất lượng sản xuất mà làm điều này:--argc
Aniket Inge

2
Tôi nghĩ nó phải làm với di sản C, nhưng không biết làm thế nào.
Luis Machuca 13/1213

13
Tôi nghi ngờ rằng "Sử dụng const bất cứ khi nào có thể" đề cập đến những thứ được truyền qua tham chiếu, trong đó bất kỳ sửa đổi nào trong hàm vẫn tồn tại sau khi hàm đã thoát. Điều này không đúng với những thứ được truyền theo giá trị, như một số nguyên, vì vậy không có gì thu được bằng cách tạo ra nó const; thực sự, truyền argcnhư một const intphương tiện mà sau đó bạn không thể sử dụng argcnhư một bộ đếm bên trong hàm.
Steve Melnikoff 13/12/13

4
@SteveMelnikoff: Tôi không đồng ý rằng không có gì để đạt được bằng cách tạo constmột tham số truyền theo giá trị. Xem ví dụ stackoverflow.com/a/8714278/277304stackoverflow.com/a/117557/277304
leonbloy

Câu trả lời:


114

Trong trường hợp này, lịch sử là một yếu tố. C đã định nghĩa những đầu vào này là "không phải là hằng số" và khả năng tương thích với (một phần tốt) mã C hiện có là mục tiêu ban đầu của C ++.

Một số API UNIX, chẳng hạn như getopt, thực sự thao tác argv[], vì vậy nó không thể được thực hiện constvì lý do đó.

(Bên cạnh: Thật thú vị, mặc dù getoptnguyên mẫu của nó cho thấy nó sẽ không sửa đổi argv[]nhưng có thể sửa đổi các chuỗi được trỏ đến, trang người dùng Linux chỉ ra rằng getopthoán vị các đối số của nó và có vẻ như họ biết họ đang nghịch ngợm . Trang người đàn ông tại Mở Nhóm không đề cập đến hoán vị này.)

Đưa constvào argcargvsẽ không mua nhiều, và nó sẽ làm mất hiệu lực của một số phương pháp lập trình cũ, chẳng hạn như:

// print out all the arguments:
while (--argc)
    std::cout << *++argv << std::endl;

Tôi đã viết những chương trình như vậy bằng C, và tôi biết mình không đơn độc. Tôi đã sao chép ví dụ từ một nơi nào đó .


1
-1 Re "Một số API UNIX, chẳng hạn như getopt, thực sự thao tác với argv [], vì vậy nó không thể được đặt là const vì lý do đó.". Người ta có thể tự do thêm một cấp cao nhất constvào argvmà không ảnh hưởng đến những gì bất kỳ hàm C nào có thể làm. Ngoài ra, getoptđược khai báo là int getopt(int argc, char * const argv[], const char *optstring);. Và đây constkhông phải là cấp cao nhất, nhưng khai báo các con trỏ là const, một lời hứa sẽ không sửa đổi chúng (mặc dù các chuỗi mà chúng trỏ tới có thể được sửa đổi).
Chúc mừng và hth. - Alf

@ Cheersandhth.-Alf: getopt hoán vị argv[]mảng theo mặc định, nhưng không sửa đổi các chuỗi. (Từ hoán vị đến trực tiếp từ trang người đàn ông.) Điều đó có nghĩa là argvbản thân mảng không phải là constngay cả khi các chuỗi mà nó trỏ đến. Làm thế nào để hoán vị argv[]mảng không thao tác với nó?
Joe Z

1
Tôi xin lỗi, các tài liệu tôi đã xem không nhất quán: chữ ký không cho phép hoán vị như vậy. Xem ví dụ . Tuy nhiên, văn bản sau đây nói rằng nó không hoán vị. Vì vậy, tôi đang gỡ bỏ phiếu giảm giá.
Chúc mừng và hth. - Alf

1
Đó là, bạn sẽ phải thực hiện một số chỉnh sửa để "mở khóa" để tôi có thể xóa downvote. Và không, bạn đang hiểu sai nguyên mẫu. Hãy xem ví dụ tôi đã cung cấp và lỗi biên dịch, "chỉ định vị trí chỉ đọc".
Chúc mừng và hth. - Alf

1
@ Cheersandhth.-Alf: Thật thú vị ... Tôi đã tìm kiếm nguyên mẫu trong tiêu đề, và nó có char* const* ___argvở đó, nhưng giao diện thực sự tuân theo const char **. Như nhầm lẫn. Tôi đã nhầm lẫn giữa mức độ ưu tiên của []*đối với const. Lời xin lỗi của tôi.
Joe Z

36

Tiêu chuẩn C (ISO / IEC 9899: 2011) cho biết:

5.1.2.2.1 Khởi động chương trình

¶1 Hàm được gọi khi khởi động chương trình được đặt tên main. Việc thực hiện tuyên bố không có nguyên mẫu nào cho chức năng này. Nó sẽ được định nghĩa với kiểu trả về là int và không có tham số:

int main(void) { /* ... */ }

hoặc với hai tham số (ở đây được gọi là argcargv, mặc dù bất kỳ tên nào cũng có thể được sử dụng, vì chúng là cục bộ của hàm mà chúng được khai báo):

int main(int argc, char *argv[]) { /* ... */ }

hoặc tương đương; 10) hoặc theo một số cách thức triển khai khác được xác định.

¶2 Nếu chúng được khai báo, các tham số của mainhàm sẽ tuân theo các ràng buộc sau:

  • Giá trị của argcsẽ không âm.
  • argv[argc] sẽ là một con trỏ null.
  • Nếu giá trị của argclớn hơn 0, thì các thành viên mảng argv[0]thông qua argv[argc-1]bao gồm phải chứa các con trỏ đến các chuỗi, được cung cấp các giá trị do môi trường máy chủ xác định việc triển khai trước khi khởi động chương trình. Mục đích là cung cấp cho chương trình thông tin được xác định trước khi khởi động chương trình từ nơi khác trong môi trường được lưu trữ. Nếu môi trường máy chủ không có khả năng cung cấp các chuỗi ký tự cả viết hoa và viết thường, thì việc triển khai phải đảm bảo rằng các chuỗi được nhận bằng chữ thường.
  • Nếu giá trị của argclớn hơn 0, chuỗi được trỏ tới argv[0] biểu thị tên chương trình; argv[0][0]sẽ là ký tự rỗng nếu tên chương trình không có sẵn từ môi trường máy chủ. Nếu giá trị của argclớn hơn một, các chuỗi được trỏ đến argv[1]thông qua argv[argc-1] đại diện cho các tham số chương trình.
  • Các tham số argcargvvà các chuỗi được trỏ đến bởi argvmảng sẽ được chương trình sửa đổi và giữ lại các giá trị được lưu trữ cuối cùng của chúng giữa khởi động chương trình và kết thúc chương trình.

10) Do đó, intcó thể được thay thế bằng tên typedef được định nghĩa là int, hoặc kiểu của argvcó thể được viết là char **argv, v.v.

Lưu ý gạch đầu dòng cuối cùng. Nó nói rằng cả hai argcargvnên được sửa đổi. Chúng không phải được sửa đổi, nhưng chúng có thể được sửa đổi.


Hấp dẫn. Trên một lưu ý bán liên quan, tôi chạy vào một lỗi tai nạn gây rằng hóa ra là vì Qt của QApplication(argc,argv)constructor được định nghĩa với argcbằng cách tham khảo . Điều đó làm tôi ngạc nhiên.
HostileFork nói không tin tưởng SE

23

argcthường không phải là một hằng số bởi vì chữ ký hàm cho main()các ngày trước const.

Vì argc là một biến ngăn xếp, việc thay đổi nó sẽ không ảnh hưởng đến bất kỳ điều gì khác ngoài việc xử lý dòng lệnh của riêng bạn.

Tất nhiên, bạn có thể tự do khai báo constnếu bạn muốn.


8

Mức cao nhất consttrên một đối số chính thức không phải là một phần của kiểu hàm. Bạn có thể thêm hoặc bớt nó tùy thích: nó chỉ ảnh hưởng đến những gì bạn có thể làm với đối số trong việc triển khai hàm.

Vì vậy, cho argcbạn có thể thêm tự do thêm a const.

Nhưng đối với argvbạn không thể tạo dữ liệu ký tự constmà không thay đổi chữ ký hàm. Điều đó có nghĩa là nó không phải là một trong những mainchữ ký hàm tiêu chuẩn và sẽ không phải được công nhận là một mainhàm. Vì vậy, không phải là một ý kiến ​​hay.


Một lý do chính đáng cho việc không sử dụng các mainđối số tiêu chuẩn trong các chương trình không phải đồ chơi là trong Windows, chúng không thể biểu diễn các đối số chương trình thực tế, chẳng hạn như tên tệp với các ký tự quốc tế. Đó là bởi vì trong Windows, chúng được mã hóa theo quy ước rất mạnh là Windows ANSI. Trong Windows, bạn có thể triển khai một số phương tiện truy cập đối số di động hơn về mặt GetCommandLinehàm API.


Tóm lại, không có gì ngăn cản bạn thêm constvào argc, nhưng tính hữu ích nhất consttrên argvsẽ cung cấp cho bạn một hàm không chuẩn main, hầu hết có thể không được công nhận như vậy. Thật hạnh phúc (nói một cách mỉa mai) có lý do chính đáng để không sử dụng các mainđối số tiêu chuẩn cho mã nghiêm túc di động. Rất đơn giản, đối với thực tế, họ chỉ hỗ trợ ASCII cũ, chỉ với các chữ cái trong bảng chữ cái tiếng Anh.


1
Nếu bạn đang phát triển cho Windows với cygwin hoặc một libc khác hỗ trợ đúng UTF-8 (theo tôi biết, cygwin là công cụ duy nhất hiện tại nhưng tôi có người đang làm việc trên một tùy chọn khác), mainchữ ký chuẩn hoạt động tốt và bạn có thể nhận các đối số Unicode tùy ý.
R .. GitHub NGỪNG GIÚP ĐỠ ICE

@R chúng cũng hoạt động tốt trên hầu hết các nền tảng * nix. vấn đề không phải là liệu có nền tảng nơi chúng hoạt động hay không. nhưng, một sáng kiến ​​rất hay , và khi nó xảy ra, tôi cũng đang làm như vậy, ít nhiều nghiêm túc
Cheers and hth. - Alf

4

Chữ ký của mainlà một phần của một hiện vật lịch sử từ C. Trong lịch sử Ckhông có const.

Tuy nhiên, bạn có thể khai báo tham số của mình constvì tác động của const chỉ là thời gian biên dịch.


Khi bạn nói "lịch sử C" chúng ta đang nói đến lịch sử như thế nào ở đây?
Joe Z

8
constđã được thêm vào C89 / C90; trước đó, nó không phải là một phần của C.
Jonathan Leffler

@JonathanLeffler: Bạn biết đấy, tôi đã quên điều đó. Tôi học C vào năm 1992. Trước đó tôi là một hacker Pascal, BASIC và lắp ráp.
Joe Z

2

Bởi vì argclà một biến cục bộ (và, trong C ++, không phải là một tham chiếu hay một cái gì đó) và vì vị trí đặc biệt củamain có nghĩa là những trò tai quái về khả năng tương thích ngược khiến nó mất một khoảng thời gian rất lớn mà không có lý do thuyết phục nào để buộc các ứng dụng làm cho nó là hằng số.

main() {}

int main() {}

main() { return 0; }

main(int argc, char* argv[]) { return 0; }

int main(const int argc, const char** argv) { /* no return*/ }

những điều này và nhiều biến thể khác sẽ được biên dịch trên một loạt các trình biên dịch C và C ++.

Vì vậy, cuối cùng không phải là argc không phải là const, chỉ là nó không cần phải như vậy, nhưng nó có thể là nếu bạn muốn.

http://ideone.com/FKldHF , C ví dụ:

main(const int argc, const char* argv[]) { return 0; }

http://ideone.com/m1qc9c , ví dụ về C ++

main(const int argc) {}

Lưu ý rằng các biến thể không có kiểu trả về rõ ràng không còn hợp lệ trong C99, chứ đừng nói đến C11, mặc dù các trình biên dịch tiếp tục cho phép chúng vì lý do tương thích ngược. Các trình biên dịch C ++ không nên chấp nhận các biến thể không có kiểu trả về.
Jonathan Leffler

@JonathanLeffler Đúng vậy, nhưng ví dụ về Ideone cuối cùng ở đó ( Ideone.com/m1qc9c ) là G ++ 4.8.2 với -std=c++11. Clang cũng chấp nhận nó nhưng không cho warning: only one parameter on 'main' declaration [-Wmain], và MSVC chỉ phản đối về việc thiếu bộ chỉ định kiểu trả về và thiếu tham chiếu đến argc. Một lần nữa, 'shenanigans' và '
leeway

1

Ngoài các lý do lịch sử, một lý do chính đáng để giữ argc và argv không constlà việc triển khai trình biên dịch không biết bạn sẽ làm gì với các đối số cho main, nó chỉ biết rằng nó phải cung cấp cho bạn những đối số đó.

Khi bạn đang xác định các chức năng của riêng mình và các nguyên mẫu liên quan, bạn biết bạn có thể thực hiện các tham số nào const những mà chức năng của bạn sẽ sửa đổi.

Tóm lại, bạn có thể khai báo rằng tất cả các tham số cho tất cả các hàm phải được khai báo constvà sau đó nếu bạn có lý do để thay đổi chúng (ví dụ: giảm chỉ số để tìm kiếm thông qua một mảng), bạn phải tạo cục bộ không phải là constbiến và sao chép các constgiá trị của đối số vào các biến đó. Điều đó làm cho công việc bận rộn và thêm LOC không có lợi ích thực sự. Một trình phân tích tĩnh tốt sẽ chọn nếu bạn không sửa đổi giá trị của một đối số và khuyên bạn nên tạo tham số const.

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.