안녕하세요 피터입니다.

오늘은 C언어로 2진수를 출력하는 예제를 소개해드리겠습니다.


개요

일상 생활에서 우리가 다루는 숫자는 10진법을 사용합니다.

하지만 컴퓨터의 세계는 0과 1로 이루어져있죠.


경우에 따라서는 10진수로 되어 있는 숫자를 2진수로 변환애서 출력해볼 필요가 있습니다. (일반적인 경우에는 16진수로 출력하겠지만요!)


이번에 소개드릴 예제는 양수 또는 음수의 10진수를 입력받아서 2진수로 출력하는 예제입니다.


당연한 얘기겠지만 C언어의 비트 연산자를 사용합니다.

연산자에 대한 내용은 아래 글을 참조해주세요.

[C언어 강좌-7] 연산자 (Operator)


예제 소스

#include 
int main()
{
	int input = 0;
	int mask;

	while (true)
	{
		printf("Input Number(Decimal: if you input zero then exit): ");
		scanf_s("%d", &input);

		if (input == 0)
		{
			break;
		}

		for (int i = 31; i >= 0; i--)
		{
			mask = 1 << i;
			printf("%d", input & mask ? 1 : 0);


			if (i % 8 == 0)
				printf(" ");
		}
		printf("\n");
	}

    return 0;
}

사용자의 입력을 scanf 함수를 이용해서 input 변수에 담습니다.


for 구문에서는 mask 변수를 이용하여 맨 왼쪽 비트(bit)부터 순차적으로 한글자씩 출력합니다.

현재 자리수만 1이고 나머지가 0인 mask과 input 값과 & 연산(and 연산)을 하면 현재 자리수의 값이 0인지 1인지 알 수가 있습니다.

이때 8 비트 단위로 한칸 띄어쓰기를 해줘서 가독성을 높입니다.


실행 결과

Input Number(Decimal: if you input zero then exit): 100
00000000 00000000 00000000 01100100
Input Number(Decimal: if you input zero then exit): 255
00000000 00000000 00000000 11111111
Input Number(Decimal: if you input zero then exit): -200
11111111 11111111 11111111 00111000
Input Number(Decimal: if you input zero then exit): 65535
00000000 00000000 11111111 11111111
Input Number(Decimal: if you input zero then exit):

컴퓨터는 음수를 표현할 때 2의 보수 형태로 표현합니다.
2의 보수란 값의 모든 비트를 반전(0은 1으로, 1은 0으로) 시킨 후 1을 더한 값입니다.

그렇기 때문에 값이 음수인지 양수인지 알기 위해서 맨 앞자리 비트실제 값으로 사용하지 않고 부호 비트(sign bit)로 사용됩니다.

만일 맨 앞자리 비트를 부호 비트가 아닌 값으로 사용하고 싶을 때에는 unsigned 로 선언하면 됩니다.

아래처럼요.

unsigned int a;




-Peter의 우아한 프로그래밍

여러분의 댓글은 저에게 크나큰 힘이 됩니다. 오류 및 의견 주시면 감사하겠습니다.

블로그 이미지

친절한 Peter Ahn

IT 정보 공유, 프로그래밍 지식 공유

댓글을 달아 주세요


안녕하세요 피터입니다.

오늘은 C언어의 함수 (Function)에 대해 알려드리겠습니다.


개요

수학에서는 함수 이런식으로 표기합니다.


y = f(x)

여기에서 x 는 함수의 입력값이 되고, y 출력값이 됩니다.


C언어에서 함수(Function)도 이와 유사하다고 볼 수 있습니다.

입력값을 가질 수 있으며, 이에 대응되는 값을 출력할 수 있습니다.



문법(Syntax)

함수 선언 (Function Declaration)

함수는 다음과 같이 선언할 수 있습니다.


DataType FunctionName(DataType, ...);


위와 같은 문장(문장이기 때문에 ; 세미콜론으로 끝납니다)을 함수 선언 또는 함수 원형이라고 합니다.

맨 앞쪽에 있는 DataTypeReturnType이며 함수의 출력값입니다.

FunctionName에서 함수 이름을 지정할 수 있으며, 함수 이름 뒤의 ( ) 안에는 0개 이상매개변수(Parameter)를 넣을 수 있습니다.

각각의 매개변수들은 , (콤마)로 구분하며 개수의 제한은 없습니다.

다만 너무 많은 매개변수를 넣으면 사용하기가 힘들겠죠.


함수 이름을 정하는 것은 변수 이름을 정하는 것 만큼이나 굉장히 중요합니다.

이름은 고유한 함수의 식별자로 방대한 소스코드 내에서 코드의 흐름(Flow)를 추적하는데 있어서 이정표와 같은 역할을 하기 때문입니다.


때문에 함수 이름이나 변수 이름을 대충 지었다가는 여러분의 코드를 보는 사람들로 하여금 코드의 미로에 갇혀 헤매이게 만들지도 모릅니다.

그러니 함수 이름을 정할 때는 의미가 잘 전달될 수 있도록 신중하게 결정해주시기 바랍니다.


올바르게 함수명과 변수명을 짓는 방법에 대해서는 추후에 포스팅하겠습니다.


함수 선언문 내에는 실제로 동작에 관련된 코드가 들어있지 않기 때문에 함수를 선언(Declaration)했다면 반드시 함수를 정의(Definition)해야합니다.

즉, 선언과 정의가 쌍을 이뤄야 합니다.


선언과 정의를 함수의 헤더(Header)바디(Body)라고도 부릅니다.


선언만 하고 정의를 않은 상태에서 해당 함수를 호출하게 되면 컴파일러에서 Link Error를 선물해줄겁니다.


이렇게 헤더와 바디를 구분해놓는 이유는 라이브러리의 경우 실제 동작 코드는 컴파일이 완료된 lib 파일이나 dll 파일 안에 존재하는데, 함수를 호출하기 위해서는 먼저 컴파일러(Compiler)에게 함수 원형을 알려줘야 합니다.

때문에 라이브러리 함수를 호출하기 전에 해당하는 함수의 원형을 선언해줘야 하는데 라이브러리에 포함된 함수가 한두개가 아니기 때문에 일일히 적기가 곤란합니다.

그래서 함수 원형만 따로 모아놓은 파일이 .h 확장자를 갖는 헤더파일(Header file)이고 이 파일만 #include로 불러오면 라이브러리의 함수를 모두 호출할 수 있게 되는 것입니다.


#include<stdio.h>

위 문장은 printf, scanf 등의 표준 입출력 함수들이 구현되어 있는 Standard Input / Output 라이브러리를 사용하기 위해 표준입출력 헤더파일을 포함한다는 의미입니다.


