Int argc, char * argv [] nghĩa là gì?


508

Trong nhiều trình biên dịch và IDE của C ++, khi nó tạo ra hàm chính cho bạn, nó trông như thế này:

int main(int argc, char *argv[])

Khi tôi mã C ++ mà không có IDE, chỉ với trình biên dịch dòng lệnh, tôi gõ:

int main()

không có bất kỳ tham số nào. Điều này có nghĩa là gì, và nó có quan trọng đối với chương trình của tôi không?


47
Nếu chương trình của bạn sẽ bỏ qua các đối số dòng lệnh, thì những gì bạn viết là tốt. Nếu chương trình của bạn cần xử lý các đối số dòng lệnh, thì IDE đang thực hiện đúng.
Jonathan Leffler

30
Một gợi ý cho tin tặc: hãy thử khai báo int main(int argc, char* argv[], char* envp[])và in ra đối số cuối cùng. ;)
ulidtko

7
@ulidtko thật không tốt khi bạn đang dạy người mới giới thiệu lỗ hổng trong các chương trình của họ;)
Gab

13
@Gab cách in đơn giản các biến môi trường dẫn đến lỗ hổng? Chỉ cần không chuyển các chuỗi bị lỗi nguyên văn cho system()các cuộc gọi, truy vấn DB, v.v. Như thường lệ với đầu vào của người dùng.
ulidtko

2
@ulidtko Thú vị .. Bạn có thể giải thích lý do tại sao bạn không phải vượt qua chuỗi bị nhiễm độc, truy vấn db, v.v. trong khi sử dụng char **envpđối số không?
Master James

Câu trả lời:


651

argvargclà cách các đối số dòng lệnh được truyền vào main()trong C và C ++.

argcsẽ là số chuỗi được trỏ bởi argv. Điều này sẽ (trong thực tế) là 1 cộng với số lượng đối số, vì hầu như tất cả các cài đặt sẽ thêm tên của chương trình vào mảng.

Các biến được đặt tên argc( đếm đối số ) và argv( vectơ đối số ) theo quy ước, nhưng chúng có thể được cung cấp bất kỳ định danh hợp lệ nào: int main(int num_args, char** arg_strings)có giá trị như nhau.

Chúng cũng có thể được bỏ qua hoàn toàn, cho năng suất int main(), nếu bạn không có ý định xử lý các đối số dòng lệnh.

Hãy thử chương trình sau:

#include <iostream>

int main(int argc, char** argv) {
    std::cout << "Have " << argc << " arguments:" << std::endl;
    for (int i = 0; i < argc; ++i) {
        std::cout << argv[i] << std::endl;
    }
}

Chạy nó với ./test a1 b2 c3đầu ra sẽ

Have 4 arguments:
./test
a1
b2
c3

8
argccó thể là 0, trong trường hợp đó argvcó thể là NULL. Nó được cho phép bởi AFAIK tiêu chuẩn. Tôi chưa bao giờ nghe nói về một hệ thống thực hiện điều này trong thực tế, nhưng nó chắc chắn có thể tồn tại và sẽ không vi phạm bất kỳ tiêu chuẩn nào.
Chuck

77
@Chuck: Vì "Giá trị của argv[argc]sẽ là 0" (C ++ 03 §3.6.1 / 2), argvkhông thể là null.
James McNellis

20
@Chuck: C (ít nhất là C99) có cùng yêu cầu.
James McNellis

2
Tôi nghĩ rằng tôi nên thêm, điều này giống nhau ở hầu hết các hệ thống ngoài kia, mặc dù đôi khi chúng bị trừu tượng hóa. Chẳng hạn, trong Pascal / Delphi / Lazarus, bạn nhận được; ParamStr và ParamCount (nếu bộ nhớ phục vụ tôi đúng). Quan điểm của tôi là, khi bạn (nếu có) viết các ứng dụng gốc bằng các ngôn ngữ / ngôn ngữ khác, rất có thể phần trên được xác định để bạn sử dụng và chúng hoạt động hoàn toàn giống nhau (danh sách đếm / chuỗi) trong tất cả các hệ thống hỗ trợ họ
Christian

