일단 리눅스에서 메인 프로세스 함수 스택의 내용에 관심을 갖게 되었습니다. 나는 약간의 조사를했고 이제 당신에게 그 결과를 제시합니다.

주요 기능을 설명하는 옵션:
1. 정수 메인()
2. int main(int argc, char **argv)
3. int main(int argc, char **argv, char **env)
4. int main(int argc, char **argv, char **env, ElfW(auxv_t) auxv)
5. int main(int argc, char **argv, char **env, char **apple)

Argc - 매개변수의 수
argv - 명령행 옵션의 문자열에 대한 포인터의 널 터미널 배열
env는 환경 변수 문자열에 대한 포인터의 널 터미널 배열입니다. NAME=VALUE 형식의 각 줄
auxv - 보조 값 배열(PowerPC에서만 사용 가능)
apple - 실행 파일의 경로(MacOS 및 Darwin에서)
보조 벡터 - 다른 배열 추가 정보, 유효 사용자 ID, setuid 비트 플래그, 메모리 페이지 크기 등과 같은.

스택 세그먼트의 크기는 맵 파일에서 볼 수 있습니다.
고양이 /proc/10918/maps

7ffffffa3000-7ffffffff000 rw-p 00000000 00:00 0

로더는 제어를 메인으로 넘기기 전에 명령줄 매개변수, 환경 변수, 보조 벡터의 배열 내용을 초기화합니다.
초기화 후 스택의 맨 위는 64비트 버전의 경우 다음과 같습니다.
상단에 수석 주소.

1. 0x7ffffffff000 스택 세그먼트의 맨 위 지점입니다. 호출로 인해 segfault가 발생합니다.
0x7ffffffff0f8 없는 무효의* 8 0x00"
2. 파일 이름 1+ "/tmp/a.out"
1 0x00
...
환경 1 0x00
...
1 0x00
3. 0x7fffffffe5e0 환경 1 ..
1 0x00
...
argv 1 0x00
...
1 0x00
4. 0x7fffffffe5be argv 1+ "/tmp/a.out"
5. 임의 길이의 배열
6. auxv용 데이터 무효의* 48"
AT_NULL Elf64_auxv_t 16 {0,0}
...
보조 Elf64_auxv_t 16
7. 보조 Elf64_auxv_t 16 예: (0x0e,0x3e8)
없는 무효의* 8 0x00
...
환경 숯* 8
8. 0x7fffffffe308 환경 숯* 8 0x7fffffffe5e0
없는 무효의* 8 0x00
...
argv 숯* 8
9. 0x7fffffffe2f8 argv 숯* 8 0x7fffffffe5be
10. 0x7fffffffe2f0 인수 긴 정수 8" 인수 수 + 1
11. 지역 변수 및 인수, main 전에 호출되는 함수
12. 지역 변수 메인
13. 0x7fffffffe1fc 인수 정수 4 인수 수 + 1
0x7fffffffe1f0 argv 숯** 8 0x7fffffffe2f8
0x7fffffffe1e8 환경 숯** 8 0x7fffffffe308
14. 변수 로컬 기능

"- 문서에서 필드에 대한 설명을 찾지 못했지만 덤프에서 명확하게 볼 수 있습니다.

32비트의 경우 확인하지 않았지만 크기를 2로 나누는 것만으로도 충분할 것입니다.

1. 최상위 지점 위의 주소에 액세스하면 Segfault가 발생합니다.
2. 실행 파일의 경로를 포함하는 문자열.
3. 환경 변수가 있는 문자열 배열
4. 명령줄 옵션이 있는 문자열 배열
5. 임의 길이의 배열. 명령으로 선택을 끌 수 있습니다.
sysctl -w kernel.randomize_va_space=0
에코 0 > /proc/sys/kernel/randomize_va_space
6. 보조 벡터에 대한 데이터(예: 문자열 "x86_64")
7. 보조 벡터. 자세한 내용은 아래를 참조하세요.
8. 환경 변수 문자열에 대한 포인터의 널 터미널 배열
9. 명령줄 매개변수 문자열에 대한 포인터의 널 터미널 배열
10. 명령줄 매개변수의 수를 포함하는 기계어("상위" 함수의 인수 중 하나, 항목 11 참조)
11. main(_start,__libc_start_main..) 이전에 호출된 함수의 지역 변수 및 인수
12. 메인에 선언된 변수
13. 주요 기능 인수
14. 지역 함수의 변수와 인수.

