C언어 기초 – 문장과 블록 – C언어 강의 10

C언어 기초 – 문장

문장(statement)은 C언어를 구성하는 기본 요소입니다. 소스코드의 한 줄(line) 한 줄은 문장으로 구성되어 있습니다. 보통 코드 한줄이 하나의 문장으로 알고 있는데 C언어에는 틀린 말입니다. ; 세미콜론이 문장을 나누는 기준입니다.

예를 들어 아래처럼 한 줄에도 세미콜론 ; 을 사용해서 두 개의 문장을 넣을 수 있습니다.

int x = 10; printf("x is %d\n", x);

가장 간단한 문장은 널 문장입니다 ; 는 문장입니다.

아래의 코드는 다소 지저분하지만 컴파일은 잘 됩니다. 널문장은 아무것도 하지 말라는 뜻 입니다.

;
;
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
printf("null statement");

아래와 같은 문장은 뭔가 쓸만한 결과는 없지만 컴파일이 됩니다.

7;
4+7;

의미를 갖지 않기 때문에 이런 코드를 작성할 일은 없을 것 입니다. 코드가 의미를 갖는 다는 것은 어떤 변수를 변경하거나 새로운 결과값을 만들고 혹은 입력을 받아서 출력하는 동작도 의미를 갖습니다. (이 과정에도 변수를 사용하지만)

의미라는 말은 사람과 기계에 다른 관점이 적용되지만 보통의 의미있는 문장이란 것은

C언어 문법 기초 에서 설명한 5가지 문장 형식인 선언, 대입, 함수, 제어, 널 중에 널(Null)문장을 제외한 것 입니다. 하나씩 알아보겠습니다.

선언문 (declaration statement)

선언문은 int x; 처럼 선언(declare)하는 문장입니다. 앞서 표현식(expression)에 관련한 내용에서 봤지만 표현식은 연산자와 피연산자의 조합으로 이루어지고 표현식을 조합하여 문장을 만들 수 있습니다. 하지만 선언문은 표현식 문장이 아닙니다.

아래의 선언문으로 알아보겠습니다. int는 정수형입니다. 시스템마다 크기가 다르지만 일반적으로 32bit 부호있는 정수형이라 보면 됩니다. x는 변수의 이름입니다. 이름을 짓는 것은 프로그래머의 재량입니다. 식별자 규칙을 따르고 전역과 지역변수를 구분하는 것이 필요하겠지만 어쨋든 컴파일러가 허용하는 범위에서 이름을 짓습니다. 이 선언문이 하는 일은 32비트의 정수형 타입을 저장할 메모리 주소를 얻어와서 x라는 변수이름에 연결시키는 것 입니다. x는 지역변수를 모아놓은 테이블에 등록이 되고 실행시간 도중에 함수가 유지되는 동안 값을 변경하며 사용할 수 있습니다.

프로그래머는 이후의 소스코드에서 x라는 이름의 저장 공간을 사용할 수 있습니다. 여기에는 딱히 연산이란게 들어가지 않기 때문에 표현식이 필요없습니다. 컴파일러가 내부적으로 공간을 할당하는 일을 할 뿐입니다.

int x;

표현식은 값을 갖는다고 했는데 선언문은 어떨까요? 아래 문장은 컴파일 에러가 납니다. expected expression before ‘)’ token -> ) 토큰 앞에는 표현식이 와야합니다. 선언문은 표현식이 아니고 값을 가지지 않습니다.

printf("%d", (int a));

대입문, 할당문(assignment statement)

대입문 혹은 할당문을 = 을 사용합니다. 수학에서의 = 은 양변이 같다는 표현이지만 프로그래밍에서는 오른쪽의 값을 왼쪽에 대입한다, 할당하는 것 입니다. 앞서 표현식 부분에서 lvalue와 rvalue에 대한 설명을 했습니다. 대입문을 표현할 때 rvalue 값을 lvalue 에 대입한다고도 말할 수 있습니다.

기본적으로 = 가 들어간 문장은 대입문이라고 할 수 있습니다. 아래 문장의 의미는 쉽게 알 수 있습니다.

int var;
var = 10 + 2;

조금 다른 문장을 보겠습니다. 아래의 첫문장 int var = 10; 은 선언과 동시에 대입을 한 문장입니다. 선언문과 대입문이 하나의 세미콜론 ; 안에 들어있습니다. 다음 문장인 var++; 의 실제 의미는 var = var + 1; 로 자기 자신에게 1을 더하는 증가 연산자를 사용했습니다. 연산을 하니까 표현식이고 세미콜론이 있으므로 문장입니다.

int var = 10;
var++;
printf("%d\n",var);

부작용 (side effect)