경우에 따라서는 함수의 선언과 동시에 구현을 하는 경우도 있습니다. 이러한 함수를 인라인 함수(Inline Function)이라고 합니다.

하지만 특별한 경우를 제외하고는 함수의 선언과 정의을 분리해서 사용하는게 좋습니다.


함수 정의

함수의 이름과 입/출력값을 지정하여 선언하였다면 이제 함수를 정의할 차례입니다. 다른 표현으로 함수를 구현한다고도 합니다.


DataType FunctionName(DataType, ...)

{

    // do something

    return value;

}


위와 같이 함수 원형 뒤에 { } 블럭에 실제 함수에서 수행할 코드를 작성해주면 됩니다.


함수 선언시에는 각 매개변수의 데이터 타입만 명시하고 이름을 생략할 수 있지만 함수 정의에서는 매개변수의 데이터 타입과 이름을 모두 명시해야 합니다.



// Declaration
int sum(int, int);

// Definition
int sum(int a, int b)
{
  return a + b;
}


위 예제는 두 개의 숫자를 매개변수로 입력받아서 두 숫자의 합을 반환하는 함수입니다.

이처럼 return 값은 특정 변수만 지정할 수 있는 것이 아니라 수식을 넣을 수도 있습니다. 그렇게 되면 수식식의 최종 결과값이 반환되게 되는 것이죠.


함수 호출

함수 정의까지 마쳤다면 이제 원하는 시점에 함수를 호출(Call) 할 수 있습니다.




아래와 같이 선언했던 함수명을 적고 ( ) 안에 매개변수에 들어갈 변수를 넣어주기만 하면 됩니다.

c = sum(a, b);

그러면 sum 함수로 프로그램이 점프(Jump)해서 실행을 하다가 return 을 만나면 다시 돌아오는 것이죠.
{ } 로 감싸여진 블록 마다 별도의 스택(Stack) 영역이 생긴다고 전에 언급 드린 적이 있었죠.

함수에서도 마찬가지입니다.
sum 함수가 호출되는 순간 sum 함수의 스택이 생성되고 스택 영역의 지역변수(Local variable)로 a와 b가 새롭게 할당됩니다.

그리고 main 스택에서 매개변수로 넣었던 a, b 변수의 값이 복사(Copy)되는데 이 부분이 중요합니다.

변수의 이름은 같지만 서로 다른 스택 영역에 존재하기 때문에 sum 함수에서 a, b 변수의 값을 아무리 변경하여도 main 스택 영역의 변수들의 값은 그대로입니다.

sum 함수의 } 부분에서 스택이 정리되면서 sum 함수 내에 할당되었던 변수 a, b는 소멸됩니다.

함수의 처리 결과를 main 스택에서 얻기 위해서는 반환값(return value)을 받는 수밖에 없습니다.

그런데 이 반환값는 한 개밖에 선언할 수가 없죠?

하지만 프로그램을 개발하다 보면 반환값이 여러 개가 필요한 경우가 종종 발생합니다. 이럴 경우에는 어떻게 해결해야 될까요?


첫 번째로는 구조체(struct)를 이용하는 방법이 있습니다.

반환값을 여러 개의 데이터 타입으로 구성된 구조체로 선언하면 원하는 만큼 다양한 결과값을 묶어서 반환할 수 있습니다.

구조체에 관련된 내용은 나중에 자세히 말씀드리기로 하고 오늘은 두 번째 방법을 소개해드리겠습니다.


바로 앞서 배웠던 포인터(pointer)를 이용해서 해결하는 방법입니다.


함수는 매개변수로 값을 그대로 넘기냐, 값에 대한 참조를 넘기냐에 따라서 다음과 같이 두 가지로 구분할 수 있습니다.

  • Call by value
  • Call by reference


위에 있는 sum 함수 예제는 전형적인 Call by value의 예입니다.


Call by reference를 구현하기 위해서는 매개변수포인터 변수로 선언해야 합니다.

sum 함수 예제를  Call by reference로 바꿔보겠습니다.



// Declaration
void sum(int, int, int*);

void main(void)
{
  int a,b,c;
  a = 10, b = 15, c = 0;
  sum(a, b, &c);
  printf("a + b = %d\n", c);
}

// Definition
void sum(int a, int b, int* c)
{
  *c = a + b;
}


int* 타입의 매개변수 c 를 선언하고 sum 함수를 호출할 때 변수 c주소를 넘깁니다.

그렇게 하면 매개변수 c에는 main 스택에 존재하는 변수 c 의 주소가 복사되겠죠.

결과적으로 sum 함수 실행 시 *c 와 같이 * 연산자를 이용해서 해당 주소의 실제 값을 변경하면 main 스택에 있는 변수 c 의 값이 변경되게 됩니다.

이러한 원리를 이용하면 매개변수의 개수에는 제한이 없기 때문에 원하는 만큼의 반환값을 함수로부터 받아올 수가 있습니다.



-Peter의 우아한 프로그래밍

여러분의 공감과 댓글은 저에게 크나큰 힘이 됩니다. 오류 및 의견 주시면 감사하겠습니다.

블로그 이미지

친절한 Peter Ahn

IT 정보 공유, 프로그래밍 지식 공유

댓글을 달아 주세요

 

 

개요

 

안녕하세요 피터입니다.

오늘은 C언어의 포인터(Pointer)에 대해 알려드리겠습니다.

 

포인터는 C언어를 배우는 많은 사람들이 어려워 하는 개념 중에 하나입니다.

동시에 C언어에서 가장 중요한 개념 중 한가지이기도 합니다.

 

사실 포인터(Pointer)라는 것은 하나의 데이터 타입(DataType)일 뿐이며 실제 값(Value)를 저장하는 대신에

값이 저장되어 있는 변수의 주소(Address)가 저장된다는 것만 기억하면 어렵지 않습니다.

 

 

문법(Syntax)

 

포인터 타입 정의 (Definition)

 

포인터 타입을 정의할 때는 기존에 정의된 데이터 타입 뒤에 * (Asterisk) 를 붙이면 됩니다. (애스터리스크 라고 발음합니다)

예를들면 다음과 같이 정의할 수 있습니다.

 

int*
char*
float*
double*
struct node*

 

기본형 데이터 타입 외에도 파생형 타입도 포인터 타입으로 만들 수 있습니다.

[C언어 강좌-4] 자료형 (DataType)

 

모든 포인터 타입의 변수에는 주소값이 저장되기 때문에 포인터 변수가 차지하는 메모리 크기는 모두 같습니다.

