C 언어 코드에 어셈블리 언어를 통합하는 방법을 제공한다. 어셈블리 언어는 컴파일러와 시스템에 의존하기 때문에 이 자리에서는 Microoft Visual C ++ 및 Intel x86 호환 프로세서를 전제로 코드 내에 어셈블리를 통합하는 방법을 설명한다.

C 언어에 어셈블리 언어를 포함하기

C 언어는 인간이 이해하기 쉬운 고급 언어로 분류되지만, 고급 언어 중에서는 매우 기계어에 가까운 존재이기도 한다. 따라서 저급 언어라는 기계어에 가까운 프로그래밍 언어인 어셈블리 언어와의 궁합도 좋다고 생각된다. 어셈블리 언어는 고급 언어와 달리, 기계어로 직접 번역할 수 있는 언어로 하나의 문장이 하나의 기계어로 대응하고 있다.

현대에서는 대부분의 프로그래머가 어셈블리 언어을 사용하지 않게 되었고, 어셈블리 언어를 몰라도 프로그래머가 될 수 있다. 그럼 정말로 어셈블리 언어는 필요없는 언어인 것인까? 그러나 실전의 개발을 하면 의외로 어셈블리 언어의 지식이 필요한 경우가 있다. 예를 들어, 고급 게임 프로그래머가 되기 위해 어셈블리 언어는 빼놓을 수 없다. CPU의 특정 명령 세트(※ 1)를 어셈블리 언어에서 명시적으로 사용함으로써, 연산 속도를 가속화시킬 수 있는 기술이 존재하기 때문이다.

또는 운영 체제의 연구를 할 경우에도 어셈블리 언어의 지식이 필요하다. 현대의 대부분의 운영 체제는 C언어로 작성되어 있지만, 그 기본 부분에는 역시 어셈블리 언어를 사용하지 않으면 안된다.

따라서 C 언어와 어셈블리 언어의 관계는 매우 밀접한 것이 있다. 많은 프로그램은 C 언어를 사용하여 실현할 있지만, 프로그램의 일부로 하드웨어의 기능을 직접 조작하는 것 같은 필요가 있는 경우, 어셈블리 언어를 사용하지 않으면 안된다. 이런 사태에 대비해 일부 컴파일러는 C 언어 소스에서 어셈블리 언어를 작성하는 것이 가능하게 되어 있다. 이를 인라인 어셈블러라고 한다.

인라인 어셈블러는 순수한 어셈블리 언어가 아니라, 일반적으로 C 언어와 어느 정도 협조할 수 있다. 예를 들어, 어셈블리에서 C 언어의 변수와 함수를 호출하는 일이 생길지도 모른다. C 언어의 인라인 어셈블러는 표준이 아닌, 개발 환경에 의존하는 것이므로, 컴파일러는 전혀 어셈블러를 지원하지 않을 수 있다. 물론, 어셈블리 언어는 CPU의 명령 세트에 의존하기 때문에 사용되는 어셈블리 언어 컴파일 환경에 따라 달라진다. C 언어와는 달리, 어셈블리 언어에는 표준이라는 개념이 없다. 따라서 개발 환경이 인라인 어셈블러를 지원하고 있는지, 어떤 명령을 사용할 것인가하는 문제는 이 문서의 범위를 초과한다. 사용하는 개발 환경의 도움말 등을 참조하자.

일반적으로 인라인 어셈블러는 asm 키워드가 사용된다. 이 책을 쓰는 시점에서 가장 유명한 컴파일러 중 하나인 Microsoft Visual C ++에서 __asm 키워드를 사용하여 인라인 어셈블러를 작성할 수 있다.

asm문(Visual C++)

__asm 어셈블러 명령
__asm { 어셈블러 명령 목록 }

__asm 키워드 뒤에는 어셈블리 언어의 명령을 작성할 수 있다. 여러 줄의 명령을 작성하려면 {}를 사용하여 __asm 블록을 작성한다. 한 줄 어셈블러이면 __asm 키워드 직후에 문장을 작성한다.

코드1

#include <stdio.h>

char strInputMessage[] = "2개의 정수를 입력하십시오.>";
char strScanf[] = "%d %d";
char strResult[] = "%d + %d = %d\n";

