는 C의 주요 기능을 작성하는 방법

지금은 아이들이 파이썬과 자바 스크립트에서의 미친 "응용 프로그램"을 쓰는 것을 알고있다. 그러나 C 언어를 거부 너무 빨리하지 않습니다 - 그것은 많은 것들을 제공하고 간결 할 수 있습니다. 당신은 속도가 필요하면, 당신의 대답은 C 언어로 아마 물품. 당신이 안정적인 일자리를 찾고 또는 널 포인터 디 레퍼런스를 캡처하는 방법을 배우고 싶은 경우, C 언어는 당신의 대답하실 수 있습니다! 이 글에서, 나는 C 파일을 구성하고 성공적으로 명령 줄 인수를 처리하는 C의 주요 기능을 작성하는 방법을 설명합니다.

I : 고집 유닉스 시스템 프로그래머.

당신 : 편집기, C 컴파일러, 그리고 사람을 죽일 시간이.

의 지금 시작하자.

지루한하지만 정확한 C 프로그램

 

는 C의 주요 기능을 작성하는 방법

 

 

패러디 오라일리 책 표지, "미워 다른 사람의 코드"

main () 함수로 시작하는 C 프로그램은 보통의 파일 이름을 main.c에 저장됩니다.

/ * *을 main.c / 
INT의 main (int argc, 문자 *는 argv []) {
}

이 프로그램은 컴파일하지만, 아무것도 할 수 없습니다.

$ gcc를 main.c에 
$ ./a.out -o foo는 -vv
$

올바른 지루.

주요 기능은 고유합니다.

main () 함수가 먼저 실행의 실행의 시작 프로그램의 함수가 아닌 함수의 첫 번째 실행된다. 첫 번째 함수는 자동 (), 일반적으로 C 런타임 라이브러리에서 제공되는 _start 않는 프로그램을 컴파일시에 연결한다. 이 통찰력은 운영 체제와 컴파일러 툴 체인에 크게 의존, 그래서 그것을 언급하지 척.

main () 함수가 일반적으로는 argv와는 argc이라 개의 파라미터를 취하고, 부호있는 정수를 반환한다. 대부분의 유닉스 환경은 프로그램이 성공과 0 (제로)에 반환 -1을 반환 (마이너스 일) 실패 할.

문자 포인터의 매개 변수 이름 매개 변수 설명는 argc 번호 벡터 번호 매개 변수 벡터 argv 배열

ARGV 매개 변수 벡터는 프로그램 명령 줄 표현의 표를 호출하는 것입니다. 위의 예에서,는 다음 ARGV 문자열 목록이다 :

ARGV = "/path/to/a.out", "-o", "foo에", "-vv"];

매개 변수 벡터는 프로그램 실행의 전체 경로는 argv [0]에서 적어도 제 인덱스 문자열을 보장한다.

을 main.c 파일 분석

나는 스크래치을 main.c에서 쓰기 시작하면 다음과 같이 그 구조는 일반적으로 :

/ * Main.c 파일 * / 
/ * 0 권리 / 라이센스 * /
/ * 포함 1 * /
/ * 2 정의 * /
/ * 3 개 외부 선언 * /
/ * 4 형식 정의 * /
/ * 전역 변수 (5) 선언 * /
/ * 6 개 함수 원형 * /
int 주 (int argc, 문자 * argv와 []) {
/ * 명령 라인을 파싱. 7 * /
}
/ * 함수 선언 8 /

부품 수가 0에 지금은 추가로,이 번호를 통해 이야기한다. 소스 코드의 저작권 또는 라이센스 텍스트가있는 경우, 그것은이 배치됩니다.

내가 논의하고 싶지 않은 또 다른 것은 주석입니다.

"거짓말을 댓글." 
- 냉소적하지만 영리하고 잘 생긴 프로그래머.

대신 주석을 사용하여, 의미있는 함수 이름 및 변수 이름을 사용하는 것이 좋습니다.