그렇기 때문에 원래 1 Bytechar 형의 데이터 타입도 char* 포인터 타입이 되면 4 Bytes의 크기를 갖게 되는 것입니다.

마찬가지로 8 Bytes의 크기를 차지하는 double의 경우에도 double* 포인터 타입이 되면 4 Bytes가 됩니다.

굉장히 중요하고 헷갈리기 쉬운 부분이니 잘 기억해두세요!

 

 

포인터타입으로 변수 선언 (Declaration)

 

포인터 타입으로 변수를 선언하는 방법은 기존의 변수 선언하는 방법과 동일합니다.

int* a; // or int *a;

 

이렇게 선언된 포인터 변수 a 는 4 Bytes 만큼 메모리 공간을 할당받게 되고 이 공간에 주소값을 저장할 수 있습니다.

일반적으로 변수를 할당하고 아무런 값을 셋팅하지 않으면 가비지(Garbage)값이 들어있기 때문에 초기화를 해줘야 합니다.

 

포인터 변수를 초기화 할 때는 보통 NULL (널) 값으로 초기화 합니다.

이것은 나중에 포인터 변수에 주소값이 할당되었는지 체크하기 위해서 아무런 주소값이 할당되지 않았다는 의미를 갖게됩니다.

따라서 포인터 변수에 접근(access)하기 위해서는 반드시 NULL 인지 체크하는 루틴이 필요합니다.

 

int* a = NULL;
if(a != NULL)
  printf("%d", *a);

NULL이 들어있는 포인터 변수에 접근하게 되면 NullptrException 이라는 무시무시한 에러를 만나실 수 있으니 이 부분은 습관처럼 익숙해지셔야 합니다.

 

NULL은 C에서 일반적으로 0으로 define 되어 있는데 메모리 주소 체계에서 0번지는 시스템 영역이므로 이러한 접근을 차단하기 위해서 Exception(예외)을 발생시키는 것입니다.

 

포인터 변수에 접근할 때에는 당연하게도 주소를 다루기 때문에 일반적인 변수와는 조금 다르게 취급해야 되겠죠?

포인터 변수에 값을 저장하거나 불러올 때는 아래와 같이 특별한 연산자가 사용됩니다.

 

 

& 연산자 (Operator &)

 

int b = 100;
a = &b;

& (Ampersand) 연산자는 변수 앞에 붙는 단항연산자입니다.

(앰퍼샌드라고 발음합니다)

이 연산자는 뒤에오는 변수의 주소값을 반환합니다.

따라서 위 코드의 의미는 int 형 변수 b 의 주소를 얻어서 포인터 변수 a 에 저장한다는 의미입니다.

 

 

* 연산자 (Operator *)

 

printf("address: %d, value: %d\n", a, *a);

* 연산자는 & 연산자와 마찬가지로 단항연산자이지만 정반대의 동작을 수행합니다.

뒤에 오는 포인터변수가 가리키고 있는 주소에 저장되어 있는 실제 값(Value)를 반환합니다.

 

따라서 포인터 변수 a에 int 형 변수 b의 주소가 저장되어 있다면 *a 는 b에 저장되어 있는 값을 의미합니다.

 

ex) 포인터 변수 a 에 int 형 변수 b의 주소를 저장한 뒤 주소값과 실제값을 출력하는 코드

int* a = NULL; // or int *a;
int b = 100;
a = &b;

printf("address: %d, value: %d\n", a, *a);

 

참고

 

Call by reference

 

이후 함수(function) 포스팅에서 자세히 다루게 될 내용이지만 간략하게 소개드리겠습니다.

함수의 매개변수(Parameter)를 포인터 타입으로 사용하게 되면 해당 함수의 외부에서 선언된 변수에 직접 접근하여 값을 저장할 수 있습니다.

따라서 Call by value 방식과는 다르게 함수 호출이 끝나고 return 되어도 포인터 타입으로 선언된 매개변수를 이용해 저장된 값은 그대로 유지가 된다는 의미입니다.

 

배열과 포인터 (Pointer and Array)

 

[C언어 강좌-10] 배열 (Array)

이전 포스트에서 배열의 이름만 쓰게 되면 첫 번째 요소의 주소의 의미가 된다고 말씀드린 적이 있습니다.

 

int cost[10];

즉, 위와 같이 선언된 배열이 있다고 한다면 cost 라고만 쓰면 &cost[0] 과 의미가 같다는 뜻입니다.

내부적으로 배열의 인덱스 연산자 [ ] 도 첫 번째 요소의 포인터 값에 [ ] 안에 들어있는 숫자 만큼 더한다는 의미이기 때문에 배열과 포인터는 매우 밀접한 관련이 있다고 할 수 있습니다.

 

 

-Peter의 우아한 프로그래밍

여러분의 공감과 댓글은 저에게 크나큰 힘이 됩니다. 오류 및 의견 주시면 감사하겠습니다.

 

 

블로그 이미지

친절한 Peter Ahn

IT 정보 공유, 프로그래밍 지식 공유

댓글을 달아 주세요


안녕하세요 피터입니다.

오늘은 C언어의 배열 (Array)에 대해 알려드리겠습니다.


개요

배열은 이전 시간에 포스팅했던 루프(반복문) 과 단짝입니다.

[C언어 강좌-9] 반복문 (Loop)


배열 자체가 같은 크기의 메모리 공간을 연속적으로 할당한 것이기 때문에 각 요소(Element)들을 개별적으로 다루는 것보다 루프를 이용해서 접근하면 훨씬 코드가 심플해지고 효율적이기 때문입니다. 

루프와 함께 사용하지 않는다면 배열은 단순히 변수를 여러 개 선언한 것과 다를 바 없습니다.

루프의 반복자(iterator)는 배열의 각 항목(item)들에 접근하는 인덱스(index)로 사용하기에 매우 적합합니다.



문법(Syntax)

배열의 선언

배열을 선언하는 기본적인 문법은 아래와 같습니다.


datatype arrayname[arraysize];


문법 자체는 변수를 선언하는 것과 크게 다르지 않습니다.

지정된 datatype으로 arraysize 만큼의 메모리 공간을 할당하고, 이 메모리 공간에 접근할 때 arrayname을 이용해서 접근하는 것이죠.

단, arraysize 는 반드시 상수여야 합니다.

[C언어 강좌-5] 상수 (Constant)


int 타입의 크기가 10인 배열을 아래와 같이 선언하면 연속된 영역의 메모리 공간을 할당받게 됩니다.

int cost[10];


따라서 배열 전체 크기는 int 타입의 크기에 arraysize를 곱한 값이 됩니다.

4* 10 = 40

여기에서 한 가지 알아두셔야 할 것은 배열의 이름만 쓰게 되면 첫 번째 요소의 주소와 의미가 같다는 점입니다.