int main() {
    int x, y;

    __asm {
        push offset strInputMessage
        call dword ptr printf
        add esp, 4
        
        lea eax, [y]
        push eax
        lea ecx, [x]
        push ecx
        push offset strScanf
        call dword ptr scanf
        add esp, 0Ch

        mov eax, x
        mov ebx, y
        add eax, ebx

        push eax
        push y
        push x
        push offset strResult
        call dword ptr printf
        add esp,10h
    }
    return 0;
}

코드1는 __asm 키워드를 사용하여 인라인 어셈블러로 작성된 C 언어 프로그램의 예이다. 이 프로그램은 Microsoft Visual C ++ 6.0에서 컴파일되는 것을 전제로 하고 있다. Microsoft Visual C ++ 인라인 어셈블러는 Intel 486 프로세서의 명령어 세트를 지원하고 있다.

어셈블리 언어의 해설은 특정 컴퓨터에 의존하는데, 이 문서의 범위가 아니므로 생략하지만, 코드1의 인라인 어셈블러는 C 언어의 printf()와 scanf() 함수를 호출한다. 어셈블러에서 C 언어의 변수명 및 함수명이 사용되고 있는 것에 주목한다. 프로그램은 2개의 정수를 x 변수와 y 변수에 입력하고 이들을 더한 결과를 표시하고 종료한다.

이와 같이, C 컴파일러에서 인라인 어셈블러를 지원하는 것이 있기 때문에 연산 속도가 요구되는 프로그램 등에서는 어셈블리 언어를 사용할 수 있다.


C 언어의 표준 함수를 사용하여 적당한 값을 얻는 방법을 설명합니다.

랜덤 값 얻기

많은 게임은 사용자가 예상할 수 없는 결과를 얻을 필요가 있다. 또는 자연 과학 및 사회 과학 등의 시뮬레이션을 수행하는 프로그램에서도 일정하게 예기치 않은 변화를 준비해야 한다. 일반적인 비즈니스 응용 프로그램은 필요하지 않지만, 프로그램의 종류에 따라서는 얻지로 부정확한 결과를 산출해야 할 수도 있는 것이다.

이것을 실현하려면 난수를 얻어야 한다. 난수를 사용하기 위한 함수는 stdlib.h 헤더 파일에 선언되어 있다. 난수를 이용하면, 게임 프로그래밍은 물론, 반복 처리와 난수를 사용한 문제를 처리하는 몬테카를로 방법(Monte Carlo method)이라는 수학 기법을 사용한 프로그램 등, 다양한 용도가 있을 수 있다.

난수를 얻으려면 rand() 함수를 사용한다.

rand() 함수

int rand( void );

rand() 함수는 0부터 RAND_MAX 범위 내에서 int 형 난수를 반환한다. RAND_MAX는 stdlib.h 헤더 파일에 정의되어 있는 rand() 함수가 반환하는 최대 값을 나타내는 상수이다.

이 함수가 반환하는 값은 완전한 난수가 아닌, 특정 계산식으로 산출한 적당한 값이다. 일반적으로 컴퓨터는 난수를 발생시키는 하드웨어를 가지고 있지 않다. 그래서 난수를 얻기 위해 기본이 되는 값을 계산하고 난수를 생성한다. rand() 함수는 생성된 난수를 얻기 위한 함수이다.

코드1

#include <stdio.h>
#include <stdlib.h>

int main() {
    int iCount;

    printf("/// 난수의 최대값 = %d ///\n" , RAND_MAX);

    for(iCount = 0 ; iCount < 10 ; iCount++)
        printf("난수 열 %d = %d\n" , iCount , rand());

    return 0;
}

코드1을 실행하면 RAND_MAX 상수 값과 rand() 함수에서 얻은 의사 난수를 표시한다.

(컴퓨터에서 만들 수 있는 난수는 임의적일 수 밖에 없어서 난수처럼 보이지만 완전한 무작위는 아니다. 이를 의사난수라고 한다.)

표준은 stdlib.h에 정의되어 있는 매크로 RAND_MAX는 32767 이상임을 보증한다. 표시되는 의사 난수를 보면 확실히 rand() 함수는 0 ~ 32767 이내의 적당한 값을 반환하는 것을 확인할 수 있다.