보조 벡터
i386 및 x86_64의 경우 보조 벡터의 첫 번째 요소 주소를 가져올 수 없지만 이 벡터의 내용은 다른 방법으로 얻을 수 있습니다. 그 중 하나는 환경 변수 문자열에 대한 포인터 배열 직후에 메모리 영역에 액세스하는 것입니다.
다음과 같이 표시되어야 합니다.
#포함 #포함 int main(int argc, char** argv, char** env)( Elf64_auxv_t *auxv; //x86_64 // Elf32_auxv_t *auxv; //i386 while(*env++ != NULL); //보조의 시작 부분 찾기 벡터 for ( auxv = (Elf64_auxv_t *)env; auxv->a_type != AT_NULL; auxv++)( printf("addr: %p 유형: %lx is: 0x%lx\n", auxv, auxv->a_type, auxv ->a_un .a_val); ) printf("\n (void*)(*argv) - (void*)auxv= %p - %p = %ld\n (void*)(argv)-(void*) (&auxv) =%p-%p = %ld\n ", (void*)(*argv), (void*)auxv, (void*)(*argv) - (void*)auxv, (void*) (argv) , (void*)(&auxv), (void*)(argv) - (void*)(&auxv)); printf("\n 인수 복사: %d\n",*((int *)( argv - 1 )), 0을 반환, )
Elf(32,64)_auxv_t 구조는 /usr/include/elf.h에 설명되어 있습니다. linux-kernel/fs/binfmt_elf.c에서 구조를 채우는 함수

벡터의 내용을 가져오는 두 번째 방법:
hexdump /proc/self/auxv

가장 읽기 쉬운 표현은 다음을 설정하여 얻을 수 있습니다. 환경 변수 LD_SHOW_AUXV.

LD_SHOW_AUXV=1ls
AT_HWCAP: bfebfbff // 프로세서 기능
AT_PAGESZ: 4096 //메모리 페이지 크기
AT_CLKTCK: 100 //업데이트 빈도 시간()
AT_PHDR: 0x400040 //헤더 정보
AT_PHENT: 56
AT_PHNUM: 9
AT_BASE: 0x7fd00b5bc000 // 인터프리터의 주소, 즉 ld.so
AT_FLAGS: 0x0
AT_ENTRY: 0x402490 //프로그램 진입점
AT_UID: 1000 //사용자 및 그룹 ID
AT_EUID: 1000 //명목 및 유효
AT_GID: 1000
AT_EGID: 1000
AT_SECURE: 0 // setuid 플래그 세트입니다.
AT_RANDOM: 0x7fff30bdc809 // 16개의 임의 바이트 주소,
시작 시 생성
AT_SYSINFO_EHDR: 0x7fff30bff000 //사용된 페이지에 대한 포인터
//시스템 호출
AT_EXECFN: /bin/ls
AT_PLATFORM: x86_64
왼쪽은 변수의 이름이고 오른쪽은 값입니다. 가능한 모든 변수 이름과 설명은 elf.h 파일에서 찾을 수 있습니다. (AT_ 접두사가 붙은 상수)

main()에서 반환
프로세스 컨텍스트가 초기화된 후 제어는 main()이 아니라 _start() 함수로 이전됩니다.
main()은 이미 __libc_start_main에서 호출합니다. 이것 마지막 기능흥미로운 기능이 있습니다 - main() 이후에 실행될 함수에 대한 포인터가 전달됩니다. 그리고 이 포인터는 스택을 통해 자연스럽게 전달됩니다.
일반적으로 __libc_start_main 인수는 glibc-2.11/sysdeps/ia64/elf/start.S 파일에 따라 형식을 갖습니다.
/*
* __libc_start_main에 대한 인수:
*out0:메인
*out1: 인수
*out2: argv
* out3: 초기화
* out4:fini //메인 다음에 호출되는 함수
* out5: rtld_fini
*out6: stack_end
*/
저것들. Fini 포인터의 주소를 얻으려면 마지막 지역 변수 main에서 두 개의 기계어를 이동해야 합니다.
다음은 발생한 일입니다(작동성은 컴파일러 버전에 따라 다름).
#포함 무효 **ret; 무효 *떠나다; void foo()( void (*boo)(void); //함수 포인터 printf("Stack rewrite!\n"); boo = (void (*)(void))leave; boo(); // fini ( ) ) int main(int argc, char *argv, char *envp) ( unsigned long int mark = 0xbfbfbfbfbfbfbfbf; // ret에서 작동하도록 표시 = (void**)(&mark+2); // 주소 추출, 함수 완료 후 호출(fini) leave = *ret; // store *ret = (void*)foo; // 덮어쓰기 return 0; // foo() 호출)