아직은 이 말이 무슨 뜻인지 정확하게 이해가 안되실 수도 있습니다.

이후에 포스팅할 예정인 포인터를 보시고 나면 이해가 좀 더 수월하실 거에요.


배열 요소 접근

이렇게 선언된 배열의 각 요소(element)는 아래와 같이 접근할 수 있습니다.


arrayname[index]


이때 주의하셔야 할 부분은 index 값은 0부터 시작하기 때문에 index의 최대값은 arraysize - 1이 된다는 점입니다.


Write: 배열 요소에 값을 저장

element에 값을 넣을 때는 = 연산자의 좌항(left value)에 넣어주면 됩니다.

int cost[10];
cost[0] = 10;
cost[5] = 30;
cost[9] = 50;


배열의 크기가 10으로 지정되었기 때문에 사용 가능한 인덱스 값은 0부터 9까지입니다.

이 범위를 벗어나는 인덱스를 사용하여 배열을 참조할 경우 스택영역의 메모리 overwrite으로 인해 프로그램이 오동작 하거나 메모리 참조 오류를 만나보실 수 있습니다.


Read: 배열 요소의 값을 읽기

element의 값을 읽어올 때는 = 연산자의 우항(right value)에 넣어주면 됩니다.

int last_cost;
last_cost = cost[9];


다음과 같이 printf 문에서 직접 읽어올 수도 있습니다.

printf("first array value is %d\n", cost[0]);


루프와 배열

int main(void)
{
int score[5]; scanf("%d", &score[0]); scanf("%d", &score[1]); scanf("%d", &score[2]); scanf("%d", &score[3]); scanf("%d", &score[4]); printf("%dst score is %d\n", 1, score[0]); printf("%dst score is %d\n", 2, score[1]); printf("%dst score is %d\n", 3, score[2]); printf("%dst score is %d\n", 4, score[3]); printf("%dst score is %d\n", 5, score[4]);

return 0;
}

위 코드는 크기가 5인 배열을 선언하고 각 요소에 값을 표준 입력(키보드 입력)으로 받아서 할당한 뒤 모든 배열의 요소를 순차적으로 출력하는 코드입니다.


배열을 사용하긴 했지만 특정 구문이 계속해서 반복되는 비효율적인 코드가 되어버렸습니다.


루프를 활용해서 코드를 개선해보겠습니다.

int main(void)
{
  int score[5];
  int i;

  for(i=0; i<5; i++)
  {
    scanf("%d", &score[i]);
  }

  for(i=0; i<5; i++)
  {
    printf("%dst score is %d\n", i+1, score[i]);
  }

  return 0;
}

루프 안에 scanf() 구문과 printf() 구문을 넣고 i 값을 인덱스로 접근하도록 바꾸니 코드가 훨씬 단순해지고 보기좋아졌습니다.

제가 항상 강조하는 것 중에 하나가 코드의 가독성인데 가동성을 높이기 위해서는 이런 작은 부분부터 신경을 써야 합니다.



여러분의 댓글은 저에게 크나큰 힘이 됩니다. 오류 및 의견 주시면 감사하겠습니다.

-Peter의 우아한 프로그래밍

블로그 이미지

친절한 Peter Ahn

IT 정보 공유, 프로그래밍 지식 공유

댓글을 달아 주세요


안녕하세요 피터입니다.

오늘은 C언어의 반복문에 대해 알려드리겠습니다.


개요

반복문은 어떠한 동작을 반복적으로 수행하고자 할 때 사용합니다.
주로 처리해야 하는 데이터가 여러 개인 경우에 효율적으로 처리하기 위해서 씁니다.

순환구조를 갖기 때문에 루프(Loop)라고 부르기도 합니다.
많이 사용되는 표현 중에 영원히 순환하는 루프를 무한루프(infinite loop)라고 하는데 한자와 영어의 합성어로 재미있는 표현입니다.


루프는 대부분의 프로그램에서 필수적으로 들어가는 요소 중에 하나입니다.

일회성으로 기능하고 바로 종료되는 프로그램을 제외하면 사용자가 별도로 종료를 시키기 전까지 계속 실행되는 프로그램들이 훨씬 더 많기 때문입니다.


그러면 지금부터 C언어에서 루프가 어떤식으로 사용되는지 알려드리겠습니다.

C언어에서는 다음과 같이 크게 2가지 방식으로 루프를 구현할 수 있습니다.

바로 while for 구문입니다.

문법(Syntax)

while

기본적인 문법은 아래와 같습니다.

while(condition) 
statement;

condition이 false가 될때까지 하면 주어진 문장(statement)을 반복적으로 수행합니다.

이 때 condition이 바로 "조건절"인데 i < 10 또는 a != 5 등과 같은 부등호가 들어간 수식부터 그 결과가 True / False 가 되는 모든 식이 올 수 있습니다.


조건문을 포스팅할 때 C언어에서 0은 False이며, 0이 아닌 모든 값은 True로 해석된다고 언급드린 적이 있습니다.

따라서 조건절에는 수식뿐만 아니라 특정 값을 갖는 변수가 올 수도 있습니다.

그 결과가 True / False로 해석될 수 있기 때문입니다.


여기서 주의하셔야 할 부분이 있는데요 while 구문은 뒤에 세미콜론(;)으로 끝나는 1개의 statement만 취한다는 점입니다.

그런데 나는 여러 개의 문장을 반복적으로 수행하고 싶은데... 그러면 어떻게 해야 할까요?


C언어에서는 { } 로 감싸여진 구문은 1개의 statement로 취급합니다.

아하! 그러면 여러 개의 문장을 { } 블록으로 감싸면 되겠군요!

while(condition)
{
  statement1;
statement2;
}

이렇게 하면 여러 개의 문장을 반복적으로 수행할 수 있습니다.


주의해야 될 점이라는 것은 바로 아래와 같은 실수를 해서는 안된다는 것입니다.

while(condition);
{
  statement1;
statement2;
}

"모든 문장(statement)의 끝은 세미콜론(;)으로" 를 충실히 이행한 결과로 위와 같이 while(conditoin) 에 ; 을 붙이게 되면 대참사가 일어나게 되는 겁니다.


세미콜론 자체도 하나의 문장으로 취급되기 때문이죠. while은 세미콜론만 취하여 조건절이 만족할 때 까지 아무런 의미도 없는 ; 문장만 처리한 뒤 { } 블록의 문장들을 1번 수행하게 되겠죠.


이 것은 애초의 의도와는 전혀 다른 동작입니다.  이 모든 일이 (;) 하나 잘못 붙임으로써 발생한 일이죠.
주의해주세요!


