52 – 변수 기억 클래스(자동 변수)

자동 변수는 함수 안에서 정의되는 것으로, 그 함수 안에서만 사용할 수 있는 지역 변수다. 함수가 실행되기 시작하면 메모리에 변수가 생성되고 함수 실행이 종료되면 사라진다. 그러므로 지금까지 살펴본 예제에서 함수 내에 선언한 변수는 모두 자동 변수이다.

자동 변수는 다음과 같이 기억 클래스 자리에 ‘auto’로 선언하면 되는데, 일반적으로는 auto를 생략해서 쓴다.

void func(void)
{
auto int i;
}

49 – 함수 호출

함수는 반드시 호출되어야 실행되는데, 함수 호출은 함수 정의 부분이 실행되도록 하는 일종의 명령어로 매개변수를 통해 값을 주고받고 반환하는 값을 전달받는다. 호출 방법은 다음과 같다.

함수이름 (매개변수들);

함수를 호출할 때는 함수 이름을 사용하고, return형은 기재하지 않는다. 그리고 소괄호 안에 함수가 실제로 존재하는 곳으로 전달하고자 하는 값(변수, 상수)를 기재하는데, 이는 없을 수도 있고 여러 개일 수도 있다. 함수 호출에 대해서는 다양한 예가 있다.

func();
func(2, 5);
i = func(2, 5);
printf(“result: %d\n”, func(2,5));

함수는 여러 번 호출할 수 있는데, 반복문을 통해서 호출을 여러 번 할 수도 있고, 필요할 때마다 호출해서 사용하면 된다.

 

70 – 문자열 판별 함수

문자를 처리하기 위해서는 먼저 입력받은 값이 어떤 문자에 속하는지를 알아야 하는데, 이 부분을 아래와 같이 직접 구현할 수도 있지만, 문자 판별용 함수가 존재한다. 아래의 예시는 입력된 문자가 영문자인지 판별해서 영문자 개수를 세어 주는 예시를 만들었다.

20170422_234248

표준 라이브러리에서 영문자를 판별하는 isalpha 함수를 제공하고 있으므로 직접 작성한 예시에서 ch에 저장된 문자가 영문자인지를 판별하는 부분이 다음과 같이 바뀔 수 있다.

if(isalpha(ch))

프로그래밍이 엄청 쉬워졌다. 이래서 표준 라이브러리 함수들을 찾아서 쓰는 것이다.

문자 판별 함수들에 대해서는 목록과 같이 쭉 나와있다.

  • 함수이름 | 기능
  • isalnum | c에 저장된 문자가 영문자 또는 수치 문자인지 판별한다.
  • isalpha | c에 저장된 문자가 영문자인지 판별한다.
  • isascii | c에 저장된 문자가 ASCII 문자인지 판별한다.
  • isblank | c에 저장된 문자가 빈 공백(공백 문자 혹은 탭)인지 판별한다.
  • iscntrl | c에 저장된 문자가 제어 문자인지 판별한다.
  • isdigit | c에 저장된 문자가 수치 문자(‘0’ – ‘9’)인지 판별한다.
  • isgraph | c에 저장된 문자가 프린트 가능 문자(공백 문자 제외)인지 판별한다.
  • islower | c에 저장된 문자가 소문자인지 판별한다.
  • isprint | c에 저장된 문자가 프린트 가능 문자(공백 문자 포함)인지 판별한다.
  • ispunch | c에 저장된 문자가 구획 문자(공백, 영 수치 문자가 아니면서 출력 가능한 문자)인지 판별한다.
  • isspace | c에 저장된 문자가 공백 문자, 종이넘김, 탭, 복귀문자인지 판별한다.
  • isupper | c에 저장된 문자가 대문자인지 판별한다.
  • isxdigit | c에 저장된 문자가 16진수 수치 문자인지 판별한다.

여기 적은 함수들의 사용법은 같다. (자세한 형식은 직접 구글에 검색해보면 나온다.) 이 중에 isalpha 함수를 사용해 키보드로 입력받은 문자가 영문자인지를 판별해서 영문자만 별도로 출력하는 예시를 작성해보았다.

69 – 문자, 문자열 처리 함수

사실 일반 사용자가 보기에는 윈도우 시스템에서는 영문 대소문자를 동일하게 취급하는 것 같지만 사실 내부적으로 대문자를 소문자로 변환해 처리해준다. 그리고 리눅스 시스템에서는 프롬프트에서 exit를 입력하면 셀이 종료되며 로그아웃하게 된다. 이는 프롬프트를 통해 입력받은 문자열이 exit면 셀 프로그램이 종료되도록 구현되어 있기 때문이다.