고유의 관성 프로그래머, 한 번에 추가 의견을 감안, 유지 보수 부담이 배가 될 것입니다. 변경하거나 리팩토링 코드의 경우, 업데이트하거나 주석을 확장 할 필요가있다. 시간이 지남에 따라 코드가 완전히 다른 모양 및 콘텐츠 주석은 완전히 다른 설명했다.

당신이 반대로, 코드가 무엇을하고 있는지에 대해 기록하지 않는 코멘트를 작성해야하는 경우, 왜 작성된 코드를 작성해야합니다. 당신이 코드를 잊어 버린 경우, 5 년 안에 주석을 읽고 싶은 무언가를 작성합니다. 세계의 운명은 당신에 따라 달라집니다. 압력이 없습니다.

1 포함

나는을 main.c 파일에 추가 제일 먼저 들어있는 파일은, 그들은 프로그램에 대한 표준 C 표준 라이브러리 함수와 변수를 많이 제공합니다. C 표준 라이브러리는 많은 것들을 할 수 있습니다. 는 / usr /가에 포함 헤더 파일, 당신은 그들이 무엇을 할 수 있는지 배울 수 있습니다 이동합니다.

#INCLUDE 문자열은 완전히 현재 파일에 인용 된 문서를 포함합니다, C 프리 프로세서 (CPP) 명령입니다. C 헤더 파일 이름은 일반적으로 .H 확장 명명 된, 및 실행 코드를 포함 할 수 없습니다. 그것은 단지 매크로 정의 타입 정의 외부 변수와 함수 원형. 문자열 <header.h가>에 정의 된 헤더 파일 시스템 경로에 header.h가라는 이름의 파일을 찾을 수 CPP 말해, 그것은으로는 / usr / 디렉토리를 포함 일반적이다.

/ * *을 main.c / 
#INCLUDE <STDIO.H>
#INCLUDE <stdlib.h>
사용법 #include <unistd.h>
#INCLUDE <libgen.h>
#INCLUDE <errno.h>
사용법 #include <string.h>
# 포함 <getopt.h>
사용법 #include <SYS / types.h>

이 세계는 최소 컬렉션을 포함 포함 내 기본, 그것은 소개한다 :

표준 입력 FILE, 입출력, 표준 오류 및 fprint ()의 malloc ()은 calloc () 및 realloc을 ()를 제공 STDLIB 함수 패밀리를 제공하는 제공 #INCLUDE STDIO 파일 뭔가 EXIT_FAILURE가 EXIT_SUCCESSlibgen가베이스 이름 () 함수의 errno errno 변수를 제공하는 제공 외부 정의 unistd 설치를 memcpy () memset 함수 () 및 나 strlen () 함수를 수용 할 수있는 모든 문자열 값은 외부 직렬 getopt에 OPTARG, OPTERR, OPTIND 및 getopt는 () 함수 SYS가 / 타입은 uint32_t와 uint64_t 같은 타입 정의를 단축을 제공하는

도 2의 정의

/ * * main.c의 / 
<...>
#DEFINE OPTSTR "VI : O : F : H"
#DEFINE USAGE_FMT "% S [-v] [-f hexflag] [-i inputfile의 [-o OUTPUTFILE] -h] "
#DEFINE의 ERR_FOPEN_INPUT는"fopen의 (입력, 등록 상표) "
는 fopen #DEFINE의 ERR_FOPEN_OUTPUT는"(출력 w) "
#DEFINE ERR_DO_THE_NEEDFUL"do_the_needful는 "폭발
#DEFINE의 DEFAULT_PROGNAME"조지 "

그것은 훨씬 이해가되지 않습니다,하지만 난 그것을 프로그램 명령 줄 스위치에 의해 제안, 설명 할 것입니다 여기에 정의 OPTSTR. OPTSTR ()가 동작을 getopt에 미치는 영향을 참조 getopt는 (3) 매뉴얼 페이지 배울 수 있습니다.

USAGE_FMT 그것이 사용 () 함수에서 사용하는의 printf () 스타일의 형식 문자열을 정의합니다.