이게 조금혼란스러울 수 있는데 부작용이란 C가 표현식을 평가할 때 변수나 객체가 변경되는 결과를 말합니다. a = 7 + 4 에서 컴파일러는 7+4의 표현식을 평가합니다. 여기서 표현식의 값인 11을 a에 대입하는 것을 부작용(side effect)라고 하는데 이상하게 보이는 표현이지만 C의 주된 목적을 표현식을 평가하는 것으로 보면 이로 인해 변경되는 것은 부가적인 것으로 해석할 수 있습니다. 표현식을 평가할 때 이 값으로 무엇을 변경할 것인지는 고려하지 않기 때문입니다.

시퀀스 포인트(sequence point)

C언어의 시퀀스 포인트(Sequence point)는 세미콜론 ; 으로 표시하는데 시퀀스 포인트가 나오면 이전까지의 모든 부작용의 평가가 끝납니다. 증감연산자에는 전위 모드와 후위 모드가 있는데 시퀀스 포인트에 대하여 알 수 있습니다.

int a;

a = 3;

printf("prefix: %d\n", ++a);
printf("a: %d\n",a);

printf("postfix: %d\n", a++);
printf("a: %d\n",a);

결과값은 아래와 같습니다.

prefix: 4
a: 4
postfix: 4
a: 5

prefix한 printf 에서는 a가 함수의 인자로 사용되기 전에 증가된 값을 반영하고, 두번째 postfix 에서는 인자로 사용될 때는 기존 값을 사용한 후 시퀀스 포인트 ; 가 종료되기 전에 부작용을 반영합니다. (a = a + 1)

세미콜론 ; 은 앞쪽의 모든 부작용이 처리되는 기준점입니다. 증감연산자는 값을 사용하기 전에 증가시키거나 사용한 후에 증가시킨다고 말하는데 정확한 시점은 시퀀스 포인트를 기준으로 결정됩니다.

한 가지 주의할 점은 int x = 2; (x++) + (x++); 와 같은 문장의 결과는 예측하기 어렵습니다.

int x = 2;

printf("%d\n", (x++)+(x++));
printf("x: %d\n", x);

결과 값은 아래와 같은데 첫번째 문장의 값이 어떻게 도출되었는지 알 수가 없습니다. x++ 는 x가 시퀀스 포인터 ; 를 만나기 전에 1을 올릴 것이란 것은 확실하지만 어디서 어떻게 올릴 건지는 알 수가 없습니다. 첫번째 printf 에 4가 아닌 5가 출력되고 다음 문장에서는 다시 4로 돌아오는데 이런 문장은 사용하지 않는게 좋습니다. 이런 복잡함을 피하기 위해서 증감연산자를 아예 사용하지 않는 파이썬 같은 언어도 있습니다.

5
x: 4

제어문(conditional statement)

제어문은 while, for의 루프문과 if 분기문 등 프로그램의 흐름을 제어하는 문장입니다. 제어문은 조건 + 단일문으로 이루어질 수도 있고 대개는 복합문(블록)으로 만들어 집니다. 다음 설명에서 예제를 보겠습니다.

블록, 복합문

블록, 복합문(compound statement)은 두 개 이상의 문장들이 중괄호 { } 로 묶여 있는 것을 말합니다. 예제를 보겠습니다.

while 문은 복합문을 사용한 제어문입니다. while 루프가 돌아갈 때 블록안에 있는 2개의 문장을 실행시킵니다. 이 복합문을 루프하는 것 이지요. while문은 블록을 단위로 하는 하나의 복합문입니다.

int count = 0;

while(count < 5)
{
    printf("count: %d\n", count);
    count++;
}

중괄호를 그냥 감싸면 복합문이 되는데 하나의 지역을 표시한 것 입니다. 이 안에서 선언하면 지역변수가 됩니다. 아래 코드를 보면 중괄호 안에서 선언한 변수는 바깥에서 사용할 수 없습니다. 컴파일러는 com_var 가 선언되지 않았다고 에러메시지를 출력합니다.

    int main_var = 7;

    {
        int com_var = 14;
        printf("%d\n", com_var);
    }

    printf("%d\n", main_var);
    // printf("%d\n", com_var);

마무리

C언어 문장(statement)의 구분은 시퀀스 포인트 ; 로 합니다. 문장의 종류에 따라 표현식의 사용여부가 다릅니다. 따라서 부작용에 의한 값이 있을 수도 없을 수도 있습니다.

문장은 프로그램에 내리는 하나의 독립된 지시사항 입니다. 문장 안에는 여러가지 연산자, 피연산자, 명령어들이 들어가게 됩니다. 한 문장에 단순한 명령어만 넣을 수도 있지만 복잡한 명령어도 하나의 함수를 사용한 문장으로 줄일 수 있습니다. 추상화 단계가 올라갈 수록 코드는 짧아집니다.

사람이 사용하는 문장이 아니라 컴파일러가 사용하는 문장의 특징을 잘 파악하도록 합니다. 문장은 가장 기초가 되니까 여기서 중요성을 한 번 강조해둡니다.

외부참고문서

C Programming/Side effects and sequence points

Statements – cppreference.com

Leave a Comment