그러나 코드1을 다시 실행하면 똑같은 결과를 얻는다. 난수 열는 확실히 적당한 값이지만, 프로그램을 실행할 때마다 같은 난수 열을 반환하도록 것은 이 프로그램의 실행 결과가 항상 같으므로 임의 처리로는 사용할 수 없다. 왜, rand() 함수는 같은 난수 열을 반환할까?

실은 의사 난수를 발생시키기 위해서는 "씨"을 뿌려야 한다. 난수 열은 기본이 되는 값을 사용하여 생성된다. 사용되는 계산식은 항상 동일하기 때문에, 기본 값이 동일하면 같은 난수 열이 생성되어 버린다. 코드1이 항상 같은 결과가 되는 것은 난수를 생성하기 위한 기준 값이 같았기 때문이다. 이 기준 값을 시드(Seed)라고 한다. 난수를 생성하기 위한 시드를 설정하려면 srand() 함수를 사용한다.

srand() 함수

void srand( unsigned int seed );

seed에는 난수를 생성하기 위한 시드 값을 지정한다. 난수는 seed로 지정된 값을 초기 값으로 난수를 생성한다. seed에 1을 지정하면 난수가 초기화된다. srand ()를 호출하기 전에 rand()가 호출된 경우 seed를 1로 난수 열이 생성된다.

프로그램을 실행할 때마다 예기치 못한 적당한 값을 얻기 위해서는 항상 다른 시드를 부여해야 한다. 난수의 생성에 가장 많이 사용되는 초기 값은 시간이다. time() 함수를 사용하여 얻은 값은 항상 다른 값이 되기 때문에 개발자도 예상할 수 없는 적당한 값을 동적으로 얻을 수 있다.

코드2

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

int main() {
    int iCount;

    srand(time(NULL));
    for(iCount = 0 ; iCount < 10 ; iCount++)
        printf("난수 열 %d = %d\n" , iCount , rand());

    return 0;
}

코드2는 rand() 함수를 호출하기 전에 srand() 함수를 이용하여 난수의 초기 값을 지정한다. time() 함수를 사용하여 현재 시간 값을 시드로 지정하고 있기 때문에 이 프로그램을 실행할 때마다 다른 초기 값을 난수의 생성에 사용한다. 이번에는 실행할 때마다 적당한 값을 얻을 수 있을 것이다.

그러나 이 상태에서는 특정 범위의 난수를 얻을 수 없다. 일반적으로 32767까지의 넓은 범위의 난수를 사용하지 않는다. 반대로, 경우에 따라서는 32767 이상의 넓은 범위의 난수를 얻고 싶을 때도 있을 것이다. 이것을 실현하려면 rand() 함수에서 얻은 결과를 계산하고 해결한다. 지정 범위의 난수를 얻는 가장 쉬운 방법은 난수와 최대 값을 나눈 나머지 값을 얻는 방법이다. 0~9의 난수가 필요한 경우는 rand() % 10를 계산하여 얻을 수 있다.

하지만, 이 방법은 그다지 권장되지 않는다. 더 좋은 방법으로, 0.0에서 1.0까지의 부동 소수점형 의사 난수를 사용하는 수법이 선호된다. 0~1 사이의 부동 소수점 의사 난수를 얻을 수 있다면, 이것은 최대 값을 곱하여 특정 범위의 난수를 얻을 수 있다. 이 방법이면 난수의 최대 값에 얽매이지는 않는다. 0.0에서 1.0 사이의 난수를 얻으려면 다음과 같은 매크로를 작성하는 것이 좋다.

#define random() ((double)rand() / (RAND_MAX + 1))

random () 매크로 함수는 0.0에서 1.0 사이의 난수를 반환한다.

코드3

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#undef random
#define random() ((double)rand() / (RAND_MAX + 1))

int main() {
    int iCount;
    srand(time(NULL));
    for(iCount = 0 ; iCount < 10 ; iCount++)
        printf("난수 열 %d = %g\n" , iCount , random());
    return 0;
}