또한 파일의이 부분에 #DEFINE 문자열 상수를하고 싶습니다. 필요한 경우, 그들은 더 쉽게 함께 올바른 맞춤법, 재사용, 뉴스, 국제 뉴스를 수집 할 수 있습니다.

마지막으로, #DEFINE에게 모두 대문자로 이름을 지정할 때하는 변수와 함수 이름을 구분합니다. 필요한 경우, 당신은 단지 그들이 라인에 대문자로되어 있는지 확인, 밑줄 함께 또는 구분 된 단어를 넣을 수 있습니다.

3 외부 성명

/ * * main.c의 / 
<...>
통근 용 INT errno를;
통근 숯 *의 OPTARG;
통근 INT의 OPTERR, OPTIND;

통근 문하여 현재 이름 공간에 이름 (예 : "파일") 컴파일 단위 및 프로그램 변수에 액세스 할 수 있습니다. 여기에서 우리는 세 개의 정수 변수와 문자 포인터 정의를 소개합니다. getopt는 () 함수에 의해 여러 변수 옵트 프리픽스는 통신하는 대역 통신 채널의 함수로서, 실패의 C 표준 라이브러리의 errno 원인을 사용한다.

4. 정의

/ * * main.c의 / 
<...>
typedef 구조체 {
상세 int로;
uint32_t 플래그;
파일 * 입력;
* 파일 출력;
options_t};

외부 선언 후, 나는, 노동 조합을 구성, 그리고 열거 선언 typedef로 정의. 타입 정의를 명명하는 것은 전통적인 습관이다. 나는 이름이 유형을 나타 내기 위해 _T 접미사를 사용하고 싶다. 이 예제에서는 구성원이 포함 구조체 4 options_t을 선언합니다. C는 공간에 독립적 인 프로그래밍 언어이기 때문에 I 필드 이름은 같은 열에 배열 된 공간을 사용한다. 난 그냥 보이는 방법을 좋아한다. 포인터가 나는 것을 선언의 이름 앞에 별표 (*)는 분명히이 포인터임을 확인합니다.

5, 전역 변수 선언

/ * * main.c의 / 
<...>
INT dumb_global_variable = -11;

전역 변수 나쁜 생각, 당신은 그들을 사용해서는 안됩니다. 여기 성명에서 전역 변수를 사용하고 확인해야하지만 반드시 그들에게 기본 값을 제공합니다. 정말, 전역 변수를 사용하지 않습니다.

6, 함수 원형

/ * * main.c의 / 
<...>
무효 사용량 (* 숯의 progname, INT 선택 해제);
INT do_the_needful (options_t * 옵션);

함수를 작성하면, 여기, 주 () 함수 대신 이전과 이후에 추가 함수 프로토 타입을 넣어. 초기 C 컴파일러는 프로그램에서 사용하는 각 심볼 (변수 나 함수 이름)을 사용하기 전에 선언해야 함을 의미합니다 단일 패스 전략을 사용합니다. 현대 컴파일러, 그들은 코드를 생성하기 전에 완전한 심볼 테이블을 구축, 거의 항상 엄격하게 필요하지 함수 프로토 타입 멀티 패스 컴파일러입니다. 그러나 때때로, 당신은 코드 컴파일러를 사용하도록 선택하는, 그래서 함수 프로토 타입을 적어주세요 그리고 계속 그렇게 할 수 없습니다.

물론, 난 항상 main () 함수는 명령 줄에서 들어오는 내용을 이해하지 않는 사용 () 함수를 포함,이 함수를 호출합니다.

7, 커맨드 라인 파싱