8
@ EmilVikström Không, đó là một lỗi nghiêm trọng có thể dẫn đến một segfault. *NULLchắc chắn không bằng NULL.
meagar

52

argclà số lượng đối số được truyền vào chương trình của bạn từ dòng lệnh và argvlà mảng đối số.

Bạn có thể lặp qua các đối số biết số lượng của chúng như:

for(int i = 0; i < argc; i++)
{
    // argv[i] is the argument at index i
}

19

Giả sử bạn chạy chương trình của mình như vậy (sử dụng shcú pháp):

myprog arg1 arg2 'arg 3'

Nếu bạn đã khai báo chính của bạn là int main(int argc, char *argv[]), thì (trong hầu hết các môi trường), bạn main()sẽ được gọi như thể:

p = { "myprog", "arg1", "arg2", "arg 3", NULL };
exit(main(4, p));

Tuy nhiên, nếu bạn khai báo chính là int main(), nó sẽ được gọi là

exit(main());

và bạn không nhận được các đối số được thông qua.

Hai điều bổ sung cần lưu ý:

  1. Đây là hai chữ ký duy nhất được ủy quyền cho main. Nếu một nền tảng cụ thể chấp nhận các đối số bổ sung hoặc loại trả về khác, thì đó là một tiện ích mở rộng và không nên dựa vào trong một chương trình di động.
  2. *argv[]**argvhoàn toàn tương đương, vì vậy bạn có thể viết int main(int argc, char *argv[])như int main(int argc, char **argv).

2
Nếu chúng tôi là kỹ thuật, basic.start.main/2rõ ràng cho phép các phiên bản bổ sung được xác định theo thực thi main(), miễn là việc triển khai cung cấp hai phiên bản được xác định trước. Vì vậy, chúng không chính xác là không phù hợp. Một trong những phổ biến nhất là envp, đó là rất nổi tiếng trong cả C và C ++ rằng đó là nghĩa đen entry đầu tiên trong phần J.5 (phần mở rộng thông thường) của tiêu chuẩn C .
Thời gian của Justin - Phục hồi lại

1
Cảm ơn vì nhà sư phạm tốt đẹp @Justin. Trả lời cập nhật để chính xác hơn.
Toby Speight

Không có ý tưởng - Tôi khuyên bạn nên tạo một ví dụ có thể lặp lại tối thiểu và hỏi nó (giả sử rằng quy trình đó không đủ để giúp bạn tự trả lời).
Toby Speight

9

Các tham số để mainbiểu diễn các tham số dòng lệnh được cung cấp cho chương trình khi nó được khởi động. Các argctham số đại diện cho số của đối số dòng lệnh, và char *argv[]là một mảng của chuỗi (con trỏ ký tự) đại diện cho các đối số cá nhân được cung cấp trên dòng lệnh.


2
Argv [] luôn có argv [arg] dưới dạng con trỏ null. và Argv [0] luôn là (đường dẫn đầy đủ) /
execableName

3
@ user3629249: Không nhất thiết; argv[0]là bất cứ điều gì chương trình khởi chạy chương trình C đã cho nó như argv[0]. Trong trường hợp của Bash, nó thường (có thể luôn luôn) tên đường dẫn của tệp thực thi, nhưng Bash không phải là chương trình duy nhất thực thi các chương trình khác. Nó được cho phép, mặc dù lập dị, để sử dụng : char *args[] = { "cat", "/dev/null", "/etc/passwd", 0 }; execv("/bin/ls", args);. Trên nhiều hệ thống, giá trị mà chương trình argv[0]sẽ thấy cat, mặc dù có thể thực thi được /bin/ls.
Jonathan Leffler

7