코드3은 0.0에서 1.0까지의 double 형의 난수를 반환 random() 매크로 함수를 정의하고 있다. 덧붙여서 호환성을 위해 일부 처리계에서는 매크로 함수 randam()가 이미 정의되어 있을 수 있다. 그래서 #undef 지시문을 사용하여 random()가 중복되지 않는 것을 보장하고 있다. 불필요한 수고가 싫다면 이름이 충돌하지 않도록 다른 이름으로 매크로 함수를 정의해도 상관없다.

  1. em9xdm 2018.07.24 15:49

    동아리 보고서 작성중 들렀습니다.
    굉장히 자세히 설명해주셔서 이해가 잘 되었습니다 감사합니다!!!

  2. chobo 2019.01.01 11:43

    #define random() ((double)rand() / (RAND_MAX + 1))

    질문 있어요!
    여기서 왜 RAND_MAX에 1을 더하신건지 이해가 잘 안되요.
    0.0 - 1.0 까지의 값을 뽑으려면 (double)rand() / RAND_MAX 가 아닌가요?

C 언어의 표준 함수를 사용하여 컴퓨터의 시간을 가져온다. 시간은 단순한 수치로 처리되어 있기 때문에, 날짜로 취급하려면 시스템의 로컬 시간으로 변환해야 한다.

시간을 처리하기

데이터 관리를 할 때, 시간은 중요한 존재이다. 예를 들어 정보가 업데이트 되었을 때는 프로그램 업데이트 로그를 남기는 등의 처리가 요구된다. 많은 시스템에는 시스템 시계가 있기 때문에 이용자에게 "지금의 시간을 입력하라"'고 묻는다 프로그램은 불친절하다. 프로그램은 지금 날짜를 알고 싶다면, 시스템 시계에서 시간을 산출한다.

현재 시간을 얻거나, 새로 설정하는 프로그램을 만드는 경우 기본적으로 시스템에 문의해야 한다. 시스템이 지원하고 있는 시간 관련 기능을 최대한으로 발휘시키는 경우는 C 언어의 표준 함수에서만 지원할 수 있는 것이 아니기 때문이다. 그러나, 복잡한 API를 사용하지 않아도 시간의 취득이나 변환 등의 기본적인 처리는 표준 함수에서 지원되고 있다.

시간 관련 함수는 time.h 헤더 파일에 선언되어 있다. 시스템 시계에서 시간을 얻으려면 time() 함수를 사용한다.

time() 함수

time_t time( time_t *timer );

timer에는 시스템 시간을 얻는 time_t 형 변수의 포인터를 지정한다. time_t 형은 time.h 헤더 파일에 정의되어 있는 시간을 나타내는 산술형의 typedef 명이다. time_t 형의 실제 형식은 구현에 따라 달라진다. time() 함수는 인수에서 얻은 값과 같은 값을 반환한다. 즉, time () 함수는 시간을 얻는 수단으로 포인터에 간접 참조에서 값을 저장하는 방법 및 반환 값에서 얻는 방법을 선택할 수 있다. 반환 값을 얻는 경우는 인수를 NULL로 해도 상관없다.

이 함수에서 얻을 수 있는 값은 시스템의 부호화된 time_t 형 달력 시간이다. 시간을 얻을 수 없는 경우는 -1을 돌려준다. time() 함수가 반환하는 값은 구현에 의존하기 때문에, 사용하는 시스템의 시간 방식을 확인한다. Microsoft Windows에는 시스템 클럭에 따라 만국 표준시(UCT)의 1970 년 1월 1일 0시 0분 0초부터 경과된 시간을 초단위로 나타내는 숫자를 반환한다.

이대로는 매우 시간이라고는 생각되지 않는듯한 수치가 나타날 뿐이기 때문에(무엇보다, 시스템에 의존하는 시간 처리는 시스템에 의존한 소스를 쓰는 것이 되기 때문), 이를 현재 시간으로 변환해야 한다. 연산에 의해 경과 시간에서 현재 시간을 얻을 수 있지만, localtime() 함수가 이 처리를 수행해 준다.

localtime() 함수

struct tm *localtime( const time_t *timer );

timer에는 변환하는 시간 값에 대한 포인터를 지정한다. 시간은 년/월/일 등의 값을 나타내는 멤버가 있는 tm 구조체에 대한 포인터를 반환한다. tm 구조체는 다음과 같이 선언되어 있다.