/ * * main.c의 / 
<...>
INT의 main (int argc, 문자 *는 argv []) {
INT 옵트;
options_t 옵션 = {0, 0x0으로, 표준 입력, 표준 출력};
OPTERR = 0;
(! (최적화 = getopt는 (는 argc, argv를, OPTSTR)) = EOF) 동안
스위치 (최적화) {
: 케이스 'I'
의 경우 (! =하면 fopen (OPTARG options.input ( "R"))) {
perror는 (ERR_FOPEN_INPUT );
출구 (EXIT_FAILURE);
/ * NOTREACHED * /
}
휴식;
케이스 'O':
만약 {((=하면 fopen (OPTARG)), "w"options.output!)
perror는 (ERR_FOPEN_OUTPUT);
출구 (EXIT_FAILURE);
/ * NOTREACHED * /
}
휴식;

케이스 (F) :
options.flags = (uint32_t) strtoul (OPTARG, NULL, 16);
단절;

options.verbose + = 1;
단절;
경우 'H':
기본 :
사용 (기본 이름 (변수는 argv [0]), 선택 하);
/ * NOTREACHED * /
휴식;
}
경우 (do_the_needful (옵션) = EXIT_SUCCESS!) {
perror는 (ERR_DO_THE_NEEDFUL);
출구 (EXIT_FAILURE);
/ * NOTREACHED * /
}
반환 EXIT_SUCCESS;
}

음, 코드를 조금 더. 주 () 함수의 목적은 사용자에 의해 제공된 파라미터를 수집하는 기본적인 입력 검증을 수행하고, 수집 된 파라미터들을 사용하여 전달 함수. 이 예는 기본 값이 변수 옵션을 초기화하고 필요에 따라 업데이트하는 명령 줄 옵션을 구문 분석하는 데 사용됩니다 선언합니다.

핵심 main () 함수는, ARGV을 통과 명령 줄 옵션 및 인수 (있는 경우)를 찾기 위해 getopt는 ()를 사용하는 동안 루프입니다. 파일의 OPTSTR 전면 템플릿 기반 getopt는 () 동작을 정의한다. 모든 문자 명령 줄 옵션의 값은 switch 문에 명령 줄 옵션의 발생을 검출에 대한 응답으로 프로그램을 찾기 위해 변수 getopt는 ()를 받아들이 선택.

당신이 요청할 수도 통지하는 경우, 왜 옵트 32 INT로 선언되어 있지만, 8 문자 것으로 예상된다? 사실 getopt에에서 ()는 int로, 그것은, 내가 EOF 일치 (파일 마커의 끝)을 사용하는 것이 음의 값의 끝에 도달 ARGV 시간을 반환합니다. 문자 서명,하지만 나는 그들의 변수 함수의 반환 값과 일치하고 싶습니다.

알려진 명령 줄 옵션을 감지하면, 특정 문제가 발생합니다. 콜론의 끝에 매개 변수를 지정 OPTSTR, 이러한 옵션은 인수를 가질 수 있습니다. 옵션 파라미터가있는 경우, 다음의 문자열 OPTARG 외부에서 정의 된 변수들에 의해 프로그램을 제공 할 수있다 argv를. 나는 읽기와 쓰기, 또는 정수 값에 문자열 변환 명령 줄 인수를 위해 파일을 열 OPTARG 사용합니다.

여기에 코드 스타일에 대한 몇 가지 주요 사항은 다음과 같습니다 :

  • 0으로 초기화 OPTERR는 getopt는 트리거 금지?
  • 또는 종료 (EXIT_SUCCESS) 메인 () 종료 (EXIT_FAILURE)의 중간에,.
  • / * NOTREACHED * / I는 보풀이 명령을 좋아한다.
  • 반환 EXIT_SUCCESS 함수의 끝에서 사용 int를 반환합니다.
  • 암시 적 타입 캐스트를 표시합니다.

다음과 같이 컴파일이 프로그램 명령 줄 형식 :