아래는 1부터 10까지 합한 값을 출력하는 반복문입니다.
참고해주시기 바랍니다.

int i = 1;
int sum = 0;
while(i <= 10);
{
   sum += i; // sum = sum + i; 
   i++;
}
printf("Sum of 1 to 10 is %d\n", sum);


i++ 는 주석에 표시한대로 i = i + 1 의 축약표현이고,
sum += i 에서 +=는  sum = sum + i 의 축약표현입니다.

자주 사용되니 익혀두세요.


일반적으로 루프를 사용할 때에는 다음과 같은 세가지 요소가 필요합니다.

  • 초기화
  • 조건절
  • 증감연산자

위 예제에서 초기화 구문이 바로 int i = 1; int sum = 0; 에 해당됩니다.

어떠한 문장으로 반복적으로 수행하기 위해서 조건절에 들어갈 변수를 초기화해주는 부분입니다.


조건절은 아시다시피 (i <= 10)으로 풀어서 해석하자면 "i 값이 10과 같거나 작으면" 입니다.

우리가 구해야 할 값은 1부터 10까지의 합계이니 1부터 10까지 다 더하기 위해 이러한 조건절을 만든 것이죠.


반복작업이 끝나는 조건을 만족하기 위해서 필수적으로 i값이 증가하는 구문이 필요합니다.

i++; 구문이 바로 증감연산자로 이러한 역할을 수행합니다. 


for

for(initialize; condition; increasement) 
statement;

for 구문의 기본 문법은 위와 같습니다.


while과 다른 부분은 위에서 언급했던 3가지 요소를 for문 안에 넣을 수 있다는 점입니다.

위에서 보시는 바와 같이 세미콜론으로 구분하여 3요소를 구현할 수 있습니다.

이러한 장점으로 똑같이 1부터 10까지 합계를 구하는 코딩을 하면 아래처럼 좀 더 단순하게 표현할 수 있습니다.

for(int i=1; i<=10; i++)
{
   sum += i;
}
printf("Sum of 1 to 10 is %d\n", sum);


자세히 보신 분들은 눈치 채셨겠지만 이렇게 for문 내부에서 int i = 1; 과 같이 선언과 초기화를 동시에 할 수 있습니다.


다만 기억하셔야 할 부분은 { } 블록은 별도의 스택 영역에 저장되기 때문에 지금 선언된 i 변수는 for 구문의 { } 블록 내에서만 유효하다는 것입니다.


변수(Variable) 포스팅에서 Scope 기억나시죠?

아래 코드를 컴파일 해보면 i 가 undefine 되었다는 컴파일 에러를 확인하실 수 있습니다.

for(int i=1; i<=10; i++)
{
   sum += i;
}
printf("Sum of 1 to 10 is %d\n", sum);
printf("Can I access variable i(%d)?\n", i);  


do while

do
{
   statement1;   
   statement2;
}while(condition);

기본적인 문법은 while과 거의 흡사합니다.

while구문과 다른점은 루프의 조건절을 보기 전에 일단 문장을 1번 수행한다는 점입니다.

어떤 작업을 반복적으로 수행하지만, 조건에 상관없이 최소한 1번을 수행해야 될 경우에 사용하면 유용합니다.

주의하셔야 할 부분은 while(condition) 뒤에 ; 이 붙는다는 점입니다.


무한루프 (infinite loop)

조건이 항상 참(True)이어서 무한대로 수행되는 루프가 존재합니다.

위에서 언급한 무한루프인데요. 어떻게 구현할까요?

while(1)
{
   statement;
}


for(;;)
{
   statement;
}


while과 for문으로 무한루프를 구현해봤습니다.

별도로 루프를 빠져나오는 코드가 없다면 이 루프는 영원히 반복됩니다.

그렇다는 얘기는 일단 한번 시작하면 끝을 볼 수 없다는 겁니다.

하지만 대부분의 경우 의도적으로 무한루프를 구현할 때 빠져나올 구멍 한개 정도는 최소 만들어두는 편입니다.

이 때 사용되는 것이 루프 제어문입니다.


루프 제어문

break

이 구문은 루프 내부에서만 사용될 수 있으며, 루프 없이 단독으로 사용될 수는 없습니다. switch / case 구문만 예외로 하고요.

루프를 수행하다가 break 구문을 만나면 그 즉시 루프를 빠져나오게 됩니다. 마치 영화 인셉션에서 킥을 하는 것과 비슷하네요.


다음과 같이 사용하면 무한루프도 예외없이 빠져나올 수 있습니다.

int i = 1;
int sum = 0;
while(1)
{
   sum += i;

   if(i == 10)
      break;
}
printf("Sum of 1 to 10 is %d\n", sum);

자, 이렇게 하면 위에서 했던 것과 마찬가지로 1부터 10까지의 합을 무한루프를 이용해서 구현할 수 있습니다.


continue

이 구문도 break 구문과 마찬가지로 루프 제어에 사용되는 구문입니다.

루프 수행도중 이 구문을 만나면 즉시 다음 번 루프로 넘어가게 됩니다.


아래는 0부터 10까지의 수 중에 홀수만 화면에 출력하는 코드입니다.

for(int i=0; i<10; i++)
{
if(i % 2 == 0)
continue;
else
printf("%d\t", i);
}

이 때 주의하셔야 할 부분은 for문에서의 동작입니다.

while문은 증감연산자가 기본 문법(syntax)에 포함되지 않기 때문에 continue 구문을 만났을 때 바로 조건절로 넘어가게 되는데,

for문은 조건절이 아니라 증감연산자로 넘어가게 됩니다.


즉, while문은 continue구문에서 '이번 턴부터 다시 수행'이라고 한다면, for문은 '다음 턴부터 수행' 이라고 표현할 수 있겠네요.



여러분의 댓글은 저에게 크나큰 힘이 됩니다. 오류 및 의견 주시면 감사하겠습니다.

-Peter의 우아한 프로그래밍

블로그 이미지

친절한 Peter Ahn

IT 정보 공유, 프로그래밍 지식 공유

댓글을 달아 주세요


안녕하세요 피터입니다.

오늘은 C언어의 조건문에 대해 알려드리겠습니다.


주어진 조건에 따라 분기한다고 해서 분기문이라고도 하는데요, 아래와 같이 크게 세가지 종류가 있습니다.

  • If / Else
  • Switch / Case
  • ? operator

동작 원리는 거의 같지만 가독성이나 코드의 유연성을 고려하여 상황에 따라 적절하게 사용하시면 됩니다.


1. If / Else

if 구문의 기본적인 문법은 다음과 같습니다.

