이전에는 gcc 컴파일 시 발생하는 경고에 대해서는 무시했었다. 에러가 아닌이상 돌아가는 경우가 많았으니까. 그러나 이제는 그 원인 하나하나에 해결해서 깔끔하게 아무런 경고 메세지가 출력되지 않는 것을 추구하고 있다. 이런 이유에서 자주 무시하고, 해결 방법을 알아보지 않았던 경고 메세지 하나를 소개하고, 해결 방법을 적어본다.


unsigned int의 출력을 위한 포맷: %zu


 


 위 경고 메세지가 발생한 원인은 sizeof 함수에 있다. 소스코드를 굳이 포함시킬 필요가 없어서 배제했으나, 그 코드 내에서는 구조체를 선언하고, 그 구조체의 크기를 출력하기 위해 sizeof 함수를 사용했다. 사이즈를 %d 포맷으로 출력을 시도하는데, 이 때 경고 메세지가 발생한다. 이를 해결하기 위해서는 sizeof 연산의 출력을 위한 포맷으로 %zu를 사용하면 된다. 아래의 코드를 참조하자.


printf("size: %zu\n", sizeof(pos1));


 간혹 리눅스 터미널 환경에서 C언어로 코드를 작성하다보면, 뜻밖의 상황에서 에러를 맞이하게 된다. 비록 치명적이지 않고 간단한 에러임에도 불구하고, 매번 발생 원인을 잊고서 구글에 검색하는 경우가 잦은데, 이런 상황을 모면하고자 생기는 문제점을 기록하려한다. 간단한 문제인 만큼 추후에 기억하기 쉽고, 내 블로그를 참조할 누군가를 위해 간단하고 간략하게 작성할 참이다.



gcc 컴파일 시 <math.h> 헤더 파일 링크가 안 될 경우


 


 코드상에 별다른 문제가 없음에도 헤더파일 인식이 안되서, 위와 같은 에러가 발생하는 경우가 있다. 필자의 경우 <math.h>를 포함시켰는데도 불구하고 sqrt 함수가 정의되지 않았다고 나온다. 이는 <math.h> 헤더 파일에만 해당하는 경우는 아니므로, 정상적으로 코드 내 헤더 파일을 include 시켰음에도 특정 라이브러리 호출이 안된다면, 아래와 같은 명령어로 해결하자.


gcc -o "filename" "filename.c" -lm


 동일한 gcc 컴파일 명령어 뒤에 -lm을 추가함으로써 해결할 수 있다. gcc 컴파일러는 디폴트로 mathematical 라이브러리를 링크할 당시 포함시키지 않는다고 한다. 따라서 -lm 명령어를 통해 추가해줘야 한다.

 이번 포스팅은 C언어에서 자주 사용되는 문자열 관련 함수에 대해서 정리를 하려고 한다. Java 함수와는 다르게 유독 C언어 함수에 대해서는 자주 쓰는 것들도 암기가 잘 안되고, IDE를 통해 개발하기 보단 vim을 통해 작성하다보니 자꾸만 인터넷 검색을 하게된다. 물론 모르는 내용을 구글링을 통해 알아가는 것은 당연하지만, 반복적으로 같은 정보를 기억하지 않고 검색에만 의존하다보니 시간도 많이 잡아먹는 것 같아, 이 기회에 한 번 정리하고자 마음먹었다.



1. putchar, fputc, getchar, fgetc


 위 함수들 모두 문자열이 아닌 '문자'를 입력하고 출력하는 함수들이다. putchar와 fputc는 출력 스트림을 표준 출력으로 할 지, 아니면 사용자가 지정할 것인지의 차이를 보인다. 이 점과 마찬가지로 getchar와 fgetc의 차이 역시, 입력 스트림을 표준 입력으로 할 지, 사용자가 지정할 것인지의 차이가 있다. 특이점은 리턴 타입이 int이므로 이점을 기억하여 사용해야 한다. 그리고 getchar의 경우 버퍼에 남아있는 개행 문자를 처리할 때도 사용한다는 점을 기억하자. 


 해당 함수를 이용할 때 알아두어야 할 내용 중에 하나는 EOF에 관한 것이다. End of File의 약자로, 파일의 끝까지 와서 더이상 읽어들일 내용이 없다는 뜻이다. 키보드 입력의 경우 Ctrl+D, Ctrl+Z 입력이 EOF를 의미하게 된다. 실제로 위 두 함수에서 반환하는 EOF는 -1로 정의된 상수임을 잊지 말자.



