기본 프로그램에 대한 표준 정의가 주어지면 :
int main(int argc, char *argv[]) {
...
}
argc
POSIX 시스템 에서 어떤 상황에서 0 이 될 수 있습니까?
기본 프로그램에 대한 표준 정의가 주어지면 :
int main(int argc, char *argv[]) {
...
}
argc
POSIX 시스템 에서 어떤 상황에서 0 이 될 수 있습니까?
if (argc < x) { fprintf(stderr, "Usage: %s ...", argv[0]); }
, 나는 확실히 볼 실제 :이 질문의 관련성
답변:
예, 가능합니다. 다음과 같이 프로그램을 호출하는 경우 :
execl("./myprog", NULL, (char *)NULL);
또는 또는 :
char *args[] = { NULL };
execv("./myprog", args);
그러면 "myprog"에서 argc
0이됩니다.
표준 은 또한 argc
호스팅 된 환경에서 프로그램 시작과 관련하여 섹션 5.1.2.2.1에 명시된대로 0 을 허용 합니다.
1 프로그램 시작시 호출되는 함수의 이름은
main
. 구현은이 함수에 대한 프로토 타입을 선언하지 않습니다. 반환 유형int
과 매개 변수없이 정의되어야합니다 .
int main(void) { /* ... */ }
또는 두 개의 매개 변수를 사용합니다 (여기에서
argc
및 로 지칭됩니다.argv
이름이 선언 된 함수에 로컬이기 때문에 모든 이름을 사용할 수 있음).
int main(int argc, char *argv[]) { /* ... */ }
또는 동등한 것; 또는 다른 구현 정의 방식으로.
2 선언 된 경우
main
함수에 대한 매개 변수 는 다음 제약 조건을 따라야합니다.
- 의 값은
argc
음수가 아니어야합니다.argv[argc]
널 포인터 여야합니다....
이는 argc
0이면 argv[0]
NULL이 보장 된다는 것을 의미 합니다. 지정자에 printf
대한 인수로 사용될 때 NULL 포인터를 처리하는 방법 은 %s
표준에 나와 있지 않습니다. 이 경우 많은 구현에서 "(null)"을 출력하지만 보장되지는 않습니다.
argc == 0
: OpenBSD.
exec
프로그램에 대한 인수없이 경로 만 사용하여 호출하면 OpenBSD가 무엇을 합니까? (실패? 0 번째 인수에 경로 복사?)
execve
오류와 함께 실패 EINVAL
빈과 당신이 경우 호출 argv
. 해당 조건에 대한 오류 동작은 맨 아래의 가능한 오류 목록에만 언급되어 있으므로 execve
매뉴얼 페이지 에서 놓치기 쉽습니다 .
예, 0이 될 수 있습니다 argv[0] == NULL
.
argv[0]
프로그램의 이름 인 규칙입니다 . 당신은 할 수 있습니다 argc == 0
당신과 같은 바이너리를 직접 실행하는 경우 가 execve 가족 및 인수를 포기하지 않습니다. 프로그램 이름에 가깝지 않은 문자열을 지정할 수도 있습니다. 그렇기 때문에를 사용 argv[0]
하여 프로그램 이름을 얻는 것이 전적으로 신뢰할 수있는 것은 아닙니다.
일반적으로 명령 줄을 입력하는 셸은 항상 프로그램 이름을 첫 번째 인수로 추가하지만 이는 규칙입니다. 경우 argv[0]
사용 == "--help"와 getopt는을 구문 분석 옵션 때문에 당신은 그것을 감지하지 않습니다 optind
1로 초기화되지만 설정할 수 있습니다 optind
0, 사용을 getopt
표시하고 "도움말"긴 옵션을 선택합니다.
long story short : argc == 0
( argv[0]
그 자체로는 특별하지 않습니다) 가질 수 있습니다 . 런처가 전혀 주장하지 않을 때 발생합니다.
초기 제안에서는 main ()에 전달 된 argc의 값이 "하나 이상"이어야했습니다. 이것은 ISO C 표준 초안의 동일한 요구 사항에 의해 주도되었습니다. 실제로 exec 함수의 호출자에게 인수가 제공되지 않은 경우 히스토리 구현은 0 값을 전달했습니다. 이 요구 사항은 ISO C 표준에서 제거되었으며 이후 POSIX.1-2017의이 볼륨에서도 제거되었습니다. 단어, 특히 단어의 사용은 Strictly Conforming POSIX 응용 프로그램이 exec 함수에 최소한 하나의 인수를 전달하도록 요구하므로 해당 응용 프로그램에서 호출 할 때 argc가 하나 이상임을 보장합니다. 실제로 많은 기존 응용 프로그램이 argc 값을 먼저 확인하지 않고 argv [0]을 참조하므로 이는 좋은 방법입니다.
Strictly Conforming POSIX 응용 프로그램에 대한 요구 사항은 또한 첫 번째 인수로 전달 된 값이 시작되는 프로세스와 관련된 파일 이름 문자열임을 명시합니다. 일부 기존 응용 프로그램은 일부 상황에서 파일 이름 문자열이 아닌 경로 이름을 전달하지만 argv [0]의 일반적인 사용이 진단 인쇄에 사용되기 때문에 파일 이름 문자열이 더 일반적으로 유용합니다. 어떤 경우에는 전달 된 파일 이름이 파일의 실제 파일 이름이 아닙니다. 예를 들어, 로그인 유틸리티의 많은 구현은 실제 파일 이름에 ( '-') 접두사를 붙이는 규칙을 사용합니다. 이것은 호출되는 명령 인터프리터에게 "로그인 쉘"임을 나타냅니다.
또한 test 및 [유틸리티는 모든 구현에서 결정적 동작을 갖기 위해 argv [0] 인수에 대한 특정 문자열이 필요합니다.
POSIX 시스템에서 argc가 0이 될 수 있습니까?
예,하지만 POSIX를 엄격하게 준수하지는 않습니다.
char *nothing = { 0 }; execve(*prog, nothing, nothing)
시스템에서 실행될 수있는 프로그램의 단순한 존재 (또는 실행) 가 POSIX가 해당 시스템을 "엄격하게 준수하지 않음"으로 선언 하도록 논리적으로 암시합니다 . 이것은 POSIX 표준이 의도 한 바가 아닐 것 같습니까?
execve
시스템 호출로 우리 프로그램을 호출하는 작은 프로그램을 작성합니다. 하지만 그 개발자는 작은 실수를하고 아무런 인수없이 우리를 호출합니다. 시스템 전체가 더 이상 "POSIX를 엄격하게 준수"하지 않는다고 말하는 것이 타당 합니까?
요약 : 예, argv[0]
NULL이 될 수 있지만 내가 아는 좋은 / 정상적인 이유가 아닙니다. 그러나 argv[0]
NULL 인지 신경 쓰지 않는 이유가 있고, 만약 그렇다면 프로세스가 충돌하도록 특별히 허용 해야하는 이유 가 있습니다.
예, argv[0]
인수없이 실행 된 경우에만 POSIX 시스템에서 NULL이 될 수 있습니다.
더 흥미로운 실제 질문은 프로그램이 관심을 가져야한다는 것 입니다.
이에 대한 대답은 "아니요, 프로그램은 argv[0]
NULL이 아니라고 가정 할 수 있습니다 "입니다 . 일부 시스템 유틸리티 (명령 줄 유틸리티)가 작동하지 않거나 비 결정적 방식으로 작동하기 argv[0] == NULL
때문입니다. (어리 석거나 사악한 목적이 아닌) 프로세스가 왜 그렇게하는지 이유. (의 표준 사용법 getopt()
도 실패 하는지 확실하지 않지만 작동 할 것으로 예상하지 않습니다.)
많은 코드와 실제로 내가 작성한 대부분의 예제 및 유틸리티는 다음과 같은 것으로 시작합니다.
int main(int argc, char *argv[])
{
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
printf("Usage: %s [ -h | --help ]\n", argv[0]);
/* ... print usage ... */
return EXIT_SUCCESS;
}
이는 합리적 , 즉 실행되고있는 적어도 명령 경로를 제공하지 않고 다른 프로세스 Exec을하는 프로세스에 대한 더 좋은 이유가 없기 때문에 수용 가능한 execlp(cmd, cmd, NULL)
것이 아니라이 execlp(cmd, NULL)
.
(그러나 파이프 또는 소켓 명령과 관련된 타이밍 경쟁 창을 악용하는 것과 같은 몇 가지 사악한 이유를 생각할 수 있습니다. 악한 프로세스는 설정된 Unix 도메인 소켓을 통해 악의적 인 요청을 보낸 다음 즉시 승인 된 희생자 명령으로 교체됩니다 (실행 최소한의 시작 시간을 보장하기 위해 인수없이) 요청을받는 서비스가 피어 자격 증명을 확인할 때 원래의 악의적 인 프로세스 대신 희생자 명령을 볼 수 있도록합니다. SIGSEGV
악의적 인 프로세스에 더 큰 시간 창을 제공하는 "멋지게"동작하는 대신 강력 하고 빠르게 충돌하는 명령 ( NULL 포인터를 역 참조하여)
그 동안 즉, 가능한 프로세스가 서로하지만 원인이 인수없이 자체를 교체하는 것이 argc
제로로, 그런 행동은 그렇게 할 알려진 비 사악한 이유가 없다는 엄격한 의미에서 불합리하다.
이 때문에 그리고 내가 사악하고 무관심한 프로그래머와 그들의 프로그램을 위해 삶을 힘들게 만드는 것을 좋아한다는 사실 때문에 나는 개인적으로 다음과 같은 사소한 수표를 추가하지 않을 것입니다.
static int usage(const char *argv0)
{
/* Print usage using argv0 as if it was argv[0] */
return EXIT_SUCCESS;
}
int main(int argc, char *argv[])
{
if (argc < 1)
return usage("(this)");
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
return usage(argv[0]);
/* argv[0] and argv[1] are non-NULL, argc >= 2 */
특정 기존 사용 사례를 염두에두고있는 사람이 요청한 경우는 예외입니다. 그리고 나서도 조금 의심스러워서 먼저 사용 사례를 직접 확인하고 싶습니다.
write()
-1이 아닌 음수 값을 반환하는 (저수준 C 함수, Linux의 syscall 래퍼)입니다. 단순히 발생해서는 안되므로 대부분의 프로그래머는이를 테스트하지 않습니다. 그러나 Linux에서 커널 파일 시스템 버그로 인해 2GiB 이상의 쓰기가 발생했습니다. (현재 쓰기는 다른 파일 시스템 드라이버에서 유사한 버그를 피하기 위해 syscall 수준에서 2GiB 미만으로 제한됩니다.)이 경우를 확인하는 코드는 제가 본 유일한 코드입니다. (나는 그것을 EIO
오류 로 취급한다 .) 그러나 내가 말했듯이, 나는 argc == 0
내 코드에서 확인하지 않기로 결정했다 .
argv[0]
때 argc == 0
? 그것은 다시 정의되지 않은 동작을 의미하는 널 포인터를 역 참조하지 않습니까? 코드가 실행될 모든 C 구현이 합리적인 작업을 수행 할 것이라고 확신하십니까?
int execv(const char *path, char *const argv[]);
하여 시도하겠습니다 .)argv
NULL