4.1 조건 분기문    [doc]    [smts]

프로그램에서 어떤 조건에 따라서 수행해야 하는 행동이 달라지는 경우는 매우 빈번하게 발생한다. 따라서 프로그램언어라면 보통 명령 수행의 흐름을 바꾸는 제어 명령어가 마련되어 있으며 C/C++언어도 이를 위해서 조건 검사 명령과 반복 명령어가 있다.


if 명령문은 조건을 제어하기 위해서 사용된다. 문법은 다음과 같다.


if (조건식) {
  실행문1;
  …
   실행문n;
}

보다시피 아주 간단한 문장으로 조건문이 참인지 거짓인지 판별하여 조건이 참이면 바로 뒤의 중괄호 {}로 묶인 실행문들을 수행하고, 참이 아니면 수행하지 않고 다음으로 넘어간다.


 만약 실행문이 하나라면 굳이 중괄호로 묶을 필요는 없으나 가독성을 높이기 위해서 중괄호를 항상 사용한다. 예를 들면 다음과 같다.


if (ca == cb)
  cx = 10;
if (cd > 10) {
  ce = 100;
}

 if 문을 사용할 때는 몇 가지 주의할 점이 있다. 먼저 조건은 반드시 괄호로 감싸야 한다는 점이고 괄호 안의 조건은 참과 거짓을 판별할 수 있어야 한다. C언어는 내부적으로 정수 0을 거짓으로 취급하고 그 이외의 수는(보통은 1값) 모두 참으로 취급한다는 점을 유의하자. 아래의 예에서 sa=10이라는 대입문은 무조건 수행되고 예2에서 sb=sc라는 대입문은 절대로 수행되지 않는다. (왜?)


if (1) {
  sa = 10; // sa=10과 동일
}

if (0) {
  sb = sc;
}

또한 조건문에서 가장 하기 쉬운 실수가 ‘==’를 ‘=’로 잘 못 사용하는 경우인데 이 경우 논리적인 버그가 발생하게 된다. 예를 들면


long la = 1, lb = 1, lc;
if (la = lb) {
  lc = 10;
}

위와 같은 경우에는 la=lb라는 표현식은 변수 la에 변수 lb값을 대입하고 그 자체로 변수 lb값인 1을 갖게 된다. 따라서 의도와 다르게 lc=10이라는 명령은 무조건 수행되게 된다. 만약 lb변수값이 0이라면 lc=10이라는 명령은 절대로 수행되지 않는다. 따라서 아래와 같이 프로그램을 수정해야 할 것이다.


long la=1, lb=1, lc;
if (la == lb) {
lc = 10;
}

또한 실수하기 쉬운 예가 다음과 같다.


if (la == 10);
  lb = lc;

이 예의 경우 if 조건 다음의 세미콜론 ‘;’에 의해 수행문이 종료되기 때문에 조건과 관계없이 lb=lc명령이 수행된다. 실제 프로그래밍을 하다보면 쉽게 하는 실수이니 눈여겨보기 바란다.


다음의 두 예는 서로 다른 프로그램이다. 첫 번째 예는 괄호가 없기 때문에 if 조건이 첫 번째 문장에만 적용되어서 ia의 값과는 상관없이 sc++이 수행되지만, 두 번째 예는 ia가 10값일 때에만 sc++이 수행된다.


if (ia == 10)
sb++;
sc++;
if (ia == 10) {
  sb++;
  sc++;
}

이번에는 if 문과 항상 같이 다니는 else문에 대해서 알아보자. 기본적인 문법은 아래와 같다.


if (조건문) {
  명령1;
  ...
} else {
  명령2;
  ...
}

else문에 포함된 명령어집합은 if 조건이 거짓일 경우 수행된다. 또한 if와 else를 조금 확장해 보면 if ~ else if 문이 된다.


if (조건문1) {
  명령문1;
  ...
} else if (조건문2) {
  명령문2;
  ...
} else {
  명령문3;
  ...
}

조건문1이 참이면 명령문1을 수행하고 조건문1이 거짓이고 조건문2가 참이면 명령문2가 수행되며, 두 조건 다 거짓일 경우 명령문 3이 수행된다.


 다음 예제는 하나의 정수를 입력받아서 3의 배수인지 아닌지를 판별하여 화면에 표시해주는 예제이다. 3의 배수라면 3으로 나눈 나머지가 0일 것이고 아니라면 3으로 나눈 나머지가 0이 아니라는 사실을 이용하면 쉽게 프로그램을 작성할 수 있다.