if(condition)
  statement;

위 코드를 "만일(if) 조건이 만족하면 statement를 실행하시오." 라고 이해하시면 됩니다.

조건이 만족하는 기준은 condition이 참(true)가 되는 경우입니다.
C언어에서 조건문의 참(true)은 0이 아닌 모든 수가 해당되며,
반대가 되는 거짓(false)는 0입니다.

따라서 if(1) 은 참이 되고, if(100)도 참이 됩니다.
오로지 if(0)인 경우에만 해당 statement가 실행되지 않습니다.
의미를 분명하게 하기 위해 아래와 같이 상수로 정의하여 사용하기도 합니다.

#define TRUE  1
#define FALSE 0

if(TRUE)
  statement;


그렇지만 일반적으로 condition 들어가는 구문은 아래와 같이 비교 구문이 대부분입니다.
else 구문은 if(condition)이 만족하지 못하는 모든 경우에 실행됩니다.
if(age >= 19)

   printf("You are adult\n");
else
printf("You are minor\n");


분기해야 할 조건이 여러개인 경우에는 아래와 같이 else if 구문을 사용할 수 있습니다.


if(type == 1)
  printf("Text type\n");
else if(type == 2)
  printf("Image type\n");
else if(type == 3)
  printf("Video type\n");
else
  printf("nvalid type\n");


2. Switch / Case

Switch-Case 구문의 기본 문법은 아래와 같습니다.

switch(variable)
{
  case value1:
    statement;
    break;
case value2: statement; break;
 default: statement; }

swich 구문에서 분기에 사용할 변수(variable)를 설정한 뒤 case 구문에서 각 value에 대한 루틴을 설정하면 분기합니다.

주의하셔야 할 부분은 case 구문에서 break; 구문인데요. break 를 쓰지 않으면 밑에 있는 case 구문까지 실행이 되어버리니 의도하지 않은 결과가 나올 수 있습니다.

아래 예제는 위의 if-else 구문으로 되어 있는 코드를 switch-case 구문으로 변환한 코드입니다. 참고해주세요~

switch(type)
{
  case 1:
    printf("Text type\n");
    break;
  case 2:
    printf("Image type\n");
    break;
  case 3:
    printf("Video type\n");
    break;
  default:
    printf("Invalid type\n");
}


3. ? operator

? (물음표) 연산자는 3개의 항이 요구되기 때문에 "삼항연산자" 라고도 불리며, 조건식을 수행하기 때문에 "조건부연산자" 라고도 합니다.

좀 더 단순하게 활용이 가능한데요. 기본 문법은 다음과 같습니다.

(condition)?a:b;

boolean 값을 갖는 조건식(condition) 뒤에 ? 연산자가 오고 그 뒤에 A:B 와 같이 콜론(:)으로 구분되는 값이 2개 오게 됩니다.

조건식의 결과가 참(True)인 경우 연산의 결과가 a 가 되구요, 거짓(False)인 경우 b가 되게 됩니다.

아래 예제와 같이 활용하면 코드를 좀 더 단순화 할 수 있습니다.

printf("%s\n", (age>=19)?"Adult":"Minor");


여러분의 댓글은 저에게 크나큰 힘이 됩니다. 오류 및 의견 주시면 감사하겠습니다.

-Peter의 우아한 프로그래밍


블로그 이미지

친절한 Peter Ahn

IT 정보 공유, 프로그래밍 지식 공유

댓글을 달아 주세요



안녕하세요 피터입니다.

오늘은 C언어에서 사용되는 연산자(Operator)에 대해서 알려드리겠습니다. 

연산자란 컴파일러에게 특정한 수학적 or 논리적 동작을 수행하도록 알려주는 기호(Symbol)입니다.  C언어에는 아래와 같은 다양한 연산자들이 제공되고 있습니다. 

연산자의 종류별 설명과 쓰임새에 대해 다루고 각 연산자들이 갖는 우선순위에 대해 설명드리겠습니다.


1. 연산자 종류

1.1. 산술 연산자 (Arithmetic Operator)

가장 익숙한 산술 연산자부터 살펴보겠습니다. 산술 연산자는 말 그대로 수학적인 계산을 하는데 필요한 연산자들입니다. 

C언어에서는 다음과 같이 사칙연산을 포함한 다양한 산술 연산자를 제공합니다. 

예제는 A = 10, B = 20 일때 결과입니다.

 연산자

설명 

 예제

 +

 두 값을 더함.

 A + B = 30 

 -

 좌항의 값에서 우항의 값을 뺌.

 A - B = -10 

 *

 두 값을 곱함. 

 A * B = 200 

 /

 좌항의 값을 우항의 값으로 나눔.

 B / A = 2

 %

 좌항의 값을 우항의 값으로 나눈 나머지 값.

 B % A = 0

 ++

 숫자값을 1만큼 증가.

 A++ (or ++A) = 11

 --

 숫자값을 1만큼 감소. 

 A-- (or --A) = 9


1.2. 관계 연산자 (Relational Operator)

관계 연산자는 비교 연산자라고도 하는데요 수학에서 부등호를 떠올리시면 이해가 빠르실 겁니다.

예제는 A = 10, B = 20 일 때 결과입니다.

 연산자

설명 

 예제

 ==

 좌항의 값과 우항의 값이 같은지 확인.

 (A == B) is False 

 !=

 좌항의 값과 우항의 값이 다른지 확인.

 (A != B) is True

 >

 좌항의 값이 더 큰지 확인.

 (A > B) is False 

 <

 우항의 값이 더 큰지 확인.

 (A < B) is True

 >=

 좌항의 값이 크거나 같은지 확인.

 (A >= B) is False

 <=

 우항의 값이 크거나 같은지 확인.

 (A <= B) is True


1.3. 논리 연산자 (Logical Operator)

논리 연산자는 주로 조건문(if)이나 반복문(while, for) 등에서 여러 개의 조건절을 처리할 때 사용합니다. 

결과는 Boolean 값으로 True 또는 False 값을 가집니다.

예제는 A = 1, B = 0 일 때 결과입니다.

 연산자

설명 

 예제

 &&

 논리곱. 두 값이 모두 True인 경우에만 True.

 (A && B) is False

 ||

 논리합. 두 값중 하나라도 True이면 True.

 (A || B ) is True

 !

 논리부정. 단항연산자이며 반대값을 취함.

 !(A && B) is True


1.4. 비트 연산자 (Bitwise Operator)

비트연산자는 좌항과 우항에 대해서 비트 단위로 처리해서 결과를 계산하도록 하는 연산자입니다. 