$ ./a.out -h 
a.out의 [-v] [-f hexflag] [-i inputfile의 [-o OUTPUTFILE] [-h]

사실, 빌드 () 후 사용은 stderr로 이러한 콘텐츠를 전송합니다.

8, 함수 선언

/ * main.c에 * / 
<...>
무효 사용 (숯불 *의 progname, INT 수신 거부) {
fprintf와 (표준 오류, USAGE_FMT, progname progname : DEFAULT_PROGNAME);
출구 (EXIT_FAILURE);
/ * NOTREACHED * /
}
INT의 do_the_needful (options_t * 옵션) {
경우 {(옵션!)
의 errno = EINVAL;
EXIT_FAILURE를 반환;
}
경우 (! 옵션 -> 입력 || 옵션 -> 출력!) {
의 errno = ENOENT;
EXIT_FAILURE를 반환;
}
/ * XXX 필요한 물건 * / 할
반환 EXIT_SUCCESS을;
}

나는 마지막 함수는 템플릿 함수가 아닙니다 물품. 본 실시 예에서, 함수는 do_the_needful () options_t 구조에 대한 포인터를 받아 들인다. 나는 그 출력의 구성원의 입력과 구조를 검증하기 위해 진행 옵션 포인터가 NULL이 확인했습니다. 테스트가 실패하면, EXIT_FAILURE를 반환하고, 기존의 오류 코드 외부 전역 변수 errno에 의해, 나는 오류의 호출 루틴 원인을 알 수 있습니다. 발신자는 오류 메시지를 발행 편의 함수 perror는 ()를 사용할 수의 errno의 값에 따라 쉽게 읽을 수 있습니다.

기능은 입력의 유효성을 검사 어떤 방법으로 거의 항상이다. 완전히 큰 비용 확인 된 경우, 데이터 유효성 검사 한 번 불변으로 간주을 수행하려고합니다. 조건 fprintf와 () 호출 할당 progname 인증 매개 변수를 사용하여 사용 () 함수. 다음 사용 () 함수는 종료합니다, 그래서 errno를,이 프로그램의 정확한 이름을 사용할지 여부에 대해 걱정할 필요가 없습니다 설정하는 귀찮게하지 않습니다.

여기, 내가 피하고 싶은 가장 큰 실수는 NULL 포인터 역 참조입니다. 이것은 피할 수없는 죽음에 이르는, 내 프로세스에 SYSSEGV라는 특별한 신호를 보낼 수있는 운영 체제의 원인이됩니다. 대부분의 사용자는 SYSSEGV의 붕괴로 인해 발생보고 싶지 않아. 바람직하게는 적절한 NULL 포인터 캡처 오류 메시지를 발행하고 정상적으로 프로그램을 닫습니다.

어떤 사람들은 함수 본문에서 여러 반환 문, 그들이 뭔가 "연속 제어의 흐름"등에 대해 많은 이야기가 있다는 것을 불평하고있다. 솔직히, 중간 기능 오류 경우,이 오류 상태를 반환해야합니다. 단지 수익이 더가없는 문장은 "좋은 아이디어"™을 의미하는 경우 중첩을 많이 씁니다.

4 개 이상의 매개 변수를 허용하는 함수를 작성하는 경우 마지막으로, 구조로 결합 고려하고 구조에 대한 포인터를 전달합니다. 이 함수 서명 간단하고 기억하기 쉽게, 그리고 당신이 후 전화를하지 않을 때 잘못. 또한 때문에 어떤 함수 스택 적은 복제의 필요성, 빠른 약간 호출 기능을 할 수 있습니다. 실제로, 기능은 수백만 또는 시간의 수십억을 호출하는 경우에만, 우리는이 문제를 고려할 것입니다. 당신이 이해가되지 않습니다 생각한다면, 그것은 중요하지 않습니다.

당신이 어떤 의견을 말하지 않는, 잠깐! ? ! !

do_the_needful () 함수에서, 나는이 코드를 설명하기보다는, 자리 표시 자로 디자인 된 주석의 특별한 유형을 썼다 :

/ * XXX 필요한 물건을 할 * /

당신이 쓸 때, 때때로 당신은 지금 나중에 쓸 수 있지만하지 않습니다 중지하고 일부, 특히 복잡한 코드를 작성하고 싶지 않아요. 그게 내가 다시 와서 자신의 장소로 남아거야. 나는 XXX 접두사에 코멘트 짧은 노트에해야할 일들에 대한 설명을 삽입합니다. 나는 더 많은 시간이있을 때 그 후, 나는 소스 코드에 XXX 찾습니다. 접두사 그냥에서 (예 : 함수 이름 또는 변수로) 다른 맥락에서 코드의 라이브러리에 표시하지 않을 있는지 확인 무엇을 중요하지 않습니다.

함께 넣어

이 프로그램을 컴파일 할 때 음, 여전히 거의 효과가 없다. 그러나 지금 당신은 당신의 자신의 명령 줄 구문 분석 C 프로그램을 구축 할 수있는 견고한 프레임 워크가있다.

/ * main.c에 - 전체 목록 * / 
사용법 #include <stdio.h에>
사용법 #include <stdlib.h>
사용법 #include <unistd.h>
사용법 #include <libgen.h>
사용법 #include <errno.h>
사용법 #include <문자열 .H>
#INCLUDE <getopt.h>
#DEFINE OPTSTR "VI : O : F : H"
#DEFINE USAGE_FMT "% S [-v] [-f hexflag] [-i inputfile의 [-o OUTPUTFILE] -h ] "
는 fopen #DEFINE의 ERR_FOPEN_INPUT"(입력, 등록 상표) "
는 fopen #DEFINE의 ERR_FOPEN_OUTPUT"(출력 w) "
#DEFINE ERR_DO_THE_NEEDFUL"do_the_needful는 "폭발
#DEFINE의 DEFAULT_PROGNAME을"조지 "
통근 INT errno를;
통근 숯 *의 OPTARG;
통근 INT의 OPTERR, OPTIND;
타입 정의 구조체 {
자세한 int로;
uint32_t 플래그;
파일 * 입력;
* 파일 출력;
options_t};
INT dumb_global_variable = -11;
무효 사용 (숯불 *의 progname, INT 수신 거부);
INT do_the_needful (options_t * 옵션);
INT의 main (int argc, 문자 *는 argv []) {
INT 옵트;
options_t 옵션 = {0, 0x0으로, 표준 입력, 표준 출력};
OPTERR = 0;
(! (최적화 = getopt는 (는 argc, argv를, OPTSTR)) = EOF) 동안
스위치 (최적화) {
: 케이스 'I'
의 경우 (! =하면 fopen (OPTARG options.input ( "R"))) {
perror는 (ERR_FOPEN_INPUT );
출구 (EXIT_FAILURE);
/ * NOTREACHED * /
}
휴식;
케이스 'O':
만약 {((=하면 fopen (OPTARG)), "w"options.output!)
perror는 (ERR_FOPEN_OUTPUT);
출구 (EXIT_FAILURE);
/ * NOTREACHED * /
}
휴식;

케이스 (F) :
options.flags = (uint32_t) strtoul (OPTARG, NULL, 16);
단절;
경우에 'V'
options.verbose + = 1;
단절;
경우 'H':
기본 :
사용 (기본 이름 (변수는 argv [0]), 선택 하);
/ * NOTREACHED * /
휴식;
}
경우 (do_the_needful (옵션) = EXIT_SUCCESS!) {
perror는 (ERR_DO_THE_NEEDFUL);
출구 (EXIT_FAILURE);
/ * NOTREACHED * /
}
반환 EXIT_SUCCESS;
}
무효 사용 (숯불 *의 progname, INT 수신 거부) {
fprintf와 (표준 오류, USAGE_FMT, progname progname : DEFAULT_PROGNAME?);
출구 (EXIT_FAILURE);
/ * NOTREACHED * /
}
INT의 do_the_needful (options_t * 옵션) {
경우 (! 옵션) {
errno를 EINVAL =;
EXIT_FAILURE를 반환;
}
경우 (! 옵션 -> 입력 || 옵션 -> 출력!) {
의 errno = ENOENT;
EXIT_FAILURE를 반환;
}
/ * XXX 필요한 물건 * / 할
반환 EXIT_SUCCESS을;
}

이제, 당신은 더 많은 유지 보수가 C 언어를 쓸 준비가 된 것입니다.

추천

출처www.cnblogs.com/dtdg777/p/10994853.html