ex04-01.c
#include <stdio.h>
int main() {
  int ia;
  printf("Input an interger :");
  scanf("%d", &ia);
  if (ia%3 == 0)
      printf("%d is multiple of 3.\n", ia);
  else
      printf("%d is NOT multiple of 3.\n", ia);
}

Input an interger number :2
2 is NOT multiple of 3.

 사용자가 입력받은 수의 절대값을 출력하는 프로그램 예를 들면 다음과 같다. 입력된 수가 양수냐 아니냐에 따라서 수행되는 일이 달라진다.


ex04-02.c

#include<stdio.h>
int main() {
double da;
printf("input a number :");
scanf("%lf", &da);
printf("|%lf|=", da);
if (da>0) {
printf("%lf",da);
} else {
printf("%lf",-da);
}
}

실행 결과

input a number :-1.1
|-1.100000|=1.100000

이 프로그램에서 입력된 수가 양수이면 그대로 출력하고 음수라면 -1을 곱해서 출력하는 간단한 방법을 사용했다.


 다음 예는 입력된 정수가 음수인지, 0인지, 양수인지를 판별하는 예이다. if-else문이 중첩되어 사용되었음을 눈여겨보아야 한다.


ex04-03.c
#include <stdio.h>
int main() {
  int ia;
  printf("Input an interger number : ");
  scanf("%d",&ia);
  if (ia < 0)
      printf("%d is negative.\n", ia);
  else if (ia > 0)
      printf("%d is positive.\n", ia);
  else
      printf("%d is a zero.\n", ia);
}

Input an interger number : 0
2 is a zero.

다음은 사용자로부터 입력 받은 문자 하나가 알파벳 소문자라면 ‘lower case’ 라고 화면에 출력하는 예제이다.


ex04-04.c
#include <stdio.h>
int main() {
char ch;
scanf("%c", &ch);
if ('a'<=ch && ch<='z' ){
printf("lower case");
}
}

g
lower case

이 예제에서 비교문 (‘a’<=ch && ch<=’z’)는 (97<=ch && ch<=122) 와 동일하다. 문자는 내부적으로 아스키코드로 간주되기 때문이다.


 위 예제어서 대문자의 경우 “upper case’라고 출력하고 숫자의 경우 ‘digit’이라고 츌력하는  부분을 추가하면 다음과 같다.


ex04-05.c
#include <stdio.h>
int main() {
char ch;
scanf("%c", &ch);
if ('a'<=ch && ch<='z' ){
printf("lower case");
} else if ('A'<=ch && ch<='Z' ){
printf("upper case");
} else if (‘0’<=ch && ch<=’9’) {
printf("upper case");
} else {
printf("unknown");
}
}

이와 같이 if - else if 문은 얼마든지 중첩하여 사용할 수 있다.


Posted by 살레시오
,

6.6 포인터 사용시 유의점     [gdoc]     [smts]

 C 프로그래밍에서 포인터를 사용할 때 주의하지 않으면 치명적인 오류를 발생하게 된다. C프로그래밍이 어렵다는 인식이 있는 것은 바로 포인터를 적절하게 사용하기가 상당히 어렵다는 점에서 기인한다. 포인터는 곧 ‘주소’이므로 어느 주소값을 가르키고 있는가가 가장 중요하다. 먼저 확인할 사항은 포인터가 초기화가 되었는가이다.


 초기화되지 않은 포인터가 있을 경우 컴파일러에 따라 경고 메시지를 내기도 하고 경고 없이 실행하다가 실행 중 오류(runtime error)를 발생시켜서 프로그램이 다운될 수도 있다. 포인터를 사용할 때는 반드시 초기화를 시켜야 한다는 것을 알아두자.


다음 예를 보자


0604-01.c
#include <stdio.h>
int main()
{
double *dpa;
*dpa = 1.0;  //(1)
}

이 예는 언듯 문제가 없어보일 지도 모르지만 포인터 dpa가 초기화되지 않고 (1)에서 사용되었다. 이 프로그램은 컴파일러에 따라 컴파일시 오류를 발생하거나 실행이 되더라도 정상적인 동작을 하지 않고 프로그램이 죽어버릴 것이다. 그 이유는 포인터가 초기화되지 않은 상태이므로 정상적인 주소값을 가지고 있기 때문이다. 비정상적인 주소에 실수값 1.0을 대입하였으니 프로그램이 죽어버리는 것이다.


 포인터를 초기화시키려면 다음과 같이 기존 변수의 주소값을 사용하는 방법이 있다.