이와 같이 문자, 문자열의 처리는 프로그램에서 중요한 역할을 차지한다. 그래서 라이브러리에서 매우 다양한 문자, 문자열을 처리하는 함수들을 제공하는데, 대표적인 함수들을 하나씩 예시로 해보려고 한다.

68 – 리눅스 C 라이브러리 함수

우리는 이전에 쓰는 예제에서 라이브러리 함수를 썼다. 대표적인 것이 바로 printf랑 scanf다. 이 둘은 표준 입력으로 데이터를 입력받고 표준 출력으로 데이터를 출력하는 기능을 하는 함수로 사용자들의 편의를 위해 시스템에서 미리 만들어 제공하는 것이다. 만약 이런 함수가 없다면 입력받고 출력하는 기능을 지닌 프로그램을 작성하는 데 어려움을 겪고 있었을 것이다.

이와 같이 사용자들의 편의를 위해 시스템에서 만들어 제공하는 함수를 라이브러리 함수라 하는데 이런 라이브러리 함수를 제대로 활용하면 프로그램 작성도 쉬워질 뿐 아니라 프로그램이 효율적인 성능을 내게 된다.

다른 C 교재들이나 강의에서는 이런 것들을 포인터나 함수, 구조체 설명하기 전에 먼저 쭉 설명해서 이것저것 짜는 것에 집중하는데, 내가 작성하는 글에서는 이런 라이브러리 함수들은 한번에 설명해서 진행하려 한다.

67 – 전처리기(조건부 컴파일)

특정 조건이 성립할 때나 조건이 성립하지 않을 때 지정한 범위 내의 문장을 컴파일하거나 무시하고 컴파일하지 않는 것이 바로 조건부 컴파일이다. 조건부 컴파일의 전처리기 지시자에는 #if, #elif, #else, #endif, #ifdef, #ifndef 등이 있다.

일단 제일 많이 사용되는 #if, #elif, #else, #endif를 보자. 형식은 아래와 같으며, 수식 1이 참이면 실행 1을 컴파일하고, 수식 1이 거짓이고 수식 2가 참이면 실행 2를 컴파일한다. 모든 수식이 거짓이면 실행 n을 실행한다.

#if 수식 1
실행 1
#elif 수식 2
실행 2
……
#else
실행 n
#endif

if, else if의 사용과 같은 느낌이 들지 않는가? 사용이 똑같다. 전처리가 되어 있다는 것으로 인해 컴파일하는 코드의 길이가 달라질 수 있기 때문에 특정 환경, 특정 옵션에 맞는 프로그램을 개발하고자 할 때 많이 쓴다. C로 만들어진 프로그램에서 설치 옵션이나 실행 옵션 등이 있는 경우에 많이 이용하고, 운영체제 같은 큰 프로그램을 짜면 반드시 보게 될 것이다.

이건 아래의 예시를 보면 더 쉽다. 설명하지 않은 #ifndef가 있는데, 이건 정의되지 않았을 경우를 나타낸다. NAME이 정의되어 있기 때문에 NAME이 정의되었다는 문장이 출력되고 ADDRESS는 정의되지 않았기 때문에 #ifndef에 맞춰 정의되지 않았다는 문장이 출력된다.

66 – 전처리기(파일 삽입)

파일 삽입 기능은 그 위치에 다른 파일을 삽입하기 위해 사용되는 기능이다. 우리는 매번 썼다. #include라고 매번 작성하였다. 이 작성문은 두 가지 형태고 작성된다.

#include <파일이름>
#include “파일이름”

우선, <파일이름>이라고 지정된 것은 해당 파일을 시스템 디렉토리에서 찾는다. 반면에 “파일이름”이라고 지정하면 파일을 우선 현재 작업하고 있는 디렉토리에서 찾고, 없을 경우에는 시스템 디렉토리에서 찾는다. 그래서 귀찮으면 그냥 “파일이름”이라고 통일해서 적기도 한다.

파일 이름에는 대부분 h라는 확장자를 붙이며, 이것을 헤더 파일(header file)이라고 한다. 그러면 이에 대한 명확한 이해를 돕기 위해 예시를 작성하였다. 예시를 보고 나서 설명을 하도록 하겠다.

<stdio.h> 헤더 파일은 사용자의 편의를 위해 시스템에서 미리 만들어 제공하는 파일로써 누구나 이용할 수 있는 파일이다. 반면 “my.h”과 같은 파일은 사용자가 직접 작성한 파일이다. my.h 파일을 아래와 같이 작성하였다.

다른 파일에서도 my.h를 파일 삽입으로 삽입하면 저기 정의된 것들을 그대로 쓸 수 있다.

전처리기로 파일 삽입을 진행하였기 때문에 컴파일 할 때에는 별도로 여러 파일을 정의하지 않아도 된다. 기존의 예시에 gcc [파일] [파일] 과 같이 해서 두 개 이상의 c 파일을 컴파일 한 적이 있는데, 이건 아니다.