tm 구조체

struct tm {
        int tm_sec;   /* 초 - [0~61] (閏秒を考慮) */
        int tm_min;   /* 분 - [0~59] */
        int tm_hour;  /* 시 - [0~23] */
        int tm_mday;  /* 일 - [1~31] */
        int tm_mon;   /* 월 - [0~11] */
        int tm_year;  /* 1900부터의 년 */
        int tm_wday;  /* 일요일부터의 요일 - [0~6] */
        int tm_yday;  /* 년초부터의 통산 일수 - [0~365] */
        int tm_isdst; /* 서머 타임이 유효하면 양수, 유효하지 않으면 0, 불명이면 음수*/
};

localtime() 함수는 인수 timer에서 지정된 시간 값을 바탕으로 각 멤버를 적절한 값으로 초기화되어 있는 tm 구조체에 대한 포인터를 반환한다. 이 함수가 반환된 포인터를 이용하면 시간 값에서 사람이 이해할 수 있는 시간을 표시하는 것이 가능하다.

tm 구조체의 월은 0부터 시작되므로, 1월은 0임을 주의한다. 또한 년은 1900년부터 계산된 값이다. 실제 서기로 변환하려면 1900을 추가해야 한다.

코드1

#include <stdio.h>
#include <time.h>

int main() {
    struct tm *date;
    const time_t t = time(NULL);
    date = localtime(&t);

    printf(
        "%d/%d/%d %d:%d:%d\n" , date->tm_year + 1900 , date->tm_mon + 1 ,
        date->tm_mday , date->tm_hour , date->tm_min , date->tm_sec
    );

    return 0;
}

코드1은 현재 시간을 표시하는 프로그램이다. 시간은 년/월/일 시:분:초 형태로 표준 출력에 표시된다. 프로그램은 먼저 time() 함수를 사용하여 시스템 시계에서 시간 값을 가져온다. 다음에 localtime() 함수를 사용하여 시간 값을 tm 구조체로 변환한다. 다음은 tm 구조체의 시간을 나타내는 각 멤버에 액세스하여 시간을 표시한다.

여러 문자열을 결합하거나 문자열의 일부를 다른 문자열을 삽입하는 것과 같은 작업은 문자열 작업을 수행 표준 함수를 사용한다.

문자열을 추가 및 변환 처리

일반적으로 많은 고급 언어는 직관적인 문자열 조작 기능을 제공한다. 인간의 감성을 생각하면 다음과 같은 문장은 문자열의 추가 처리인 것을 기대할 것이다.

"오늘은 " + 2017 + "년 "+ 11 + "월 " + 26 + "일 입니다"

이 수식은 여러 문자열과 숫자로 구성되어 있다. 고급 언어의 대부분은 식을 최종적으로 문자열로 변환한다.

"오늘은 2017년 11월 26일 입니다"

많은 프로그래머는 문자열의 덧셈 연산이 가능한 결과를 원할 것이다. 그러나, C 언어의 경우는 문자열 리터럴의 실체는 문자 배열이므로 배열의 앞에 포인터로 처리된다. 따라서 문자열 편집하려면 직접 배열을 조작할 수 밖에 없다. 문자열을 추가하려면 충분한 메모리 공간을 할당하고, 문자열의 맨 끝에서 추가할 문자열의 각 문자를 순서대로 대입해 나갈 필요가 있다. 문자열의 일부를 잘라내거나 숫자와 문자열의 상호 변환을 할 때도 마찬가지이다.

그러나, 이를 모두 자기 부담으로 준비하는 것은 효율적이지 않다. 그래서 표준 라이브러리는 문자열을 조작하기 위한 기본적인 함수들을 제공하고 있다. 문자열 관련 함수는 string.h 헤더 파일에 선언되어 있다. 기본적인 문자열의 추가는 strcat() 함수를 사용한다.

strcat() 함수

char *strcat( char *string1, const char *string2 );

strcat() 함수는 string1에 string2를 추가한다. 이 함수의 반환 값은 string1과 동일한 것이고, 에러 값을 반환하는 것은 아니다. string1와 string2은 NULL로 끝나는 문자 배열이어야 한다.