0604-02.c
#include <stdio.h>
int main()
{
  double da = 0.0;
  double *dpa = &da; // 변수 da의 주소로 포인터 dpa를 초기화
  *dpa = 1.0;
}

아니면 malloc()함수를 사용하여 정상적인 메모리 공간에  새로운 저장 공간(메모리)과 주소를 할당받을 수 있다.


#include <stdio.h>
int main()
{
  double *dpa = malloc(sizeof(double)); //새로운 저장공간과 주소값 할당
  *dpa = 10;
  …
  free(dpa); // 다 사용한 후 반드시 저장공간을 반환해야 한다.
}

malloc()함수와 free()함수는 stdio.h 헤더파일에 정의된 표준 함수이며 각각 메모리를 할당하고 반환하는 역할을 한다. sizeof()함수는 입력 인자의 바이트수를 반환하는데 sizeof(double) 은 double형의 바이트 수를 반환한다. (이 경우는 8) 따라서 8바이트 저장 공간을 확보한 후 그 주소를 반환한다. 그 주소는 포인터 dpa에 저장된다. 단, malloc()함수로 할당받은 저장 공간은 반드시 free()함수로 반환시켜야 한다.


다음 예는 소위 dangling 포인터의 예이다.


0604-04.c
#include <stdio.h>

int main()
{
int ia=10, *ipa = &ia;
printf("*ipa = %d\n", *ipa);
if (ia==10) {
int ib = 20;
ipa = &ib;
}
printf("*ipa = %d\n", *ipa);
}

if 블록 안에서 ipa가 &ib로 초기화가 되었지만 이 블록에서 벗어나는 순간 내부 변수 ib는 소멸되므로 ipa포인터도 그 소재가 불분명하게 된다. 이러한 경우를 dangling 포인터라고 하는데 프로그램이 길어질 경우 프로그래머가 의식하지 못하는 부분에서 이러한 실수가 일어날 수도 있게 된다. 당연히 이러한 경우도 방지해야 한다.


 다음 예를 보자. 다음 예는 ①에서 func(ipa)라고 호출을 하고 있다. 이 함수 내에서 포인터는 ②에서 내부 지역변수 ia의 주소값으로 초기화 된다. 그런데 func()함수 내부의 ipb포인터는 main()함수의 ipa와는 별개의 포인터이다. 따라서 main()함수 안의 ipa는 여전히 초기화가 되지 않은 포인터이다. 따라서 엉뚱한 값이 나오거나 프로그램이 실행 중지되기도 한다.


0604-03.c
#include <stdio.h>
void func(int*);

int main()
{
int *ipa;
func(ipa); //①
printf("*ipa = %d\n in main()", *ipa);
}

void func(int *ipb)
{
int ia = 20;
ipb = &ia; //②
printf("*ipb = %d in func().\n", *ipb);
}

 이와 같이 포인터를 사용할 경우에는 메모리의 할당과 해제에 각별한 주의를 기울여야 하는데 메모리 누수(leakage)와 같은 예기치 않은 동작을 발생시키기 쉽기 때문이다.



Posted by 살레시오
,

 C/C++에서 함수의 반환값이 포인터가 될 수 도 있다. 이 경우 함수의 선언은 다음과 같다.

반환자료형 *함수명(입력데이터형1, 입력데이터형2, …);


함수 내부에서 return 명령어에 의해 반환되는 변수도 당연히 반환자료형의 포인터가 되어야 한다. 이것은 문자열을 반환하거나 배열을 반환해야 하는 함수를 작성할 때 사용된다.


 예를 들어서 getMsg()라는 함수의 반환형이 문자열일 경우 다음과 같이 선언한다.

char *getMsg();


자료형 (char*)은 문자배열 즉 문자열을 표현하는 것이다. 이것을 이용하여 예제를 들어보면 다음과 같다.


#include <stdio.h>

char *getMsg(); // 함수 선언

int main()
{
printf("%s", getMsg());
}

char *getMsg() // 함수 정의
{
char *str = "getMsg() called.\n";
return str;
}


getMsg() called.


이 예제에서 getMsg() 함수는 내부에서 문자열 포인터 str을 생성한 후 그것을 반환한다. main()함수에서는 그것을 받아서 그대로 printf()함수의 입력 인자로 주는 간단한 예제이다.


C++ 강좌 전체 목록 >>>


c{c++},n{c0010}

Posted by 살레시오
,