Các mainchức năng có thể có hai tham số, argcargv. argclà một tham số nguyên ( int) và nó là số lượng đối số được truyền cho chương trình.

Tên chương trình luôn là đối số đầu tiên, do đó sẽ có ít nhất một đối số cho một chương trình và giá trị tối thiểu argcsẽ là một đối số. Nhưng nếu một chương trình có hai đối số thì giá trị của argcsẽ là ba.

Tham số argvtrỏ đến một mảng chuỗi và được gọi là vectơ đối số . Nó là một chuỗi chuỗi một chiều của các đối số hàm.


5
int main();

Đây là một tuyên bố đơn giản. Nó không thể lấy bất kỳ đối số dòng lệnh.

int main(int argc, char* argv[]);

Tuyên bố này được sử dụng khi chương trình của bạn phải lấy các đối số dòng lệnh. Khi chạy như vậy:

myprogram arg1 arg2 arg3

argc, hoặc Số lượng đối số, sẽ được đặt thành 4 (bốn đối số) và argv, hoặc Đối số vectơ, sẽ được điền với các con trỏ chuỗi thành "myprogram", "arg1", "arg2" và "arg3". Lệnh gọi chương trình ( myprogram) được bao gồm trong các đối số!

Ngoài ra, bạn có thể sử dụng:

int main(int argc, char** argv);

Điều này cũng hợp lệ.

Có một tham số khác bạn có thể thêm:

int main (int argc, char *argv[], char *envp[])

Các envpthông số còn chứa các biến môi trường. Mỗi mục theo định dạng này:

VARIABLENAME=VariableValue

như thế này:

SHELL=/bin/bash    

Danh sách các biến môi trường được kết thúc bằng null.

QUAN TRỌNG: KHÔNG sử dụng bất kỳ argvhoặc envpgiá trị trực tiếp trong các cuộc gọi đến system()! Đây là một lỗ hổng bảo mật rất lớn vì người dùng độc hại có thể đặt các biến môi trường thành các lệnh dòng lệnh và (có khả năng) gây ra thiệt hại lớn. Nói chung, không sử dụng system(). Hầu như luôn luôn có một giải pháp tốt hơn được thực hiện thông qua các thư viện C.


3

Tham số đầu tiên là số lượng đối số được cung cấp và tham số thứ hai là danh sách các chuỗi đại diện cho các đối số đó.


7
mục đầu tiên trong argv [0] là tên chương trình, không phải là đối số
user3629249

@ user3629249 Tên chương trình với đường dẫn chương trình. ;)
Master James

1

Cả hai

int main(int argc, char *argv[]);
int main();

là các định nghĩa pháp lý về điểm vào cho chương trình C hoặc C ++. Stroustrup: Câu hỏi thường gặp về Phong cách và Kỹ thuật C ++ chi tiết một số biến thể có thể hoặc hợp pháp cho chức năng chính của bạn.


4
Có thể muốn đặt void vào ... int main()==> int main(void)... để tương thích và dễ đọc. Tôi không biết nếu tất cả các phiên bản cũ hơn của C cho phép các hàm void có danh sách tham số trống trong khai báo.
dylnmc

1
@dylnmc điều này không mang lại bất kỳ mức tăng khả năng đọc nào và hoàn toàn tương đương trong tất cả các phiên bản C ++. Chỉ trong C điều này không có sự khác biệt, nhưng chỉ trong khai báo, không phải trong định nghĩa.
Ruslan

@Ruslan Xin lỗi, tôi đã đăng bài này khi tôi mới học C, và tôi có thể đã đọc rằng trong các phiên bản đầu tiên của C voidthì bắt buộc phải có. Đừng trích dẫn tôi về điều đó, và bây giờ tôi biết đó là một nhận xét hơi ngu ngốc. Nó không thể làm tổn thương, mặc dù.
dylnmc

Nếu argc <3 trả về lỗi thì sao? Điều gì có thể xảy ra?
AVI
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.