나는 그것이 흥미로웠기를 바랍니다.
행운을 빕니다.

유용한 팁을 제공한 사용자 Xeor에게 감사드립니다.

C++ 프로그래밍 언어로 콘솔 응용 프로그램을 만들 때 다음과 매우 유사한 줄이 자동으로 생성됩니다.

int main(int argc, char* argv) // main() 함수 매개변수

이 줄은 헤더입니다. 주요 기능 main() , 매개변수 argс 및 argv는 대괄호 안에 선언됩니다. 따라서 프로그램을 실행하면 명령줄, 그러면 이 프로그램에 일부 정보를 전달할 수 있습니다. 이를 위해 매개변수 argc 및 argv 가 있습니다. argc 매개변수는 데이터 유형이 int 이며 전달된 매개변수의 수를 포함합니다. 주요 기능. 더욱이, argc는 함수의 이름이 첫 번째 매개변수로 간주되기 때문에 정보를 전달하지 않더라도 항상 최소 1입니다. argv 매개변수는 문자열에 대한 포인터의 배열입니다. 문자열 유형 데이터만 명령줄을 통해 전달할 수 있습니다. 포인터와 문자열은 별도의 섹션이 생성된 두 가지 큰 주제입니다. 따라서 모든 정보가 전송되는 것은 argv 매개변수를 통해서입니다. 명령줄을 통해 실행할 프로그램을 개발해 보겠습니다. 윈도우 라인, 정보를 전달합니다.

// argc_argv.cpp: 콘솔 애플리케이션의 진입점을 지정합니다. #include "stdafx.h" #include 네임스페이스 std 사용 int main(int argc, char* argv) ( if (argc ><< argv<

// 코드 코드::블록

// Dev-C++ 코드

// argc_argv.cpp: 콘솔 애플리케이션의 진입점을 지정합니다. #포함 네임스페이스 std 사용 int main(int argc, char* argv) ( if (argc > 1)// 인수를 전달하면 argc는 1보다 클 것입니다(인수 수에 따라 다름) ( cout<< argv<

프로그램을 디버깅한 후 Windows 명령줄을 열고 프로그램 실행 파일을 명령줄 창으로 드래그하면 프로그램의 전체 경로가 명령줄에 표시됩니다(하지만 프로그램 경로를 수동으로 쓸 수 있음). 누를 수 있는 입력하다프로그램이 시작됩니다(그림 1 참조).

그림 1 - 주요 기능의 매개변수

방금 프로그램을 실행하고 인수를 전달하지 않았기 때문에 Not arguments 메시지가 나타납니다. 그림 2는 명령줄을 통해 동일한 프로그램을 실행하지만 Open 인수가 전달된 것을 보여줍니다.

그림 2 - 주 함수의 매개변수

인수는 Open 이라는 단어인데, 그림에서 알 수 있듯이 이 단어가 화면에 나타납니다. 여러 매개변수를 쉼표로 구분하여 한 번에 전달할 수 있습니다. 여러 단어로 구성된 매개변수를 전달해야 하는 경우 큰따옴표로 묶어야 하며 이러한 단어는 하나의 매개변수로 간주됩니다. 예를 들어, 그림은 프로그램의 시작을 보여주고 두 단어로 구성된 인수를 전달합니다 - It work .

그림 3 - 주요 기능의 매개변수

그리고 따옴표를 제거하면. 그러면 It이라는 단어만 보게 될 것입니다. 프로그램을 시작할 때 정보를 전달할 계획이 없으면 main() 함수에서 인수를 제거하고 이러한 인수의 이름을 변경할 수도 있습니다. 때때로 argc 및 argv 매개변수가 수정되지만 모두 생성되는 애플리케이션 유형 또는 개발 환경에 따라 다릅니다.

선택적 인수 및 명명된 인수

선택적 인수

C# 4.0에는 메서드를 호출할 때 인수를 지정하는 편의성을 향상시키는 새로운 기능이 도입되었습니다. 이 도구는 선택적 인수메소드 매개변수의 기본값을 정의할 수 있습니다. 메서드가 호출될 때 매개변수에 해당 인수가 지정되지 않은 경우 이 값이 기본적으로 사용됩니다. 따라서 이러한 매개변수에 대한 인수를 지정할 필요가 없습니다. 선택적 인수를 사용하면 기본 인수가 일부 매개변수에 적용되는 메서드를 더 쉽게 호출할 수 있습니다. 메서드 오버로딩의 "짧은" 형식으로도 사용할 수 있습니다.