함수는 string1의 NULL 문자를 string2의 앞에 문자열로 덮어쓰고, 그 후의 문자열을 추가하는 것이다. string1에 할당된 메모리 공간이 부족하기도 하고, string1과 string2가 겹쳐져 있는 경우의 동작은 정의되지 않는다. 즉, string1과 string2는 원칙적으로 다른 문자 배열에 대한 포인터이어야 한다.

코드1

#include <stdio.h>
#include <string.h>

int main() {
    char str1[256] = "Kitty on your lap ";
    strcat(str1 , "~당신의 무릎 위에 고양이~");
    printf("%s\n" , str1);

    return 0;
}

코드1에는 단순히 str1에 다른 문자열을 strcat() 함수를 사용하여 추가한다. str1는 문자열을 추가할 수 있도록 여분에 배열 크기를 할당하고 있다. 리터럴 문자열에 문자열을 추가하지 않도록 주의한다.

이러한 처리를 위해서는 문자열의 문자수가 중요하다. 함수가 항상 문자열의 수를 파악할 수 있는 것은 아니다. 동적으로 문자열 포인터에서 문자 수를 확인하고, malloc() 함수에서 힙을 할당하여, 이에 문자열을 편집하는 처리를 할 수 있을 것이다. 문자수를 확인하려면 배열의 처음부터 조사하여 NULL 즉 0의 값을 발견 할 때까지 계산하여 얻을 수 있지만, 이 처리도 표준 함수가 지원해주고 있다. 문자수를 얻으려면 strlen() 함수를 사용한다.

strlen() 함수

size_t strlen( const char *string );

string의 문자수를 확인하고 싶은 NULL로 끝나는 문자 배열의 포인터를 지정한다. strlen () 함수는 string의 문자 수를 반환한다. 이 문자 수에는 맨 끝의 NULL 문자는 포함되지 않으므로 주의하자.

코드2

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    const char *str1 = "Kitty on your lap ";
    const char *str2 = "~당신의 무릎 위에 고양이~";
    char *str3 = (char*)malloc(strlen(str1) + strlen(str2) + 1);
    *str3 = 0;

    strcat(str3 , str1);
    strcat(str3 , str2);
    printf("%s\n" , str3);

    free(str3);
    return 0;
}

코드2에는 strlen() 함수를 사용하여 문자열에 대한 포인터 str1과 str2에서 문자 수를 조사하고 있다. 문자 수는 malloc() 함수에 의해 메모리 할당에 사용되고, 동적으로 문자의 가산 처리를 실시하고 있다. str3는 str1과 str2의 문자열을 strcat()에 추가하기 위해 충분한 메모리 공간이 할당한다. 이러한 처리를 실시하면, 매개 변수로 받은 문자열에 대한 포인터 등, 함수의 작성 단계에서 문자를 정적으로 파악할 수 없는 경우에도 올바른 문자열 처리를 할 수 있다.

코드2의 중간에 str3에 문자열 str1을 추가하기 위해 str3의 앞 요소에 일부러 0 (NULL 문자)를 대입하고, strcat() 함수를 사용하고 있는데, 이것은 제대로 된 방법이라고 할 수 없다. 첫번째 strcat(str3, str1)는 단순히 str3에 str1을 복사하고 있을 뿐이다. 문자열을 복사할 경우 strcpy() 함수를 사용하는 것이 좋다.

strcpy() 함수

char *strcpy( char *string1, const char *string2 );

이 함수는 단순히 string1에 string2를 NULL 문자를 포함하여 복사한다. 그 이외의 동작은 기본적으로 strcat() 함수와 같고, 함수는 대상에 문자열 string1을 반환한다. string1에 할당된 메모리 공간이 부족하거나, string1과 string2가 겹쳐져 있는 경우의 동작은 정의되지 않는다.

그런데 이러한 문자열 편집 처리를 프로그램을 수행할 경우, if문으로 문자열 비교가 요구될지도 모른다. 문자열에 대한 포인터가 동일한지 여부를 조사하는 것은 간단하지만, 두개의 NULL로 끝나는 문자 배열이 완전히 똑같은 내용의 문자 배열을 확인하려면 strcmp() 함수를 사용한다.

strcmp() 함수

int strcmp( const char *string1, const char *string2 );