65 – 전처리기(매크로 정의)

#define을 이용하여 기호 상수 PI를 정의하는 것을 이전에 한 번 예제로 보여줬는데, 바로 그것이 매크로다. 즉, 프로그램을 작성할 때 반복 기술되는 명령어나 수식이 사용자가 별도의 이름을 부여할 수 있는 기능이 매크로다. 매크로에는 매개변수를 사용하지 않는 경우와 매개변수를 사용하는 경우가 있다. 정의 형식이 다른데, 형식은 다음과 같다.

#define 매크로이름 치환문자열
#define 매크로이름(매개변수들) 매개변수를포함하는치환문자열

배개변수를 사용하는 매크로는 예시를 보면 좀 알 수 있을 거 같다.

#define ADD(A, B) ((A) + (B))

ADD는 매크로 이름, A랑 B는 매개변수, ((A) + (B))는 치환 문자열이다. 여기서 주의할 사항은 매크로 이름인 ADD와 매개변수의 목록인 A,B 사이를 띄우면 안된다는 점이다.

일단 예시를 보도록 하자. 매개변수가 없는 매크로의 예시는 다음과 같다.

전처리를 하여 문장 중의 매크로가 해당 문자열로 바뀐 상황이다.

다음은 매개변수가 있는 매크로를 이용해 두 수 중에 큰 수를 구하는 프로그램이다.

매개변수가 있는 매크로는 함수와 거의 유사하지만, 컴파일 할 때 링크 과정을 거치지 않아도 되므로 함수를 불러오는 시간이 절약되는 장점이 있다.

이런 매크로를 정의하는 지시자뿐만 아니라 이미 정의된 매크로를 해제하는 지시자도 있는데, 바로 #undef문이다. 예시를 보도록 하자. 먼저 정의했던 SIZE 매크로를 실행 도중에 #undef로 해제한 다음에 다시 #define으로 정의하는 코드가 있다. 그로 인해 실행 결과가 바뀐다.

64 – 전처리기

사용자가 작성한 프로그램을 실행하기 위해서는 컴파일러로 소스 파일을 실행 파일로 변환해야 한다. C언어에서는 컴파일하기에 앞서 #으로 시작되는 전처리기 지시자 부분을 먼저 처리한다. 이러한 일을 하는 것이 전처리기(preprocessor)로, 프로그램에서 사용자 임의로 수식이나 상수를 정의하거나 외부 파일을 삽입하는 등의 작업을 한다.

전처리해야 하는 전처리기 부분은 #으로 시작하고 문장 끝에는 세미콜론(;)을 붙이지 않는데, 이런 전처리기는 어떤 종류가 있는지, 정의는 어떻게 하는지를 정리해 나가도록 하겠다.

63 – 공용체

공용체는 구조체와 형식이 동일하지만 구조체와 달리 공용체 내에 있는 각 맴버가 동일한 기억장소를 공유하면서 이를 경우에 따라 다른 용도로 사용한다. 그러므로 공용체는 필요한 유형의 변수에 따라 다르게 사용될 수 있지만 한순간에 하나의 맴버만 사용할 수 있다.

union position {
int class;
char department[10];
};

공용체는 이 예처럼 union 키워드를 이용하여 만든다. position은 태그 이름이고, class와 department라는 두 개의 맴버로 구성되어 있다. 선언하는 방식도 구조체 선언하듯이 하면 된다.

union position my;

그리고 공용체도 구조체처럼 정의와 변수 선언이 동시에 가능하다.

여기서 차이가 어떤 것이 있는지를 보여주도록 하겠다. 동일한 구조의 구조체가 있다고 하면, 해당 구조체의 구조가 어떤 식으로 다른 구조로 되어있는지를 보여주면 다음과 같다.

공용체형으로 만들게 되면 구조체형과 달리 동일한 기억장소에 공유하게 되어 두 개의 변수 중 하나만을 사용할 수 있도록 되어 있다. 그에 반해 구조체에는 두 개의 변수를 둘 다 별도의 기억장소에 저장하게 되며, 둘 다 사용할 수 있다.

이런 공용체를 만드는 이유가 있다. 공용체는 한정된 기억장소를 절약하기 위해 사용되는 구조이다. 즉, 값으로 들어가는 맴버가 여러 형태가 존재하나 그 여러 맴버가 동시에 사용될 일이 없는 구조에 이용된다. 이걸 예시로 만들어본 것이 아래의 예제이다. 고등학생일 경우에는 사실 반으로 따지지만 대학생이라면 전공으로 따지게 된다. 그런 것을 예시로 하여 직접 예시를 작성해봤다. 단, 값은 어떤 것이 들어가도 된다는 걸 보여주기 위해 임의로 막 넣었다.