선택적 인수를 추가하는 주된 동기는 COM 개체와의 상호 작용을 단순화해야 한다는 것이었습니다. 여러 Microsoft 개체 모델(예: Microsoft Office)에서 기능은 COM 개체를 통해 제공되며, 그 중 많은 개체는 오래 전에 작성되었으며 선택적 매개 변수를 사용하도록 설계되었습니다.

선택적 인수를 사용하는 예는 다음과 같습니다.

시스템 사용; System.Collections.Generic 사용; System.Linq를 사용하여; System.Text 사용; namespace ConsoleApplication1 ( class Program ( // static int mySum(int a, int b = 5, int c = 10)을 호출할 때 인수 b와 c는 선택 사항임) ( return a + b + c; ) static void Main() ( int sum1 = mySum(3); int sum2 = mySum(3,12); Console.WriteLine("Sum1 = "+sum1); Console.WriteLine("Sum2 = "+sum2); Console.ReadLine(); ) ) )

모든 선택적 인수는 필수 인수의 오른쪽에 반드시 표시되어야 함을 명심해야 합니다. 메서드 외에도 생성자, 인덱서 및 대리자에서 선택적 인수를 사용할 수 있습니다.

선택적 인수의 장점 중 하나는 프로그래머가 복잡한 메서드 및 생성자 호출을 보다 쉽게 ​​처리할 수 있다는 것입니다. 결국, 일반적으로 필요한 것보다 더 많은 매개변수를 메소드에 설정해야 하는 경우가 종종 있습니다. 그리고 이와 같은 경우에는 선택적 인수를 신중하게 사용하여 이러한 매개변수 중 일부를 선택적으로 만들 수 있습니다. 즉, 이 특정 경우에 중요한 인수만 전달되어야 하며 그렇지 않으면 필요해야 하는 모든 인수가 아닙니다. 이 접근 방식을 통해 메서드를 합리화하고 프로그래머의 처리를 단순화할 수 있습니다.

명명된 인수

.NET 4.0 릴리스와 함께 C#에 추가된 또 다른 기능은 명명된 인수. 아시다시피, 메서드에 인수를 전달할 때 표시되는 순서는 일반적으로 메서드 자체에서 매개변수가 정의된 순서와 일치해야 합니다. 즉, 인수 값은 인수 목록에서 해당 위치에 따라 매개변수에 할당됩니다.

명명된 인수는 이러한 제한을 극복하기 위해 설계되었습니다. 명명된 인수를 사용하면 해당 값이 할당되는 매개변수의 이름을 지정할 수 있습니다. 그리고 이 경우 인수의 순서는 더 이상 중요하지 않습니다. 따라서 명명된 인수는 구문이 다르지만 앞에서 언급한 개체 이니셜라이저와 다소 유사합니다. 이름으로 인수를 지정하려면 다음 형식의 구문을 사용하십시오.

parameter_name: 값

여기 매개변수_이름값이 전달되는 매개변수의 이름을 나타냅니다. 물론, parameter_name은 호출되는 메소드에 대한 유효한 매개변수의 이름이어야 합니다.

특정 인수를 C 프로그램에 전달할 수 있습니다. 계산 시작 시 main()이 호출되면 세 개의 매개변수가 여기에 전달됩니다. 첫 번째는 프로그램에 액세스할 때 명령 인수의 수를 결정합니다. 두 번째 것은 이러한 인수를 포함하는 문자열에 대한 포인터 배열입니다(문자열당 하나의 인수). 세 번째는 또한 문자열에 대한 포인터 배열이며 운영 체제 매개변수(환경 변수)에 액세스하는 데 사용됩니다.

이러한 행은 다음과 같이 표시됩니다.

변수 = 값\0

마지막 줄은 두 개의 후행 0으로 찾을 수 있습니다.

main() 함수 인수의 이름을 각각 argc, argv 및 env로 지정합시다(다른 이름도 가능). 그런 다음 다음 설명이 허용됩니다.

메인(int argc, char *argv)

메인(int argc, char *argv, char *env)

A: 드라이브에 prog.exe 프로그램이 있다고 가정합니다. 다음과 같이 처리해 보겠습니다.

A:\>prog.exe 파일1 파일2 파일3

그런 다음 argv는 문자열 A:\prog.exe에 대한 포인터이고 argv는 문자열 file1에 대한 포인터입니다. 첫 번째 실제 인수는 argv가 가리키고 마지막 인수는 argv로 가리킵니다. argc=1이면 명령줄에서 프로그램 이름 뒤에 매개변수가 없습니다. 이 예에서는 argc=4입니다.

재귀

재귀는 함수가 자신을 호출하는 호출 방법입니다.

재귀 프로그램을 컴파일할 때 중요한 점은 출구 구성입니다. 함수가 지속적으로 자신을 무한정 호출한다는 실수를 하기 쉽습니다. 따라서 재귀 프로세스는 단계적으로 문제를 단순화하여 결국에는 비재귀적 솔루션이 나타나도록 해야 합니다. 재귀를 사용하면 스택 오버플로가 발생할 수 있으므로 항상 바람직한 것은 아닙니다.

라이브러리 기능

프로그래밍 시스템에서 일반적인 문제를 해결하기 위한 서브루틴은 라이브러리로 결합됩니다. 이러한 작업에는 수학 함수 계산, 데이터 입력/출력, 문자열 처리, 운영 체제 도구와의 상호 작용 등이 포함됩니다. 라이브러리 서브루틴을 사용하면 적절한 도구를 개발할 필요가 없고 사용자에게 추가 서비스를 제공합니다. 라이브러리에 포함된 기능은 프로그래밍 시스템과 함께 제공됩니다. 그들의 선언은 *.h 파일에 제공됩니다(이들은 소위 포함 또는 헤더 파일입니다). 따라서 위에서 언급했듯이 라이브러리 함수가 있는 프로그램의 시작 부분에는 다음과 같은 줄이 있어야 합니다.

#포함<включаемый_файл_типа_h>

예를 들어:

#포함

사용자 프로그램으로 새 라이브러리를 확장하고 생성하는 기능도 있습니다.

전역 변수는 프로그램 기간 동안 메모리에서 고정된 위치에 할당됩니다. 지역 변수는 스택에 저장됩니다. 그들 사이에는 동적 할당을 위한 메모리 영역이 있습니다.

malloc() 및 free() 함수는 여유 메모리를 동적으로 할당하는 데 사용됩니다. malloc() 함수는 메모리를 할당하고 free() 함수는 메모리를 해제합니다. 이러한 함수의 프로토타입은 stdlib.h 헤더 파일에 저장되며 다음과 같습니다.

무효 *malloc(size_t 크기);

무효 *자유(무효 *p);

malloc() 함수는 void 유형의 포인터를 반환합니다. 적절한 사용을 위해 함수 값은 적절한 유형에 대한 포인터로 변환되어야 합니다. 성공하면 함수는 크기 크기의 여유 메모리의 첫 번째 바이트에 대한 포인터를 반환합니다. 메모리가 충분하지 않으면 0 값을 반환하고 sizeof() 연산을 사용하여 변수에 필요한 바이트 수를 결정합니다.

이러한 기능을 사용하는 예:

#포함

#포함

p = (int *) malloc(100 * sizeof(int)); /* 100에 대한 메모리 할당

정수 */

printf("메모리 부족\n");

(나는 = 0; 나는< 100; ++i) *(p+i) = i; /* Использование памяти */

(나는 = 0; 나는< 100; ++i) printf("%d", *(p++));

무료(p); /* 여유 메모리 */

malloc()에 의해 반환된 포인터를 사용하기 전에 메모리가 충분한지 확인해야 합니다(포인터가 null이 아님).

전처리기

C 전처리기는 컴파일러에 대한 입력을 처리하는 프로그램입니다. 전처리기는 소스 프로그램을 보고 주어진 파일을 여기에 연결하고 대체를 수행하며 컴파일 조건을 관리하는 작업을 수행합니다. 전처리기는 # 기호로 시작하는 프로그램 행을 위한 것입니다. 한 줄에 하나의 명령(전처리기 지시문)만 허용됩니다.

지령

#define 식별자 대체

다음 프로그램 텍스트가 명명된 식별자를 대체 텍스트로 바꾸도록 합니다(이 명령 끝에 세미콜론이 없음에 유의). 기본적으로 이 지시문은 매크로 정의(매크로)를 소개합니다. 여기서 "식별자"는 매크로 정의의 이름이고 "대체"는 전처리기가 프로그램 텍스트에서 지정한 이름을 찾을 때 대체하는 문자 시퀀스입니다. 매크로 이름은 일반적으로 대문자로 표시됩니다.

예를 고려하십시오.

첫 번째 줄은 프로그램이 식별자 MAX를 상수 25로 바꾸도록 합니다. 두 번째 줄을 사용하면 여는 중괄호(()라는 단어 대신 텍스트에서 BEGIN이라는 단어를 사용할 수 있습니다.

전처리기는 매크로 정의의 기호 이름과 사용되는 컨텍스트 간의 호환성을 확인하지 않으므로 이러한 식별자를 #define 지시문이 아닌 명시적 유형 표시가 있는 const 키워드로 정의하는 것이 좋습니다. (C ++의 경우 더 그렇습니다):

상수 정수 MAX = 25;

(int 유형은 기본적으로 설정되어 있으므로 생략할 수 있습니다.)

#define 지시문이 다음과 같은 경우:

#define 식별자(식별자, ..., 식별자) ​​대체

첫 번째 식별자와 여는 괄호 사이에 공백이 없으면 인수가 있는 매크로 대체 정의입니다. 예를 들어 다음과 같은 줄이 나타난 후:

#define READ(val) scanf("%d", &val)

문 READ(y); scanf("%d",&y);와 동일하게 처리됩니다. 여기서 val은 인수이고 매크로 대체는 인수로 수행됩니다.

다음 줄에 계속되는 대체에 긴 정의가 있는 경우 \ 문자는 다음 연속 줄 끝에 배치됩니다.

매크로 정의에서 ## 문자로 구분된 개체를 배치할 수 있습니다. 예를 들면 다음과 같습니다.

#PR(x, y) x##y 정의

그 후, PR(a, 3)은 치환을 a3이라고 부를 것입니다. 또는 예를 들어 매크로 정의

#define z(a, b, c, d) a(b##c##d)

z(sin, x, +, y)를 sin(x+y)로 변경합니다.

매크로 인수 앞에 있는 # 문자는 문자열로 변환되었음을 나타냅니다. 예를 들어 지시문 뒤에

#define PRIM(var) printf(#var"= %d", var)

프로그램 텍스트의 다음 조각

다음과 같이 변환됩니다.

printf("연도""= %d", 연도);

다른 전처리기 지시문에 대해 설명하겠습니다. #include 지시문은 전에 본 적이 있습니다. 두 가지 형태로 사용할 수 있습니다.

#include "파일 이름"

#포함<имя файла>

두 명령의 효과는 지정된 이름의 파일을 프로그램에 포함시키는 것입니다. 첫 번째는 현재 디렉토리 또는 접두사로 지정된 디렉토리에서 파일을 로드합니다. 두 번째 명령은 프로그래밍 시스템에 정의된 표준 위치에서 파일을 검색합니다. 이름이 큰따옴표로 묶인 파일이 지정된 디렉토리에 없으면 #include 명령에 지정된 하위 디렉토리에서 검색이 계속됩니다.<...>. #include 지시문은 서로 중첩될 수 있습니다.

다음 지시문 그룹을 사용하면 프로그램의 일부를 선택적으로 컴파일할 수 있습니다. 이 프로세스를 조건부 컴파일이라고 합니다. 이 그룹에는 #if, #else, #elif, #endif, #ifdef, #ifndef 지시문이 포함됩니다. #if 지시문의 기본 형식은 다음과 같습니다.

#if constant_expression statement_sequence

여기서 상수 표현식의 값이 확인됩니다. 참이면 주어진 명령문 시퀀스를 실행하고 거짓이면 이 명령문 시퀀스를 건너뜁니다.

#else 지시문의 동작은 C 언어의 else 명령의 동작과 유사합니다. 예를 들면 다음과 같습니다.

# 상수 표현식인 경우

문_시퀀스_2

여기서 상수 표현식이 참이면 sequence_of_operators_1이 실행되고 거짓이면 sequence_of_operators_2가 실행된다.

#elif 지시문은 "else if" 유형 작업을 의미합니다. 주요 사용 형태는 다음과 같습니다.

# 상수 표현식인 경우

statement_sequence

#elif 상수 표현식_1

문_시퀀스_1

#elif 상수 표현식_n

sequence_of_statements_n

이 형식은 다음 형식의 C 언어 구성과 유사합니다. if...else if...else if...

지령

#ifdef 식별자

지정된 식별자가 현재 정의되어 있는지 여부를 설정합니다. #define 형식의 지시문에 포함되었는지 여부. 뷰 라인

#ifndef 식별자

지정된 식별자가 현재 정의되지 않았는지 확인합니다. 이러한 지시문 뒤에는 #else 문(#elif는 사용할 수 없음)을 포함하고 #endif 줄로 끝나는 임의의 수의 텍스트 행이 올 수 있습니다. 검사 중인 조건이 참이면 #else와 #endif 사이의 모든 행이 무시되고, 거짓이면 검사와 #else 사이의 행이 무시됩니다(#else 단어가 없으면 #endif). #if 및 #ifndef 지시문은 서로 중첩될 수 있습니다.

지시문 보기

#undef 식별자

지정된 식별자가 정의되지 않은 것으로 간주되도록 합니다. 교체 불가.

예를 고려하십시오. 세 가지 지시문은 다음과 같습니다.

식별자 WRITE가 정의되었는지(즉, #define WRITE... 형식의 명령인지) 확인하고, 그렇다면 WRITE 이름은 정의되지 않은 것으로 간주됩니다. 교체 불가.

지시

#define 쓰기 fprintf

WRITE 식별자가 정의되지 않았는지 확인하고, 정의되지 않은 경우 fprintf 이름 대신 WRITE 식별자가 결정됩니다.

#error 지시문은 다음 형식으로 작성됩니다.

#오류 error_message

프로그램 텍스트에서 발생하면 컴파일이 중지되고 디스플레이 화면에 오류 메시지가 표시됩니다. 이 명령은 주로 디버그 단계에서 사용됩니다. 오류 메시지는 큰따옴표로 묶을 필요가 없습니다.

#line 지시문은 C 프로그래밍 시스템에 정의된 _LINE_ 및 _FILE_ 변수의 값을 변경하기 위한 것입니다. _LINE_ 변수에는 현재 실행 중인 프로그램의 줄 번호가 포함됩니다. _FILE_ 식별자는 컴파일 중인 프로그램 이름이 있는 문자열에 대한 포인터입니다. #line 지시문은 다음과 같이 작성됩니다.

#줄 번호 "파일 이름"

여기서 number는 _LINE_ 변수에 할당될 양의 정수이고, filename은 _FILE_ 값을 재정의하는 선택적 매개변수입니다.

#pragma 지시문을 사용하면 일부 명령을 컴파일러에 전달할 수 있습니다. 예를 들어, 라인

C 프로그램에 어셈블리 언어 문자열이 있음을 나타냅니다. 예를 들어:

일부 전역 식별자 또는 매크로 이름(매크로 정의의 이름)을 고려하십시오. 5개의 이러한 이름이 정의됩니다: _LINE_, _FILE_, _DATE_, _TIME_, _STDC_. 그 중 두 개(_LINE_ 및 _FILE_)는 이미 위에서 설명했습니다. _DATE_ 식별자는 소스 파일이 개체 코드로 변환된 날짜를 저장하는 문자열을 지정합니다. _TIME_ 식별자는 소스 파일이 개체 코드로 변환된 시간을 저장하는 문자열을 지정합니다. 표준 정의 매크로 이름이 사용되는 경우 _STDC_ 매크로의 값은 1입니다. 그렇지 않으면 이 변수가 정의되지 않습니다.


때때로 프로그램을 시작할 때 몇 가지 정보를 전달하는 것이 유용합니다. 일반적으로 이 정보는 명령줄 인수를 통해 main() 함수에 전달됩니다. 명령줄 인수프로그램 이름 다음에 운영 체제 명령줄에 입력되는 정보입니다. 예를 들어, 프로그램 컴파일을 시작하려면 프롬프트 다음에 명령줄에 다음을 입력해야 합니다.

참조 프로그램 이름

프로그램 이름컴파일하려는 프로그램의 이름을 지정하는 명령줄 인수입니다.

명령줄 인수를 허용하기 위해 두 개의 특수 내장 인수인 argc 및 argv 가 사용됩니다. argc 매개변수는 명령행에 있는 인수의 수를 포함하며 정수이며 첫 번째 인수가 프로그램의 이름이기 때문에 항상 1 이상입니다. 그리고 argv 매개변수는 문자열에 대한 포인터 배열에 대한 포인터입니다. 이 배열에서 각 요소는 일부 명령줄 인수를 가리킵니다. 모든 명령줄 인수는 문자열이므로 숫자를 원하는 이진 형식으로 변환하는 것은 개발 시 프로그램에서 제공해야 합니다.

다음은 명령줄 인수를 사용하는 간단한 예입니다. 화면에는 명령줄 인수로 지정해야 하는 Hello라는 단어와 이름이 표시됩니다.

#포함 #포함 int main(int argc, char *argv) ( if(argc!=2) ( printf("이름을 입력하는 것을 잊었습니다.\n"); exit(1); ) printf("안녕하세요 %s", argv) ; 반환 0; )

이 프로그램 이름(이름)을 지정하고 이름이 Tom인 경우 프로그램을 실행하려면 명령줄에 이름 Tom을 입력합니다. 프로그램을 실행한 결과 Hello, Tom이라는 메시지가 화면에 나타납니다.

많은 환경에서 모든 명령줄 인수는 공백이나 탭으로 서로 구분해야 합니다. 쉼표, 세미콜론 및 이와 유사한 문자는 구분 기호로 간주되지 않습니다. 예를 들어,

런 스팟, 런

3개의 문자열로 구성되어 있지만,

에릭, 릭, 프레드

단일 문자열입니다. 일반적으로 쉼표는 구분 기호로 간주되지 않습니다.

문자열에 공백이 포함된 경우 일부 환경에서는 문자열을 큰따옴표로 묶어 여러 인수가 생성되는 것을 방지할 수 있습니다. 결과적으로 전체 문자열이 하나의 인수로 간주됩니다. 운영 체제에서 명령줄 옵션을 설정하는 방법에 대한 자세한 내용은 해당 시스템의 설명서를 참조하십시오.

argv를 올바르게 선언하는 것은 매우 중요합니다. 다음은 가장 자주 수행하는 방법입니다.

문자 *argv;

빈 대괄호는 배열의 길이가 무한함을 나타냅니다. 이제 argv 배열을 인덱싱하여 개별 인수에 액세스할 수 있습니다. 예를 들어, argv는 항상 프로그램 이름인 첫 번째 문자열을 가리킵니다. argv는 첫 번째 인수를 가리키는 식입니다.

명령줄 인수를 사용하는 또 다른 작은 예는 아래의 카운트다운 프로그램입니다. 이 프로그램은 명령줄에 지정된 일부 값에서 카운트다운하고 0에 도달하면 경고음을 울립니다. 초기 값을 포함하는 첫 번째 인수는 표준 함수 atoi()를 사용하여 정수 값으로 변환됩니다. 명령줄의 두 번째 인수(그리고 프로그램 이름을 세 번째 인수로 사용하는 경우)가 문자열 "display"(화면에 출력)이면 카운트다운 결과(역순)가 표시됩니다. 화면에.

/* 거꾸로 계산하는 프로그램. */ #포함 #포함 #포함 #포함 int main(int argc, char *argv) ( int disp, count; if(argc)<2) { printf("В командной строке необходимо ввести число, с которого\n"); printf("начинается отсчет. Попробуйте снова.\n"); exit(1); } if(argc==3 && !strcmp(argv, "display")) disp = 1; else disp = 0; for(count=atoi(argv); count; --count) if(disp) printf("%d\n", count); putchar("\a"); /* здесь подается звуковой сигнал */ printf("Счет закончен"); return 0; }

명령줄 인수를 지정하지 않으면 오류 메시지가 표시됩니다. 명령줄 인수가 있는 프로그램은 종종 다음을 수행합니다. 사용자가 필수 정보를 입력하지 않고 이러한 프로그램을 실행하면 인수를 올바르게 지정하는 방법에 대한 지침이 표시됩니다.

명령줄 인수 중 하나에서 단일 문자에 액세스하려면 argv에 두 번째 인덱스를 입력합니다. 예를 들어 다음 프로그램은 호출된 모든 인수를 문자별로 인쇄합니다.

#포함 int main(int argc, char *argv) ( int t, i; for(t=0; t)

argv의 첫 번째 인덱스는 문자열에 대한 액세스를 제공하고 두 번째 인덱스는 개별 문자에 대한 액세스를 제공한다는 것을 기억하십시오.

일반적으로 argc 및 argv는 시작할 때 필요한 프로그램에 초기 명령을 전달하는 데 사용됩니다. 예를 들어, 명령줄 인수는 종종 파일 이름, 옵션 또는 대체 동작과 같은 정보를 지정합니다. 명령줄 인수를 사용하면 프로그램에 "전문적인 느낌"이 나고 배치 파일에서 더 쉽게 사용할 수 있습니다.

argc 및 argv 이름은 전통적이지만 필수는 아닙니다. main() 함수에서 이 두 매개변수의 이름을 원하는 대로 지정할 수 있습니다. 또한 일부 컴파일러는 main()에 대한 추가 인수를 지원할 수 있으므로 컴파일러 설명서를 확인하십시오.

프로그램에 명령줄 매개변수가 필요하지 않으면 main() 함수에 매개변수가 없도록 명시적으로 선언하는 것이 가장 일반적입니다. 이 경우 이 함수의 매개변수 목록에는 void 키워드가 사용됩니다.