2. puts, fputs, gets, fgets


 위 함수 중 문자열 입력 함수인 gets, fgets가 scanf 함수와 보이는 가장 큰 차이점은 공백이 있는 문자열을 입력받을 수 있다는 점이다. 더불어, puts, fputs의 차이 및 gets, fgets의 차이점은 위에서 언급한 입/출력 스트림을 지정할 것인가, 아니면 표준 입/출력 스트림을 사용할 것인가의 차이다. 추가적으로 puts 함수는 자동으로 개행을 진행하지만, fputs 함수는 자동 개행을 하지 않는다. gets 함수를 사용함에 있어서 미리 마련된 배열의 길이를 넘어서는 문자열이 입력되면, 할당되지 않은 메모리 영역을 침범할 수 있는 우려가 있다. 따라서 가급적이면 fgets 함수를 사용하는 것이 유익하다. 그러나 fgets 함수에도 주의해야 할 점이 있다. fgets 함수를 사용하면 특정 사이즈만큼 입력받을 수 있는데, 문제는 맨 마지막에 Null 문자를 포함하기 때문에 지정한 크기보다 하나 작은 길이만큼 읽어들인다. 또, fgets 함수는 개행 문자를 만날 때까지 문자열을 읽어들이는데, 개행문자까지 문자열에 포함시킨다.



3. fflush


 출력 버퍼를 비우는 함수. 출력 버퍼를 비운다는 것은 출력 버퍼에 저장된 데이터가 목적지로 전달되는 것을 의미한다. 그러나 입력 버퍼를 비운다는 것은 전혀 다른 내용이다. 입력 버퍼의 비움은 곧 데이터의 소멸을 의미한다. 특히 입/출력 동작중에 유독 엔터-키로 인해서 개행문자가 삽입되는 바람에 출력이 원하는 형태로 되지 않는 경우가 있는데, 위에 1번에서 언급했던 것과 같이 getchar 함수를 활용하면 이런 문제를 해결할 수 있다. 아래의 함수를 참조하자.


void EmptyOutputBuffer(void) {

while(getchar != '\n');

}



4. strlen


 문자열의 길이를 출력해주는 함수다. 반환형이 unsigned int를 대변하는 size_t 타입이라서 출력할 때 경고 메시지가 뜨는 경우가 다분하다. %d가 아닌, %zu 형식을 사용하면 해결된다. 뒤에 따라오는 개행문자를 제거하고, 순수하게 유효한 문자열만 입력받는데 사용되기도 한다. 아래의 함수를 참조하자.  strlen 함수를 이용하여, 문자열의 길이를 파악한 후 맨 마지막 문자열을 개행문자에서 Null로써 대체하고 있다.


void PureString(char str[]) {

int len = strlen(str);

str[len-1] = 0;

}



5. strcpy, strncpy


 문자열 복사에 사용되는 함수다. strncpy는 특정 길이를 명세하여, 명세된 길이까지 복사한다. 그러나 이 함수를 사용하기 위해서는 주의해야할 점이 있다. strncpy 함수는 딱 정해진 크기만큼 복사하는데, 그 안에 Null 문자를 포함하지 않을 수 있다. 따라서 출력 해보면 이상한 메모리 위치에 접근하여, 원하는 출력이 안될 수 있다. 따라서 해당 함수를 사용할 때는 아래 코드처럼 우회해서 사용하는 편이 안전하다. 아래처럼 문자열의 마지막 위치에 강제적으로 Null을 삽입될 수 있도록, 하나 작은 값까지 복사를 진행하자.


strncpy(str2, str1, sizeof(str2)-1);

str2[sizeof(str2)-1] = 0;



6. strcat, strncat


 문자열을 이어붙여주는 함수다. 기억할만한 점은 앞 문자열의 Null 문자 위치부터 덧붙여진다는 점이다. 따라서 사용자는 한 문자열에 두 개의 Null 문자가 생길 수 있는 가능성을 무시하고 사용할 수 있다. strncat의 경우, 덧붙일 문자열의 크기를 지정할 수 있는데, 해당 크기에는 Null 문자가 포함되어 있지 않다. 만약 10을 인수로 전달했다면, 10개의 문자열과 Null 문자열이 덧붙여져서 총 11개의 문자가 추가된다.



7. strcmp, strncmp


 두 문자열이 동일한 지, 아닌지를 판별할 때 사용한다. 같으면 0을 반환하고, 0이 아니면 다른 문자열이라고만 기억해도 충분할 것 같다. strncmp를 사용할 일은 거의 없을 것 같다. 



8. atoi, atof, atol


 문자열을 int, double, long으로 변환해주는 함수. 앞서 소개한 함수들은 <string.h>에 선언된 함수지만, 이 함수들은 <stdlib.h>에 선언되어 있다.



9. 마치며...


 위 함수들은 개발함에 있어서 정말 자주 사용되는 함수들이다. 반환형이나 인수 타입을 암기해서, 검색하는데 소모되는 시간을 절약하도록 하자. 특히 각 함수들이 가지고 있는 기본 기능외에 기억해야할 점이 있다. 대부분 개행문자와 관련된 점 또는 Null 문자와 관련된 점인데, 이런 부분을 정확하게 숙지해야만 문자열 입/출력을 다루는 데 문제 없이 능숙하게 처리할 수 있을 것 같다.



10. 참고


 열혈 C 프로그래밍 [윤성우 저]