주로 옵션을 저장하는 Flag 값을 셋팅할 때나 반대로 Mask를 적용해서 특정 옵션값을 추출할 때 사용합니다. 

비트단위 연산은 처리 속도가 빠른 편이기 때문에 잘 활용할 수 있다면 성능 향상을 기대해볼 수 있습니다. 

예제는 A = 60, B = 13 일 때 결과입니다.

이해를 돕기 위해 2진법으로 표기하였습니다. 

A = 0011 1100

B = 0000 1101

-----------------

A&B = 0000 1100

A|B = 0011 1101

A^B = 0011 0001

~A = 1100 0011


 연산자

설명 

 예제

 &

 좌항과 우항을 비트 단위로 논리곱 연산을 수행. 결과값은 둘 다 1일 때 1, 나머지 경우는 0이 된다.

 (A & B) = 12, i.e., 0000 1100

 |

 좌항과 우항을 비트 단위로 논리합 연산을 수행. 결과값은 둘 다 0일 때 0, 나머지 경우는 1이 된다.

 (A | B) = 61, i.e., 0011 1101

 ^

 좌항과 우항을 비트 단위로 배타적논리합 연산을 수행. 결과값은 둘 다 0이거나 1이면 0, 두 값이 다르면 1이 된다.

 (A ^ B) = 49, i.e., 0011 0001

 ~

 단항 연산자로 모든 비트를 반전시킨다. 0은 1로 1은 0으로. 비트단위 논리부정. 부호비트가 바뀌기 때문에 음수가 된다. 

 (~A ) = -61, i.e,. 1100 0011

 <<

 비트단위 이동 연산자로 좌항의 값을 우항의 수 만큼 왼쪽으로 이동.

 A << 2 = 240 i.e., 1111 0000

 >>

 비트단위 이동 연산자로 좌항의 값을 우항의 수 만큼 오른쪽으로 이동. 

 A >> 2 = 15 i.e., 0000 1111


아래와 같이 비트 연산자 &, |, ^ 에 대한 결과를 함께 비교해보면 이해가 좀 더 쉽게 되실 겁니다.

 p

 q 

 p & q

 p | q

 p ^ q

0

0

 0

0

0

0

1

 0

1

1

1

1

 1

1

0

1

0

 0

1

1


1.5. 대입 연산자 (Assignment Operator)

대입 연산자는 기본적으로 우항의 값을 좌항에 대입할 때 사용합니다. 

다음과 같이 산술 연산자나 비트 연산자 등과 결합하여 축약형으로 사용할 수 있습니다.

 연산자

설명 

 예제

 =

 우항의 값을 좌항으로 대입.

 C = A + B

 +=

 좌항의 값에 우항을 더한 값을 좌항에 대입.

 C += A is C = C + A

-=

 좌항의 값에서 우항을 뺀 값을 좌항에 대입. 

 C -= A is C = C - A

 *=

 좌항의 값과 우항의 값을 곱한 값을 좌항에 대입.

 C *= A is C = C * A

 /=

 좌항의 값에서 우항의 값을 나눈 값을 좌항에 대입.

 C /= A is C = C / A

 %=

 좌항의 값을 우항으로 나눈 나머지 값을 좌항에 대입.

 C %= A is C = C % A

 <<=

 좌항의 값을 우항의 수만큼 왼쪽으로 비트 단위 이동을 한 값을 좌항에 대입.

 C <<= 2 is C = C << 2

 >>=

 좌항의 값을 우항의 수만큼 오른쪽으로 비트 단위 이동을 한 값을 좌항에 대입.

 C >>= 2 is C = C >> 2

 &=

 좌항의 값과 우항을 비트 단위 논리곱한 값을 좌항에 대입.

 C &= 2 is C = C & 2

 ^=

 좌항의 값과 우항을 비트 단위 배타적논리합한 값을 좌항에 대입.

 C ^= 2 is C= C ^ 2

 |=

 좌항의 값과 우항을 비트 단위 논리합한 값을 좌항에 대입.

 C |= 2 is C = C | 2


1.6. 기타 연산자 (ETC Operator)

위에서 설명한 연산자들 외에도 C언어에서 자주 사용되는 중요한 연산자가 있습니다. 

앞으로 배우게 될 중요한 개념중 하나인 포인터 관련 연산은 굉장히 자주 사용되고 중요합니다. 

sizeof()같은 경우 자료형이나 변수, 구조체의 크기를 구할 때 자주 사용됩니다. 

 연산자

설명 

 예제

 sizeof()

 괄호 안에 주어진 변수 또는 자료형의 크기를 반환.

 sizeof(int) is return 4

 &

 단항연산자로 변수의 주소값을 반환.

 &a is 변수 a의 주소값 반환

 *

 포인터의 값을 반환.

 *p is 포인터변수 p의 값 반환

 ? :

 조건 연산자. 조건에 따라 두 값중 하나를 반환. (조건절)? A : B 에서 조건절이 True이면 A조건절이 False이면 B 반환

 (A<0) A is A가 0보다 작으면 0, 크면 A를 반환


2. 연산자 우선순위

C언어에서 사용되는 모든 연산자에는 우선순위가 있습니다. 

예를 들어 x = 7 + 3 * 2; 과 같이 여러 연산자가 연달아서 사용된 문장의 경우 연산자 우선순위에 따라 + 보다 * 이 우선시되므로 결과값이 20이 아니라 13이 됩니다. 

아래 표에서 상위에 위치한 연산자일 수록 우선순위가 높고 맨 아래 위치한 연산자가 가장 우선순위가 낮습니다.

 분류

연산자 

결합법칙

 후위(Postfix)

 () [] -> . ++ --

 왼쪽에서 오른쪽

 단항(Unary)

 + - ! ~ ++ -- (type)* & sizeof

 오른쪽에서 왼쪽

 곱셈(Mutiplicative)

 * / %

 왼쪽에서 오른쪽

 덧셈(Additive)

 + - 

 왼쪽에서 오른쪽

 비트이동(Shift)

 << >>

 왼쪽에서 오른쪽

 관계(Relational)

 < <= > >=

 왼쪽에서 오른쪽

 균등(Equality)

 == !=

 왼쪽에서 오른쪽

 비트곱(BitwiseAND)

 &

 왼쪽에서 오른쪽

 비트배타합(BitwiseXOR)

 ^

 왼쪽에서 오른쪽

 비트합(BitwiseOR)

 |

 왼쪽에서 오른쪽

 논리곱(LogicalAND)

 &&

 왼쪽에서 오른쪽

 논리합(LogicalOR)

 ||

 왼쪽에서 오른쪽

 조건(Conditional)

 ?:

 오른쪽에서 왼쪽

 대입(Assignment)

 = += -= *= /= %=>>= <<= &= ^= |=

 오른쪽에서 왼쪽

 콤마(Comma)

 ,

 왼쪽에서 오른쪽