string1과 string2는 비교할 NULL으로 끝나는 문자열에 대한 포인터를 지정한다. 함수의 반환 값이 0이면 두 문자열이 같은 문자열임을 나타낸다. 음수를 돌려 주었을 경우는 사전 순으로 string1이 string2보다 작고, 양수를 반환하면 string1이 string2보다 크다는 것을 나타낸다.

코드3

#include <stdio.h>
#include <string.h>

int main() {
    char str1[255], str2[255];

    printf("문자열 2개를 입력하십시오.>");
    scanf("%s %s", str1, str2);

    if (strcmp(str1 , str2) == 0)
        printf("%s와 %s는 같다.\n" , str1 , str2);
    else if(strcmp(str1 , str2) < 0)
        printf("%s와 %s보다 작다.\n" , str1 , str2);
    else
        printf("%s는 %s보다 크다.\n" , str1 , str2);

    return 0;
}

코드3은 커멘드 라인 인수로 2개의 문자열을 입력한다. 그러면 프로그램은 입력된 문자열을 strcmp() 함수로 비교하고, 그 결과를 표준 출력에 표시한다. 문자열을 사전 순으로 정렬하거나, 동일한 문자열 여부를 확인 할때에 strcmp()는 유용하다.

여기까지 소개한 문자열 관련 함수는 간단한 문자열의 제어에 자주 사용되는 함수이지만, 복잡한 처리가되면 이것만으로는 실현될 수 없다. 예를 들어 숫자 형식의 변수를 문자열로 변환하여 다른 문자열에 추가하려면 매우 시간이 걸릴 것이다. 이를 쉽게 달성할 방법이 있다면 편리하지만, 문자열 관련 함수에 그런 것은 없다.

그러나, 우리는 이미 고급 문자열 변환을 수행하는 함수를 사용하고 있다. printf() 함수와 fprintf() 함수이다. 이것들은 서식 제어 문자열과 가변 개수의 옵션 인수를 사용하여 숫자나 부동소수, 문자 등을 하나의 문자열로 출력할 수 있었다. printf()와 fprintf() 함수는 문자열을 스트림에 출력했지만, 메모리 버퍼에 출력할 수 있으면, 문자열로 변환을 printf() 함수처럼 할 수 있다는 것이다. 이를 실현하는 함수가 sprintf () 함수이다.

sprintf() 함수

int sprintf( char *buffer, const char *format [, argument] ... );

buffer에는 문자열의 출력이 되는 버퍼의 포인터를, format에는 서식 제어 문자열을, argument는 옵션 인수를 지정한다. format과 argument 내용은 printf() 함수와 완전히 동일하다. 첫번째 인수에 출력의 포인터를 지정하는 점에서 printf() 함수와 다르다. 이 함수를 사용하면, 본래 printf() 함수가 표준 출력에 표시하게 될 문자열을 버퍼에, 즉 충분한 저장 공간이 할당된 문자 배열로 출력할 수 있다.

sprintf() 함수가 있기 때문에 예상할 수 있지만 sscanf() 함수도 존재한다. 이 함수는 버퍼에서 입력을 수행한다.

sscanf() 함수

int sscanf( const char *buffer, const char *format [, argument ] ... );

이 함수도 buffer에 버퍼에 대한 포인터를 지정한다. format에는 서식 제어 문자열, argument에는 옵션 인수를 지정한다. 역시, sprintf()와 동일하게 첫번째 인수에 버퍼에 대한 포인터를 지정하는 점을 제외하고 scanf() 함수와 동일하다.

코드4

#include <stdio.h>

int main() {
    char str[256];
    sprintf(str , "오늘은 %d년 %d월 %d일이다." , 2017 , 11 , 26);
    printf("%s\n" , str);
    return 0;
}

코드4는 sprintf() 함수를 사용하여, 지금까지 표준 출력에 문자열을 표시해 온 방법과 동일한 방법으로 문자열 편집을 실시하고 있다. 이 방법이라면 수치나 다른 문자열 등을 일괄적으로 문자열로 변환할 수 있다. 마찬가지로, 문자열을 숫자 등으로 변환하는 경우는 sscanf() 함수를 사용하기만 하면 된다.

+ Recent posts