여러분의 댓글은 저에게 크나큰 힘이 됩니다. 오류 및 의견 주시면 감사하겠습니다.

-Peter의 우아한 프로그래밍

블로그 이미지

친절한 Peter Ahn

IT 정보 공유, 프로그래밍 지식 공유

댓글을 달아 주세요

안녕하세요 피터입니다.

오늘은 C언어에서 사용되는 Storage Class에 대해 설명드리겠습니다.

기억 영역 분류 (Storage Class)


Storage Class는 C언어에서 기억 영역을 분류하기 위해서 사용되는 용어입니다. 이 용어는 한글로 번역하기가 애매한 부분이 있는데 일반적으로 기억 영역 분류 또는 기억류 라고 번역되어 쓰이고 있습니다. 

이 stroage class는 class 라는 단어가 들어있습니다만, 여기서의 쓰임새는 C++ 의 'class' 키워드와는 전혀 관련이 없습니다. 단지 어떤 것의 종류를 의미하는 사전적인 의미로 class가 쓰인 것입니다. 

C언어의 storage class에는 auto, register, static, extern 4가지 종류가 있습니다.

각각의 class는 상호 배타적이라서 두 가지 이상의 키워드를 함께 사용할 수 없습니다. 


1. auto

auto는 지역변수를 선언할 때 아무것도 지정하지 않았을 때 기본값으로 지정되는 storage class 입니다. 

{
   int count;
   auto int count;
}

auto는 함수 내에서만 사용이 가능하며(지역 변수) 위 두 구문은 의미가 같습니다.

(사실 실제로 auto 키워드를 사용할 일은 거의 없습니다)


2. register

register는 변수가 메모리 대신 레지스터에 저장되면 좋겠다는 희망사항을 컴파일러에게 알려주는 storage class 입니다. 

레지스터에 저장된 변수에는 메모리에 저장된 변수보다 훨씬 더 빠르게 접근할 수 있으므로 성능 향상을 꾀할 수 있습니다. 

레지스터에 저장되려면 변수의 크기가 레지스터 사이즈보다 같거나 작아야 합니다. 일반적으로 1 word를 저장할 수 있습니다. 그리고 레지스터는 메모리처럼 주소값을 갖을 수 없기 때문에 변수의 주소값을 구하는 & 연산자는 사용할 수 없습니다.

{
   register int  miles;
}

register 지시자를 사용했다고 해서 반드시 레지스터에 저장되는 것은 아닙니다. 그저 '레지스터에 저장했으면 좋겠다' 정도인 것입니다. 

최근 나온 컴파일러들은 최적화를 수행하면서 레지스터를 잘 활용하기 때문에 register 지시자를 무시할 확율이 높습니다. 

(결론적으로는 실제로 사용할 일이 거의 없습니다.)


3. static

이전 강좌에서 변수의 Scope에 대해서 설명드렸던 것 기억하시나요?

지역 변수(Local variable)의 경우에는 함수 내에서만 유효하다고 말씀드렸습니다. 함수를 빠져나가는 순간. 즉 '}' 를 만나는 순간 지역 변수는 스택에서 제거됩니다. 

같은 함수를 다시 호출했을 때 예전에 사용하던 지역 변수는 이미 삭제되어 값을 유지하지 못합니다. 같은 이름의 새로운 지역 변수가 스택에 생성되었기 때문이죠. 

그런데 static 지시자를 사용해서 생성한 지역 변수는 함수를 빠져나가도 값을 유지할 수 있습니다. 변수의 생명 주기(Life cycle)이 달라지게 되는거죠.  그렇다고 해서 다른 함수에서 접근이 가능해지는 것은 아니니 혼동하지 말아주세요.

static은 전역 변수에도 사용할 수 있습니다. 전역 변수를 static으로 지정하면 해당 변수의 Scope을 선언된 파일 내로 제한합니다. 

아래 예제 코드를 봐주세요. 

#include <stdio.h>
 
/* function declaration */
void func(void);
 
static int count = 5; /* global variable */
 
main() {

   while(count--) {
      func();
   }
	
   return 0;
}

/* function definition */
void func( void ) {

   static int i = 5; /* local static variable */
   i++;

   printf("i is %d and count is %d\n", i, count);
}

count라는 전역 변수에 static을 사용해서 Scope를 파일 내로 제한했습니다. 

func() 함수 내에서 선언된 지역 변수 i 의 경우 함수 호출이 끝나더라도 값을 유지해서 다음 호출 시 늘어나는 것을 아래 실행 결과에서 볼 수 있습니다.  

static int i = 5; 이 초기화 구문은 변수가 처음 생성될 때만 유효하기 때문에 첫 번째 함수 호출 시에만 실행됩니다. 그 이후 호출 시에는 무시되죠. 

함수가 호출될 때마다 생성 - 파괴가 반복되는 일반적인 지역변수와는 전혀 다르게 동작하죠. static을 빼고 테스트 해보시기 바랍니다. 

실행 결과

i is 6 and count is 4

i is 7 and count is 3

i is 8 and count is 2

i is 9 and count is 1

i is 10 and count is 0


4. extern

extern은 프로그램을 구성하는 파일들이 여러 개일 때 다른 파일에서 정의된 전역 변수나 함수를 접근할 수 있게 참조(reference)를 제공해주는 지시자입니다. 

다만 extern으로 선언된 외부 전역 변수는 초기화할 수 없습니다. 

아래 예제 코드를 봐주세요.

main.c

#include 
 
int count ;
extern void write_extern();
 
main() {

   count = 5;
   write_extern();
}

support.c

#include 
 
extern int count;
 
void write_extern(void) {
   printf("count is %d\n", count);
}

main.c 파일에서 선언된 전역 변수 count를 support.c 에서 extern으로 선언하여 printf하고 있습니다. 

또한 support.c에서 선언된 write_extern 함수를 main.c에서 extern으로 선언하여 호출하고 있습니다. 

이런식으로 여러 개의 파일에서 전역 변수를 공유하고 싶을 때는 extern을 사용하여 같은 이름의 변수 또는 함수를 선언하면 됩니다. 

컴파일 및 실행 결과

$gcc main.c support.c
$./a.out

count is 5


여러분의 댓글은 저에게 크나큰 힘이 됩니다. 오류 및 의견 주시면 감사하겠습니다.

-Peter의 우아한 프로그래밍

블로그 이미지

친절한 Peter Ahn

IT 정보 공유, 프로그래밍 지식 공유

댓글을 달아 주세요