'Language/C'에 해당되는 글 66건

  1. 2012.06.13 the way to check if a character array is empty
  2. 2012.06.11 가변인수 va_start()
  3. 2012.05.29 typedef & struct (2)
  4. 2012.05.29 sscanf
  5. 2012.05.23 typedef & struct (1)
  6. 2012.05.16 inconv source (iconv_open, iconv, iconv_close)
  7. 2012.05.16 inconv.h
  8. 2012.05.11 read() 파일 읽기
  9. 2012.04.26 execl 다른 프로그램 실행
  10. 2012.04.26 Why use select() instead of sleep()?

the way to check if a character array is empty

Language/C 2012. 6. 13. 18:33

char text[50];

if(strlen(text) == 0) {}
if(text[0] == '\0') {}
 memset(text, 0, sizeof(text));
 
if(strlen(text) == 0) {}
text[0] == 0;

'Language > C' 카테고리의 다른 글

가변인수 va_start()  (0) 2012.06.11
typedef & struct (2)  (0) 2012.05.29
sscanf  (0) 2012.05.29
typedef & struct (1)  (0) 2012.05.23
inconv source (iconv_open, iconv, iconv_close)  (0) 2012.05.16
:

가변인수 va_start()

Language/C 2012. 6. 11. 10:54

11-3.가변 인수

.가변 인수 함수

여기서는 가변 인수 함수에 대해서 알아 본다가변 인수의 함수를 만드는 방법에 대해서는 물론이고가변 인수 함수가 동작하는 원리에 대해서도 자세하게 분석해  것이다조금 어렵기는 하지만 포인터를 적절하게 활용하는 예를   있으며 포인터로 어떤 일이 가능한지를 경험할  있는 좋은 기회가 것이다가변 인수 함수가 어떻게 동작하는지를 설명할  있다면 포인터를 정복했다고 생각해도 좋다.

가변 인수란   그대로 인수의 개수와 타입이 미리 정해져 있지 않다는 뜻이며 그런 인수를 사용하는 함수를 가변 인수 함수라고 한다가변 인수 함수의 가장 좋은 예는 C언어의 가장 기초 함수인printf이다. C언어를 배우는 사람이 가장 먼저 배우는 친근한 함수이므로  함수를 통해 가변 인수 함수를 어떻게 사용하는지 연구해 보자 함수는 서식 문자열과 서식에 대응되는 임의 타입의 인수들을개수에 상관없이 전달받을  있다다음이 printf 함수의 호출 예이다.

 

printf("정수는 %d이고 실수는 %f이다.",i,d);

printf("이름=%s, 나이=%d, =%f","김상형",25,178.8);

printf("%d + %f = %f", 123, 3.14, 123+3.14);

 

printf 함수로 전달되는 인수의 개수와 타입이 모두 다르지만 정상적으로 컴파일되고 실행된다반면gotoxy(10,15,"quickly") strcpy(src,dest,3) 따위의 호출은 당장 컴파일 에러로 처리된다이런 함수들은 가변 인수를 받아들이지 않기 때문에 헤더 파일에 적힌 원형대로 정확하게 인수의 개수와 타입을맞춰서 호출해야 한다인수가 남아서도 안되며 모자라도 안되고 타입이 틀려도 에러로 처리된다그렇다면 printf 함수의 원형은 어떻게 선언되어 있길레 가변 인수를 처리할  있을까다음이 printf 함수의원형이다.

 

int printf( const char *format, ... );

 

 함수의  번째 인수는 format이라는 이름의 문자열 상수인데 흔히 서식 문자열이라고 부른다 번째 이후의 인수에는 타입과 인수 이름이 명시되어 있지 않으며 대신 생략 기호(ellipsis) ... 적혀 있다생략 기호는 컴파일러에게 이후의 인수에 대해서는 개수와 타입을 점검하지 않도록 하는데  기호에 의해 가변 인수가 가능해진다.

컴파일러는 ... 이후의 인수에 대해서는 개수가 몇개든지 어떤 타입이든지 상관하지 않고 있는 그대로함수에게 넘겨주므로 얼마든지 많은 임의 타입의 인수들을 전달할  있다대신 전달된 인수의 정확한타입을 판별하여 꺼내쓰는 것은 함수가 알아서 해야 한다컴파일러는 인수를 마음대로 취할  있도록허락은  주지만(사실은 허락이 아니라 무관심이다뒷일에 대해서는 절대로 책임지지 않는다.

생략 기호 이전에 전달되는 인수를 고정 인수라고 하는데 printf 함수의 경우 format 인수가 바로 고정인수이다고정 인수는 원형에 타입과 개수가 분명히 명시되어 있으므로 원형대로 정확하게 전달해야한다. printf 아무리 가변 인수를 지원한다고 하더라도 printf(1, 2) printf(3.14) 따위의 호출은 안된다. printf  번째 인수는 반드시 const char * 타입의 서식 문자열이어야 하며  번째 인수부터 가변 인수이다.

가변 인수 함수를 사용하는 것은 별로 어렵지 않다. printf 함수의 경우 고정 인수인 서식 문자열을 먼저전달하고 서식의 개수와 타입에 맞는 인수들을 순서대로 전달하기만 하면 된다그렇다면 이런 가변 인수를 취할  있는 함수는 어떻게 만드는지 알아보자관건은 자신에게 전달된 임의 타입의 인수들을순서대로 꺼내서 정확한 값을 읽는 것이다가변 인수 함수의 개략적인 구조는 다음과 같다.

 

void VarFunc(int Fix, ...)

{

   va_list ap;

   va_start(ap,Fix);

   while (모든 인수를  읽을 때까지) {

      va_arg(ap,인수타입);

   }

   va_end(ap);

}

 

물론 함수의 이름이나 원형고정 인수의 개수 등은 필요에 따라 마음대로 작성할  있다마지막 인수자리에 ... 있으면 가변 인수 함수가 된다가변 인수 함수 내부에서는 인수를 읽기 위해 이상한 모양의 매크로 함수들을 많이 사용하는데  문장들을 각각 분석해 보자.

 

va_list ap

함수로 전달되는 인수들은 스택(Stack)이라는 기억 장소에 저장되며 함수는 스택에서 인수를 꺼내 쓴다스택에 있는 인수를 읽을  포인터 연산을 해야 하는데 현재 읽고 있는 번지를 기억하기 위해va_list형의 포인터 변수 하나가 필요하다변수 이름은 ap 되어 있는데 ap 어디까지나 지역 변수일 뿐이므로 이름은 마음대로 정할  있되 관습적으로 가변 인수를 다루는 매크로에서는 ap라는 이름을 사용한다. va_list 타입은 char *형으로 정의되어 있다가변 인수를 읽기 위한 포인터 변수를 선언했다고 생각하면 된다.

va_start(ap,마지막고정인수)

 명령은 가변 인수를 읽기 위한 준비를 하는데 ap 포인터 변수가  번째 가변 인수를 가리키도록 초기화한다 번째 가변 인수의 번지를 조사하기 위해서 마지막 고정 인수를 전달해 주어야 한다. va_start 내부에서는 마지막 고정 인수 다음 번지로 ap 맞추어 주므로 이후부터 ap 번지를 읽으면 순서대로 가변 인수를 읽을  있다.

va_arg(ap,인수타입)

가변 인수를 실제로 읽는 명령이다. va_start ap  번째 가변 인수 번지로 맞추어 주므로 ap 위치에 있는 값을 읽기만 하면 된다, ap 번지에 있는 값이 어떤 타입인지를 알아야 하므로  번째 인수로 읽고자 하는 값의 타입을 지정해 주어야 한다예를 들어 ap 위치에 있는 정수값을 읽고자 한다면va_arg(ap, int) 호출하고 실수값을 읽고자 한다면 va_arg(ap, double)이라고 호출하면 된다 명령은 ap위치에서 타입에 맞는 값을 읽어 리턴해 주며 또한 ap 다음 가변 인수 위치로 옮겨준다그래서 va_arg 반복적으로 호출하면 전달된 가변 인수를 순서대로 읽을  있다.

그런데  명령에서 조금 이상한 점을 발견할  있는데 int double같은 타입 이름이 어떻게 함수의인수로 전달될  있는가 하는 점이다함수의 인수로는 값이 전달되는 것이 정상인데 타입명이 어떻게함수의 인수가   있는가 말이다타입명은 분명히 함수의 인수가   없다그럼에도 불구하고va_arg 타입명을 인수로 받아들일  있는 이유는 va_arg 진짜 함수가 아니라 매크로 함수이기 때문이다. va_arg  번째 인수는 내부적으로 sizeof 연산자와 캐스트 연산자로 전달되기 때문에 타입명이   있다.

va_end(ap)

 명령은 가변 인수를  읽은  뒷정리를 하는데 별다른 동작은 하지 않으며 실제로 없어도 전혀 지장이 없다 명령이 필요한 이유는 호환성 때문인데 플랫폼에 따라서는 가변 인수를 읽은 후에 뒷처리를  주어야  필요가 있기 때문이다적어도 인텔 계열의 CPU에서는 va_end 아무 일도 하지 않는다그러나 다른 플랫폼이나 미래의 환경에서는 va_end 중요한 역할을  수도 있으므로 호환성을위해서는 관례적으로 넣어 주는 것이 좋다.

 

여기까지 설명을 읽고 "그렇군가변 인수 함수 만들기 무지 쉽군"이라고 한번에 이해할  있는 사람은 많지 않을 것이다 매크로들을 사용하는 방법과 정확한 동작 원리는   연구해 봐야  과제이다일단 실제로 동작하는 가변 인수 함수를 하나 만들어 보자다음 예제의 GetSum 함수는  번째인수로 전달된 num 개수만큼의 정수 인수들의 합계를 구해 리턴해 준다.

 

  : GetSum

#include <Turboc.h>

 

int GetSum(int num, ...)

{

   int sum=0;

   int i;

   va_list ap;

   int arg;

 

   va_start(ap,num);

   for (i=0;i<num;i++) {

      arg=va_arg(ap,int);

      sum+=arg;

   }

   va_end(ap);

   return sum;

}

 

void main()

{

   printf("1+2=%d\n",GetSum(2,1,2));

   printf("3+4+5+6=%d\n",GetSum(4,3,4,5,6));

   printf("10~15=%d\n",GetSum(6,10,11,12,13,14,15));

}

 

GetSum 함수의  번째 인수 num 전달될 정수 인수의 개수를 가지는 고정 인수이며  인수 다음에num개의 정수값을 나열해 주면 된다인수의 개수가 몇개이든간에 전달된 모든 값의 합계를 더해 리턴해  것이다실행 결과는 다음과 같다.

 

1+2=3

3+4+5+6=18

10~15=75

 

num 다음의 가변 인수가 1개든, 10개든, 100개든 GetSum 함수는 전달된 모든 인수의 합계를 구해 것이다. GetSum 함수에서 가변 인수들을 어떻게 읽는지 분석해 보자. va_list형의 포인터 ap 선언하고 va_start(ap,num) 호출로 ap 마지막 고정 인수 num 다음의 위치그러니까  번째 가변 인수를가리키도록 초기화했다그리고 num만큼 루프를 돌면서 va_arg(ap,int) 호출로 ap 위치에 있는 int값을계속 읽어 sum 누적시킨다.

모든 가변 인수를  읽었으면 va_end(ap) 뒷정리를 하고 계산된 sum값을 리턴하였다앞에서 보인기본 형식대로 va_ 매크로를 사용하여 가변 인수를 읽어 처리하기만 하면 되므로 사용만을 목적으로한다면 그리 어렵지 않다.

.가변 인수 함수의 조건

가변 인수 함수는 인수의 개수와 타입에 대한 제약이 없지만 그렇다고 해서 아무 인수나 마음대로 전달할  있는 것은 아니다가변 인수 함수에도 지켜야  규칙들이 있는데  규칙에 대해 알아 보자.

 

 가변 인수 함수는 반드시 하나 이상의 고정 인수를 가져야 한다 번째 인수부터 가변 인수일 수는없는데 왜냐하면 가변 인수를 읽기 위한 포인터 ap 초기화하기 위해서 마지막 고정 인수의 번지를 알아야 하기 때문이다. va_start 매크로는 마지막 고정 인수의 번지에 길이를 더해 가변 인수가 시작되는번지를 계산하는데 고정 인수가 없으면  매크로가 동작하지 않는다. GetSum 함수는 인수의 개수를전달하는 num 고정 인수를 가지며 printf 함수도 서식 문자열을  번째 인수로 가진다.

만약 고정 인수를 가지지 않는 가변 인수 함수를  만들고 싶다면 va_ 매크로를 쓰는 대신 스택을 직접 뒤지는 방법을 사용할 수는 있다하지만 컴파일러마다 함수를 호출할  스택을 조작하는 방법이다르고 어셈블리를 직접 사용해야 하기 때문에 일반적으로 불가능하다고 보는 편이 옳다또한 바로 다음의 2, 3 규칙을 만족하기 위해서도 고정 인수가 필요하다가변 인수들을 일관된 방법으로 읽기 위해서는 반드시 하나 이상의 고정 인수가 있어야 한다.

 함수 내부에서 자신에게 전달된 가변 인수의 개수를   있도록 해야 한다전달될  있는 인수의개수에는 제한이 없으며 컴파일러는 함수가 호출될  인수의 개수를 점검하지도 않는다그래서 호출측에서 가변 인수가 몇개나 전달되었는지를 알려 주지 않으면 함수 내부에서 인수의 개수를   있는방법이 전혀 없다함수 스스로 인수의 개수를 파악할  있도록 호출측이 정보를 제공해야 한다.

GetSum 함수는  번째 고정 인수 num 통해 뒤쪽의 가변 인수가 몇개나 전달되었는지를 알려 주도록 되어 있으며 함수 내부에서는 num만큼 루프를 돌면서 va_arg 인수들을 읽었다만약 num 인수가없다면 GetSum 함수는 루프를 얼마만큼 돌아야   결정할  없을 것이다.

GetSum 함수의 예처럼 가변 인수의 개수를 고정 인수로 알려 주는 것은 가장 쉽기는 하지만 개수를 바꿀 때마다 고정 인수를 수정해야 하므로 불편할 수도 있다고정 인수로 개수를 전달하는 것이 귀찮다면 가변 인수의 목록 끝에 특이값을 전달하는 방법을  수도 있는데 예를 들어 인수값  0 만나면 값을 가변 인수의 끝으로 인식하도록 약속을 하는 것이다이런 방법으로 GetSum 함수를 수정해 보았다.

 

  : PrintSum

#include <Turboc.h>

 

void PrintSum(const char *msg, ...)

{

   int sum=0;

   va_list ap;

   int arg;

 

   va_start(ap,msg);

   for (;;) {

      arg=va_arg(ap,int);

      if (arg == 0) {

          break;

      }

      sum+=arg;

   }

   va_end(ap);

   printf(msg,sum);

}

 

void main()

{

   PrintSum("1+2=%d\n",1,2,0);

   PrintSum("3+4+5+6=%d\n",3,4,5,6,0);

   PrintSum("10~15=%d\n",10,11,12,13,14,15,0);

}

 

GetSum 함수를 수정한 PrintSum 함수는 서식 문자열과 여러 개의 정수값들을 인수로 전달받되 가변인수의 끝에는 0 두어 0 만날 때까지 모든 인수의 값을 합해 메시지를 직접 출력한다함수 내부의루프는 무한 루프로 수정되었으며 읽은 인수값이 0 때까지 루프를 돌도록 했다실행 결과는 앞에서만든 예제와 동일하다.

GetSum 함수는 가변 인수의 개수를 고정 인수를 통해 직접적으로 알려 주도록 했으며 PrintSum 함수는 개수는 알려 주지 않되 가변 인수의 끝을 알려 주는 특별한 표지값을 전달함으로써  값이 나올 때까지 가변 인수를 취할  있도록 했다어떤 방법을 쓰든지 어쨋든 함수 내부에서 가변 인수의 개수를  있도록만  주면 된다.

그렇다면 표준 함수인 printf 인수의 개수를 어떻게 파악할까개수를 전달해 주는 고정 인수도 없고끝을 나타내는 특이값도 없어서 함수 내부에서 가변 인수의 개수를   없는  같다그러나 자세히관찰해 보면 서식 문자열에 포함된 서식의 개수가 바로 가변 인수의 개수와 일치한다는 것을   있다. printf  번째 고정 인수로 전달되는 서식 문자열에서 %d, %f, %s 같은 서식의 개수만큼 가변 인수를 읽음으로써 사실상 가변 인수의 개수를 전달받는다.

 개수와 마찬가지로 함수 내부에서 각각의 가변 인수 타입을   있어야 한다. GetSum이나 PrintSum함수처럼 모든 인수를 정수형으로 고정하든가 아니면  번째 번째는 실수 번째 이후는 모두 정수라는 식으로 미리 약속이 되어 있어야 한다. printf 대응되는 서식으로부터 가변 인수의 타입을 판별하는데 %d 제일 처음 나왔으면  번째 가변 인수는 정수다음으로 %f 나왔으면  번째 가변인수는 실수라는 것을 알게 된다.

가변 인수들의 타입을 알아야 하는 이유는 va_arg 매크로가 ap번지에서 가변 인수를 읽을  얼마만큼읽어서 어떤 타입으로 해석해야  지를 알아야 하기 때문이다가변 인수의 타입을 전달하는 방식도여러 가지를 생각할  있는데 printf 같이 하나의 고정 인수를 통해 모든 가변 인수의 타입을 판단할 있는 힌트를 제공하는 방식이 가장 좋다.

다음 예제의 GetSum2 함수는 types 고정 인수에 이후 전달되는 가변 인수들의 타입을 문자열로 전달한다정수형에 대해서는 i, 실수형에 대해서는 d라는 문자를 할당해서  문자들을 순서대로  적어주는 것이다예를 들어 types "iidd"라면    인수는 정수형이고 뒤쪽  인수는 실수형이며  가변 인수는 4개라는 정보가 전달된다.

 

  : GetSum2

#include <Turboc.h>

 

double GetSum2(const char *types, ...)

{

   double sum=0;

   va_list ap;

   const char *p;

 

   va_start(ap,types);

   for (p=types;*p;p++) {

      switch (*p) {

      case 'i':

          sum+=va_arg(ap,int);

          break;

      case 'd':

          sum+=va_arg(ap,double);

          break;

      }

   }

   va_end(ap);

   return sum;

}

 

void main()

{

   printf("1+2=%f\n",GetSum2("ii",1,2));

   printf("2.5+3.8+4=%f\n",GetSum2("ddi",2.5,3.8,4));

   printf("1+2.345+6+7.8901=%f\n",GetSum2("idid",1,2.345,6,7.8901));

}

 

types 고정 인수를 통해 인수의 개수와 타입까지도 한꺼번에 전달할  있기 때문에 정수형실수형을마구 섞어서 전달해도 함수 내부에서 다양한 타입의 인수들을 제대로 읽을  있을 것이다실행 결과는 다음과 같다.

 

1+2=3.000000

2.5+3.8+4=10.300000

1+2.345+6+7.8901=17.235100

 

GetSum2 함수에서는 types 길이만큼 루프를 돌되  문자열의 처음부터 순서대로 문자를 읽으면서i이면 va_arg(ap, int) 인수를 읽고 d이면 va_arg(ap, double) 인수를 읽었다정수실수 외에도 다양한 타입을 전달하고 싶다면 types 의미를 확장하고 switch문의 case 늘려 주면 된다.

 

규칙들이 다소 복잡하다고 느껴질  모르겠지만  생각해 보면 지극히 당연한 규칙들 뿐이다함수내부에서 전달된 인수의 개수나 타입을 전혀   없다면 값을 정확하게 읽지 못하므로 이런 규칙이필요하다규칙만 지킨다면 인수에 대한 정보를 알려 주는 방법에 대해서는 자유를 누릴  있다.

가변 인수 함수는 인수의 개수나 타입에 대해 호출측에서 자유롭게 결정할  있는 편리한 함수이다.그러나 자유에는 언제나 책임이 따르는 법이라 규칙을 제대로 지키지 않았을 때의 결과에 대해 컴파일러는 어떠한 책임도 지지 않는다가변 인수 함수를 잘못 호출했을  어떤 결과가 발생하는지 다음 예제를 실행해 보자.

 

  : printfCall

#include <Turboc.h>

 

void main()

{

   delay(1000);printf("%d%d\n",1,2);

   delay(1000);printf("%d%d%d\n",1,2,3,4,5);

   delay(1000);printf("%d%d\n",1,3.14);

   delay(1000);printf("%f%f\n",1,2);

   delay(1000);printf("%s\n",1);

}

 

다섯 개의 printf 함수 호출문이 있고 결과를 천천히 감상할  있도록 1초씩 시간을 지연시키도록 했다.순서대로  호출문들을 분석해 보자 번째 호출문은  개의 정수를 출력하되 서식과 인수의 개수,타입이 정확하게 일치하며 따라서  호출문은 아주 정상적으로 처리될 것이다 번째 호출문에는 서식이   밖에 없지만 인수는 다섯 개나 전달되었다 경우도 정상 실행되는데 모자라는 것은 문제가 되지만 남는 것은 무시해 버리면 되므로 문제가 되지 않는다.

 번째 호출문은 %d 서식  개를 가지고 있지만 실제 전달된 인수는 정수 상수 하나와 실수 상수 하나여서 개수는 맞지만 타입이 일치하지 않는다 경우 printf  번째 가변 인수를 정수형으로 읽기때문에 3.14 억지로 정수형으로 바꾸어 출력하게 된다이상 동작을 하지는 않지만 어쨋든 원하던 결과는 아닐 것이다.

 번째 호출문은  개의 %f 서식을 가지고 있지만 가변 인수는   정수형이다 경우 정수형 값을 8바이트의 실수형으로 읽으려고 시도하게 되는데 원하는 결과도 나오지 않을 뿐더러 잘못하면 다운될 수도 있다 코드가 다운될 것인가 아닌가는 순전히 운의 문제이되 요행스럽게도 스택에는 자유공간이 많이 남아 있기 때문에 8바이틀  읽는다고 해서 쉽게 다운되지는 않는다.

마지막 호출문은 심각한데 서식은 %s 되어 있어 가변 인수가 문자열인 것으로 전달되지만 실제 전달된 인수는 정수형이다따라서 불쌍한 printf 정수 1 포인터로 해석하여  위치의 문자열을 읽으려고 시도하는데 절대 번지 1 시스템 영역이기 때문에  자리에서 즉사해버린다. 32비트의 보호된 환경에서는 허가되지 않은 영역을 읽으려고   운영체제가 강제로 프로세스를 종료해 버리기 때문이다.

보다시피 가변 인수 함수를 잘못 쓰면 이렇게 위험해질  있다그러나 이런 위험한 코드를 작성했음에도 불구하고  예제를 컴파일하면 컴파일러는 뻔뻔스럽게도 0 error 0 warning이라는 결과를 보여준다컴파일러는  코드가 위험한지 아닌지를 판별할 능력도 없고 권한도 없다. printf 함수의 원형에는 ...이라고 되어 있어 인수에 대해서는 개수든 타입이든 간섭하지 말라고 했기 때문이다그러니 가변인수 함수는 사용하는 사람이 주의하는 수밖에 없다.

.매크로 분석

가변 인수 함수는 만드는 방법과 주의 사항 등에 대해 알아 봤는데 사용만을 목적으로 한다면 여기까지만 이해해도 충분하다그러나 가변 인수를 읽어 내는 매크로들이 어떤 식으로 동작하는지포인터를어떻게 조작하길레 임의 타입의 인수를 자유 자재로 읽을  있는지 호기심이 발동한다면 va_ 매크로를 분석해 보도록 하자 매크로들은 길이가 짧지만 포인터와 sizeof 연산자그리고 비트 연산자들이어떻게 절묘하게 동작하는지 감상해   있는 좋은 예제이다.

가변 인수와 관련된 타입과 매크로는 표준 헤더 파일 stdarg.h 정의되어 있다 헤더 파일을 직접열어 보면 플랫폼별로 va_ 매크로들이 각각 작성되어 있는데 대부분의 경우 인텔 계열의 CPU 사용하고 있으므로 매킨토시나 알파, MIPS 같은 경우는 무시하고 X86 계열의 경우만 분석해 보도록 하자.실제 매크로 구문은 컴파일러마다 조금씩 다른데 아래 코드는 가장 간략하게  정리되어 있다고 생각되는 비주얼 C++ 6.0 stdarg.h 헤더 파일에 기록된 내용이다.

 

typedef char *  va_list;

#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

#define va_start(ap,v)  ( ap = (va_list)&v + _INTSIZEOF(v) )

#define va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

#define va_end(ap)      ( ap = (va_list)0 )

 

먼저 va_list 대한 타입 정의를   있는데 va_list 단순한 char *형으로 정의되어 있다여기서char 대한 포인터라는 것은 별다른 의미는 없고 증감할  1바이트씩 증감하도록 하기 위해 char포인터로 선언된 것이다실제로 어떤 컴파일러는 va_list void * 정의하는 것도 있다중요한 것은va_list 타입이 포인터 타입이라는 것이다.

_INTSIZEOF(n) 매크로는 인수로 전달된 타입 n 길이를 계산하는데 n 값에 따라  매크로의 계산결과가 어떻게 되는지 조사해 보자매크로의 연산식을 엄밀하게 분석해 보면  타입의 크기가 얼마로계산될  예측할  있지만 이럴 때는 그냥 프로그램을 하나 만들어 확인해 보는 것이  간편하고 확실하다.

 

printf("char = %d\n",_INTSIZEOF(char));

printf("short = %d\n",_INTSIZEOF(short));

printf("int = %d\n",_INTSIZEOF(int));

printf("float = %d\n",_INTSIZEOF(float));

printf("double = %d\n",_INTSIZEOF(double));

 

크기별로  타입에 대해 _INTSIZEOF 매크로가 어떤 값을 계산해 내는지 출력해 보았다결과는 다음과 같다.

 

char = 4

short = 4

int = 4

float = 4

double = 8

 

char형의 크기는 1이지만  매크로에 의해 4 계산되며 short, float 4 되며 double 8 된다. 매크로가 하는 일은 타입의 크기를 4 배수로 올림해 준다고   있는데   정확하게 표현하자면 정수형의 크기에 대한 배수로 올림한다알다시피 정수형의 크기는 시스템마다 다른데 16비트 환경에서는 2바이트이고 32비트 환경에서는 4바이트이며  크기는 또한 스택 하나의 크기이기도 하다.

결국  매크로는  타입의 변수가 스택을 통해 함수로 전달될   바이트를 차지하는가를 계산해낸다. char형이 1바이트라도 함수의 인수로 전달될 때는 int형으로 확장되므로 스택에는 4바이트로 들어가며 _INTSIZEOF 인수가 스택에 들어가 있을 때의 크기를 계산해 내는 것이다아주 간단한 동작을하는 매크로이지만 플랫폼에 따른 스택의 크기까지 고려하여 이식성을 보장할  있도록  작성되어있다. 4바이트의 배수 타입에 대해서 _INTSIZEOF sizeof  연산자와 실질적으로 동일하다.

va_start 매크로는 가변 인수의 위치를 가리키는 포인터 ap 초기화하는데  초기화를 위해 마지막고정 인수 v 전달해 주어야 한다. ap 마지막 고정 인수 v 번지에 v 크기를 더한 번지로 초기화된다스택에 인수가 들어갈 때는 전달된 역순으로 들어가므로 가변 인수들이 먼저 전달(높은 번지)되고 고정 인수가 제일 끝에 전달(낮은 번지)된다따라서 가변 인수 함수가 호출된 직후의 스택 모양은다음과 같다.

 상태에서 &v 고정 인수의 번지를 가리키며  번지를 char * 캐스팅한  고정 인수의 길이만큼더하면 바로 아래에 있는  번째 가변 인수의 번지를 구할  있다. va_start 매크로는  연산을 통해ap 가변 인수의 시작 번지를 초기화한다이후 ap 있는 값을 읽으면 가변 인수의 값을 구할  있는데  동작을 하는 매크로가 바로 va_arg 매크로이다.

va_arg 함수는 ap 일단 가변 인수의 길이만큼 더해 다음 가변 인수 번지로 이동시킨다그리고 다시길이를 빼서 원래 자리로 돌아온   번지를 t타입의 포인터로 캐스팅하여 * 연산자로  값을 읽는다 매크로는 ap 값을 읽기만 하는 것이 아니라 다음번 va_arg 호출을 위해 ap 방금 읽은 가변인수 다음의 번지로 옮겨 주는 동작까지 해야 하기 때문에 길이를 더했다가 다시    위치를 읽도록 되어 있다 매크로의 동작을 그림으로 그려 보면 다음과 같다.

중간 변수를 사용하지 않고 매크로  줄로 값을 읽기도 하고 ap 다음 위치로 옮겨 놓기도 해야 하므로 조금 복잡하게 되어 있는데 매크로 구문의 연산 순서에 따라 어떤 동작들이 일어나는지를 보도록 하자.

va_arg(ap,t) 호출문은 ap 번지에 있는 가변 인수를 t 타입으로 읽고  길이만큼 ap 증가시켜 다음가변 인수를 읽을  있도록 한다그래서 va_arg 계속 호출하면 가변 인수들을 연속적으로 액세스할 있다, va_arg 정확하게 읽고 길이만큼 다음 위치로 이동하기 위해서는 가변 인수의 타입을 반드시 알려 주어야 한다.

마지막으로 va_end 매크로는 가변 인수를 가리키던 ap 포인터를 NULL 만들어 무효화시키는데 사실 동작은 굳이 필요치 않다어차피 ap 지역 변수로 선언되었고 함수가 종료되면 사라지므로 어떤값을 가지더라도 아무 문제가 없으며 실제로 va_end 호출을 빼도  문제없이  동작한다. va_end 매크로는 미래의 플랫폼에서 가변 인수를 읽는 방법이 달라질 경우 뒷정리를   있는 위치를 확보하는역할 이외에는 아무 의미가 없다.

가변 인수 함수의 예제로 최초 작성했던 GetSum 함수를 매크로를 쓰지 않고 전개해서 간략하게 다시작성해 보면 다음과 같다동작은 완벽하게 동일하다.

 

int GetSum(int num, ...)

{

   int sum=0;

   int i;

   // va_list ap;

   char *ap;

   int arg;

 

   // va_start(ap,num);

   ap=(char *)&num+sizeof(num);

   for (i=0;i<num;i++) {

      // arg=va_arg(ap,int);

      arg=*(int *)ap;

      ap+=sizeof(int);

      sum+=arg;

   }

   // va_end(ap);

   return sum;

}

 

보다시피 가변 인수 함수는 포인터 연산, sizeof 연산자캐스트 연산자들의 절묘한 조합에 의해 동작한다는 것을   있다 동작을   쓰기 쉽고 호환성과 이식성에 유리하도록 정리해 놓은 것이 바로 va_ 매크로이다.

.가변 인수 함수의 활용

가변 인수 함수는  줄에 여러 개의 정보를 다양한 방법으로 다룰  있다는 면에서 편리하다. printf함수는 다양한 타입의 변수들을 한꺼번에 출력할  있어 변수값을 확인해   아주 유용하다이런함수를 직접 만들려면 독자적으로 서식을 정의하고 서식 문자열과 대응되는 가변 인수를 직접 읽는 복잡한 루틴을 만들어야 하는데 다행히 이런 일을 대신해 주는 함수들을 준비되어 있다대표적으로 다음 함수만 소개 한다.

 

int vprintf( const char *format, va_list argptr );

int vsprintf( char *buffer, const char *format, va_list argptr );

 

이외에 vscanf, vsscanf 등의 함수도 있는데 알파벳 v 시작한다고 해서 이런 함수들을 v계열의 함수라고 한다  함수들은 printf, sprintf 동일한 기능을 수행하는데 가변 인수를 직접 나열하는 대신가변 인수가 시작되는 번지만을 인수로 취한다는 점이 다르다 실제로 가변 인수를 취하지는 않으며가변 인수를 취하는 다른 함수의 내부에서 printf  서식을 해석하고 적용하는 일을 대신해 준다.

  함수를 사용하면 printf처럼 동작하는 비슷한 함수를 직접 만들어   있다다음 함수는C/C++언어의 가변 인수 기능을 활용하여 실행중에 변수값을 디버거로 실시간 확인해 보는 기능을 제공한다.

 

void CustomTrace(char *format, ...)

{

   char buf[1024];

   va_list marker;

  

   va_start( marker, format );

   vsprintf(buf,format,marker);

   OutputDebugString(buf);

}

 

OutputDebugString이라는 API 함수가 사용되었는데  함수는 주어진 문자열을 디버깅 창으로 출력해준다비주얼 C++ 경우 Output 윈도우에  함수의 출력 내용이 나타나므로 실행중에 변수값의 변화를 확인하거나 특정 함수의 호출 시점회수 등을 알고 싶을  중간 중간에  함수를 삽입해 주면 된다사용예를 들자면 다음과 같다.

 

CustomTrace("변수 a=%d, 변수 f=%f\n",a,f);

CustomTrace("함수 func %d번째 호출되었음",count++);

 

CustomTrace 함수의 내부는 무척 간단하다. va_start  번째 가변 인수의 번지를 구한   번지를서식 문자열과 함께 vsprintf 함수로 넘겨 주기만 하면 된다. OutputDebugString 함수를 직접 사용할 있지만  함수는 단순한 문자열만 출력할  있는데 비해 CustomTrace 서식화된 문자열을 출력할 있어 훨씬  편리하다.

다음은 똑같은 목적의   복잡한 함수를 소개한다 함수는 Win32 파일 입출력 함수까지 사용하고있기 때문에 현재 단계에서 분석해 보기는 어려우므로 차후에 API 배운 후에 직접 분석해 보기 바란다.

 

#define DEBUGLOGFILE "c:\\DebugLog.txt"

void WriteLogFile(char *strLog,...)

{

   HANDLE hLog;

   static int count=0;

   DWORD dwWritten;

   char szLog[1024];

   char strLog2[1024];

   va_list marker;

   SYSTEMTIME st;

 

   // 가변 인수를 조립한다.

   va_start( marker, strLog );

   vsprintf(szLog,strLog,marker);

 

   // 처음 호출될  파일을 만들고 이후부터는 파일을 열기만 한다.

   if (count == 0) {

      hLog=CreateFile(DEBUGLOGFILE,GENERIC_WRITE,0,NULL,

      CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);

   } else {

      hLog=CreateFile(DEBUGLOGFILE,GENERIC_WRITE,0,NULL,

      OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);

   }

 

   // 로그에 현재 시간과 카운터를 기록한다.

   GetLocalTime(&st);

   wsprintf(strLog2,"카운터=%06d(%d:%d:%d:%d) %s\r\n",count++,

      st.wHour,st.wMinute,st.wSecond,st.wMilliseconds,szLog);

   SetFilePointer(hLog,0,NULL,FILE_END);

   WriteFile(hLog,strLog2,strlen(strLog2),&dwWritten,NULL);

   CloseHandle(hLog);

}

 

사용하는 방법은 printf CustromTrace 동일하다차이점이라면 조립된 서식 문자열이 화면이나디버깅 창으로 출력되는 것이 아니라 지정한 파일에 기록된다는 것과 카운트호출 시점의 시간 등을같이 기록해 준다는 점이다멀티 스레드 환경이나 실시간으로 동작하는 프로그램을 디버깅할 때는 디버거를 쓰기 쉽지 않기 때문에 모든 디버깅 정보를 파일에 일단 기록한  파일에 남겨진 로그 정보를분석하는 것이  효율적이다.

이럴   함수가 아주 유용하게 사용되며 실전에서 여러  활용했었는데 효과가 아주 좋았다릴리즈 모드에서만 증상이 나타날 때라든가 디버거를   없는 서비스류의 프로그램을 디버깅할  특히많은 활약을 한다물론  함수는  개선해  여지가 많은데 다음에 실력이 늘면 직접 개선해 보기바란다.


출처 - http://blog.naver.com/endfirst?Redirect=Log&logNo=20003220224

 

[출처] 가변인수 va_start()|작성자 그냥

'Language > C' 카테고리의 다른 글

the way to check if a character array is empty  (0) 2012.06.13
typedef & struct (2)  (0) 2012.05.29
sscanf  (0) 2012.05.29
typedef & struct (1)  (0) 2012.05.23
inconv source (iconv_open, iconv, iconv_close)  (0) 2012.05.16
:

typedef & struct (2)

Language/C 2012. 5. 29. 15:12

typedef unsigned int UINT;
 
이 구문은 int형의 자료형을 UINT이란 이름으로써 자료형으로 쓰겠다는 말입니다.
UINT i;
라는 식으로 자료형자체로 사용해서 인스턴스를 생성할 수 있는거죠
이건 아실겁니다.
 
//(1)
struct _tagPoint{
     int x; 
     int y;
}POINT;
 
//(1-1)
struct _tagPoint{
     int x; 
     int y;
};
 
//(2)
typedef struct _tagPoint{
     int x; 
     int y;
}POINT;
 
//(2-1)
typedef struct{
     int x; 
     int y;
}POINT;
 
 
하나씩 설명하겠습니다.
(1)에서는
POINT라는 인스턴스를 생성한것입니다.
POINT.x로서 사용할 수 있다는 말입니다.
 
(1-1)에서는 
선언된 인스턴스가 없습니다.
struct _tagPoint POINTS;
POINTS s;
s.x로서 사용할 수 있습니다.
 
 
(2)에서는
구조체자체를 POINT라는 자료형으로 정의한 것입니다.
POINT s;
s.x로서 사용할 수 있습니다.
 
(2-1)도 마찬가지지만
tag명을 생략하고 사용가능합니다.
(2)와의 차이점은 
struct xxx s;
s.x로서 사용하실 수 없습니다.
오로지 POINT s;
s.x로서 사용하실 수 있습니다.
 
정리하면 
typedef 문을 사용하시게 되면
인스턴스가 생성되는게 아닙니다. 자료형이 하나 정의되게 되는 것입니다.


출처 - http://tarakuny.egloos.com/4428318

'Language > C' 카테고리의 다른 글

the way to check if a character array is empty  (0) 2012.06.13
가변인수 va_start()  (0) 2012.06.11
sscanf  (0) 2012.05.29
typedef & struct (1)  (0) 2012.05.23
inconv source (iconv_open, iconv, iconv_close)  (0) 2012.05.16
:

sscanf

Language/C 2012. 5. 29. 10:33

다음 예제에서는 구분 쉼표 (,) 필드와 내부 버퍼에서 읽을 sscanf() C 런타임 함수를 사용하는 방법을 보여 줍니다. 키가 sscanf() 함수 형식으로 대괄호를 사용합니다. 형식 % 됩니다 [^ ','], 있는 쉼표 (,) 도달할 때까지 버퍼에서 읽을 함수를 알려 줍니다.

예제 코드
/* The following sample illustrates the use of brackets and the
   caret (^) with sscanf().
   Compile options needed: none
*/ 

#include <math.h>
#include <stdio.h>
#include <stdlib.h>

char *tokenstring = "first,25.5,second,15";
int result, i;
double fp;
char o[10], f[10], s[10], t[10];

void main()
{
   result = sscanf(tokenstring, "%[^','],%[^','],%[^','],%s", o, s, t, f);
   fp = atof(s);
   i  = atoi(f);
   printf("%s\n %lf\n %s\n %d\n", o, fp, t, i);
}

출처 - http://support.microsoft.com/kb/38335/ko

'Language > C' 카테고리의 다른 글

가변인수 va_start()  (0) 2012.06.11
typedef & struct (2)  (0) 2012.05.29
typedef & struct (1)  (0) 2012.05.23
inconv source (iconv_open, iconv, iconv_close)  (0) 2012.05.16
inconv.h  (0) 2012.05.16
:

typedef & struct (1)

Language/C 2012. 5. 23. 17:55

먼저 typedef와 struct 지시어에 대해 먼저 논해보자.

<typedef>

typedef unsigned int UINT

// 이 구문은 unsigned int의 자료형을 UINT로 쓰겠다는 의미임

.

.

unsigned int i;

UINT i;

//위의 두 선언을 같은 결과를 가져온다.

 

함수 포인터의 재정의로도 사용된다.

typedef int (*funcptr) ();

//funcptr을 리턴값이 int이고,매개인자가 없는 함수포인터로 선언

.

.

funcptr myfunction[10];

int (*myfunction[10])();

//같은 결과를 가져옴..

 

 

<struct>

struct MY_struct {

int a;

int b;

};

//구조체 선언을 나타내는 지시어 struct, MY_struct는 구조체의 이름.

// a, b는 멤버 데이터이며, 이를 묶어주는 중괄호로 구성되며, 구조체 선언끝에  ;를 명시

함수의 선언에서 {}의 의미는 메인메모리로 부터 스택영역을 할당받는 역할을 수행하지만, 구조체 선언에서 사용되는 {}는 메인메모리에 아무런 영향을 미치지 않는다. 따라서 후자를 구분하기 위해 ;를 구조체 선언끝에 명시한다.

 

구조체의 객체 생성은 다음과 같다.

1.

struct MY_struct {

int a;

int b;

};

struct MY_struct A1, *pA2;

//C compiler에서는 객체 생성시 struct지시어를 반드시 명기해야 하고, C++ compiler에서는 생략해도 C++ complier가 알아서 My_struct를 사용자 정의 데이터형으로 인식하여 객체를 선언해줌.  

//좀더 자세히 설명하면, C complier는 변수선언이 가능한 키워드(int , char , float ,void 등)로만 시작할때 변수선언이 가능합니다. 사용자 정의 변수형(enum, union, struct)의 경우 그냥 선언한 이름만 사용할 경우 컴파일러는 변수 선언을 인식하지 못한다. 따라서 C complier에게 사용자 정의 변수형이라는 것을 알려주기 위해 사용자 정의 변수형 이름앞에 지시어를 적어줘야 함. 

// 구조체의 객체중 A1은 정적 객체로 선언, pA2는 포인터 객체로 선언. 

2.

struct MY_struct {

int a;

int b;

}A1, *pA2;

 

이상으로 간단하게 typedef와 struct 지시어에 대해 간단히 알아보았다.

그럼 이제 본격적으로 typedef와 struct를 같이 사용할때의 경우를 알아보자.

 

앞에서도 언급했지만, 사용자 정의 데이터형을 선언할때 매번 지시어(enum, struct, union)를 적어주는 일은 많이 번거롭다. C complier에서는 사용자 정의 타입 이름도 typedef로 재정의가 가능하게 허용하였다.

typedef를 사용하여 재정의한 사용자 정의 데이터형의 예를 보면,.....

 

typedef struct __MY_struct{

int a;

int b;

}MY_struct, *pMY_struct;

//이 구문에서 __MY_struct는 구조체의 이름, a, b는 맴버 데이터, MY_struct와 *pMY_struct는 선언한 구조체를 정의한 데이형을 MY_struct와 *pMY_struct로 제선언.

.

.

1.

struct __MY_struct A1, *pA2;

2.

MY_struct A1;

pMY_struct pA2;

//1에서 선언한 것과 2에서 선언한 것은 같은 결과를 가져온다.

//2처럼 사용할 경우 앞에 struct라는 지시어를 사용하지 않고 바로 사용 가능

//보통 C에서는 이런식으로 사용하기 때문에 구조체의 이름은 잘 사용하지 않는 이름으로 정의하고 재정의되는 이름을 알아보기 편한 이름으로 정의한다.

 

참고적으로, 위에서 선언한 경우는 정석적인 사용이며, 오사용및 변칙사용의 예를 들어 들어 보면.... 

1.
typedef struct {
     int a; 
     int b;
}MY;


2.
struct MY{
     int a; 
     int b;
};

 

3.

typedef struct MY{
     int a; 
     int b;
};

1과 2의  경우는 C++ 에서는 같은 동작을 합니다. 하지만, C에서는 다름 결과를 냅니다.

1에서는 이름없는 구조체가 생성이 되고, 그 구조체를 사용하기 위한 새로운 자료형이름으로 MY가 등록이 됩니다.

2에서는 단지 MY라는 구조체가 생성이됩니다.

 

따라서 C에서는 다음과 같이 사용해야 합니다.

<1의 경우>

MY A1;

<2의 경우>

struct MY A1;

 

3의 경우는 에러 발생(경우에 따라서는 warning만 뜬다고 합니다.) 사용자의 의도와는 다른 방향으로 컴파일 됩니다.

typedef의 경우는 두개의 인자가 필요합니다. 앞의 인자는 컴파일러가 기존에 알고있는 지시어, 뒤에있는 인자는 사용자가 새롭게 정의할 이름입니다.

3의 경우는 2번째 인자가 없는 형태이므로 에러가 발생하거나, 그냥 구조체의 이름이 MY인 구조체만 생성하게 됩니다.

-이상-


출처 - http://blog.naver.com/kri7001?Redirect=Log&logNo=20018686572

 

[출처] typedef & struct|작성자 kri7001

'Language > C' 카테고리의 다른 글

typedef & struct (2)  (0) 2012.05.29
sscanf  (0) 2012.05.29
inconv source (iconv_open, iconv, iconv_close)  (0) 2012.05.16
inconv.h  (0) 2012.05.16
read() 파일 읽기  (0) 2012.05.11
:

inconv source (iconv_open, iconv, iconv_close)

Language/C 2012. 5. 16. 17:40

#include "stdio.h"

#include "string.h"

#include "iconv.h"

#include "errno.h"


#define BUFF_SIZE 64


int main()

{

        iconv_t cd = iconv_open("UNICODE", "UTF-8");

        if (cd == (iconv_t)(-1))

        {

                perror("iconv_open");

                return 0;

        }


        char inBuf[BUFF_SIZE] = "Hello world";

        int inBufSize = sizeof(inBuf);


        char outBuf[BUFF_SIZE];

        int outBufSize = sizeof(outBuf);

        memset(outBuf, 0, outBufSize);


        // convert

        size_t readBytes = strlen(inBuf);

        size_t writeBytes = sizeof(outBuf);

        char* in = inBuf;

        char* out = outBuf;


        printf("readBytes:%d writeBytes:%d\n",readBytes,writeBytes);


        if (iconv(cd, &in, &readBytes, &out, &writeBytes) == -1)

        {

                printf("failed to iconv errno:%d EILSEQ:%d\n", errno, EILSEQ);

        }

        else

        {

                int idx;

                printf("in:%x out:%x\n",in,out);

                printf("readBytes:%d writeBytes:%d\n",readBytes,writeBytes);

                for(idx = 0; idx < BUFF_SIZE; idx++)

                {

                        printf("%03d %c %x\t\t", idx, inBuf[idx], inBuf[idx]);

                        printf("%03d %c %x\n", idx, outBuf[idx], outBuf[idx]);

                }

                outBuf[writeBytes] = '\0';

        }


        iconv_close(cd);

        return 0;

}


출처 - http://minimonk.tistory.com/1543

'Language > C' 카테고리의 다른 글

sscanf  (0) 2012.05.29
typedef & struct (1)  (0) 2012.05.23
inconv.h  (0) 2012.05.16
read() 파일 읽기  (0) 2012.05.11
execl 다른 프로그램 실행  (0) 2012.04.26
:

inconv.h

Language/C 2012. 5. 16. 17:37

1소개 #

  • 홈페이지 : [http]http://www.gnu.org/software/libiconv/
  • 각나라의 코드페이지로 문자열을 변환해주는 라이브러리입니다. 예를 들면, 완성형->조합형, 조합형->UTF-8과 같은 것이죠. :)

2설치 #

  • mingw에 이미 빌드된 인스톨가능한 배포본이 존재합니다. gettext와 상호참조 의존성이 있으므로 같이 설치하는 것이 좋습니다.

3GNU iconv 메뉴얼 번역 #

3.1iconv_open() #

          #include <iconv.h>
          
          iconv_t iconv_open (const char* tocode, const char* fromcode);
          

3.1.1설명 #

iconv_open 함수는 문자 인코딩 fromcode에서 문자 인코딩 tocode 방식으로 문자열을 변환하기위한 변환기 핸들을 생성하는 역할을 합니다. fromcode와 tocode에 지정가능한 값과 두 값의 조합은 시스템마다 다릅니다. libiconv 라이브러리에서는 다음과 같은 인코딩이 지원되며 모두 상호조합이 가능합니다.
한국어EUC-KR, CP949, ISO-2022-KR, JOHAB
유럽ASCII, ISO-8859-{1,2,3,4,5,7,9,10,13,14,15,16}, KOI8-R, KOI8-U, KOI8-RU, CP{1250,1251,1252,1253,1254,1257}, CP{850,866}, Mac{Roman,CentralEurope,Iceland,Croatian,Romania}, Mac{Cyrillic,Ukraine,Greek,Turkish}, Macintosh
셈족 계통ISO-8859-{6,8}, CP{1255,1256}, CP862, Mac{Hebrew,Arabic}
일본어EUC-JP, SHIFT_JIS, CP932, ISO-2022-JP, ISO-2022-JP-2, ISO-2022-JP-1
중국어EUC-CN, HZ, GBK, GB18030, EUC-TW, BIG5, CP950, BIG5-HKSCS, ISO-2022-CN, ISO-2022-CN-EXT
아르메니아ARMSCII-8
그루지아Georgian-Academy, Georgian-PS
타지키스탄KOI8-T
타이어TIS-620, CP874, MacThai
라오스MuleLao-1, CP1133
베트남VISCII, TCVN, CP1258
특정 플렛폼 전용HP-ROMAN8, NEXTSTEP
유니코드UTF-8, UCS-2, UCS-2BE, UCS-2LE, UCS-4, UCS-4BE, UCS-4LE, UTF-16, UTF-16BE, UTF-16LE, UTF-32, UTF-32BE, UTF-32LE, UTF-7, C99, JAVA
uint16_t 또는 uint32_t에 기초한 유니코드(시스템 엔디안과 정렬방식에 의존적임)UCS-2-INTERNAL, UCS-4-INTERNAL
char 또는 wchar_t에 기초한 로케일 의존적(시스템 엔디안과 정렬방식에 의존적임, LC_CTYPE 로케일 facet과 운영체계 설정에 따름)char, wchar_t

--enable-extra-encodings 옵션을 주어 빌드했을 경우 다음과 같은 추가된(잘쓰이지 않는) 인코딩 방식을 사용할 수 있습니다.
유럽CP{437,737,775,852,853,855,857,858,860,861,863,865,869,1125}
셈계통CP864
일본어EUC-JISX0213, Shift_JISX0213, ISO-2022-JP-3
투르크멘 언어TDS565
특정 플렛폼 전용RISCOS-LATIN1

  • 인코딩 명을 ""와 같이 공백으로 지정하면 이는 "char"로 지정한 것과 같이 처리됩니다 : 이는 로케일 의존적인 문자 인코딩 방식을 사용한다는 뜻이 됩니다.
  • tocode에 "//TRANSLIT"을 덧붙여 지정하면 고쳐쓰기 기능이 활성화됩니다. 이는 특정 문자가 결과 문자집합에 존재하지 않을경우 비슷하게 보이는 글자중 하나로 어림잡아 대체한다는 의미입니다.
  • tocode에 "//IGNORE"를 덧붙여 지정하면, 결과 문자집합에 존재하지 않는 문자들은 아무런 경고없이 건너뛰어 처리됩니다.
  • 반환되는 변환기 핸들은 iconv()함수를 사용하여 몇번이고 실행이 가능합니다. 모든 처리가 끝나면 iconv_close()함수로 이 핸들을 닫아주면 됩니다.
  • 변환기 핸들은 변환 상태를 담고 있습니다. iconv_open() 함수를 사용하여 생성된 직후에는, 상태는 초기 상태로 지정됩니다. iconv()함수를 사용하면 핸들의 변환상태를 수정하게 됩니다. (이는 다중 쓰레드 환경에서 동시에 하나의 핸들을 공유해서 사용할 수 없다는 것을 의미합니다) 상태를 다시 초기상태로 되돌리려면, iconv()에 inbuf 매개변수에 NULL을 지정하여 실행하세요.

3.1.2반환값 #

iconv_open() 함수는 변환기 핸들을 반환합니다. 오류가 발생하면 errno에 해당 오류값을 설정하고 (iconv_t)(-1)를 반환합니다.

3.1.3errno #

EINVALfromcode에서 tocode로 변환하는 기능이 현재 구현에서 지원되지 않을 경우

3.2iconv() #

          #include <iconv.h>
          
          size_t iconv (iconv_t cd, const char* * inbuf, size_t * inbytesleft, char* * outbuf, size_t * outbytesleft);
          

3.2.1설명 #

  • 매개변수 cd는 iconv_open()함수를 사용하여 생성된 변환기 핸들이어야만 합니다.
  • 기본적인 사용법은 inbuf 및 *inbuf 모두 NULL이 아닐 경우입니다. In this case, the iconv function converts the multibyte sequence starting at *inbuf to a multibyte sequence starting at *outbuf. At most *inbytesleft bytes, starting at *inbuf, will be read. At most *outbytesleft bytes, starting at *outbuf, will be written.
  • iconv() 함수는 한번에 하나의 다중바이트 문자 하나를 변환합니다. 그리고 각각의 문자변환때마다 변환된 입력문자 바이트수만큼 *inbuf를 증가시키고 *inbytesleft를 감소시키게 되며, 변환된 출력문자 바이트수만큼 *outbuf을 증가시키고 *outbytesleft를 감소시킵니다. 그런다음 cd 변환기 핸들내의 변환 상태를 갱신합니다. 변환은 다음과 같은 4가지 경우에 정지하게 됩니다.
    1. An invalid multibyte sequence is encountered in the input. In this case it sets errno to EILSEQ and returns (size_t)(-1). *inbuf is left pointing to the beginning of the invalid multibyte sequence.
    2. The input byte sequence has been entirely converted, i.e. *inbytesleft has gone down to 0. In this case iconv returns the number of non-reversible conversions performed during this call.
    3. An incomplete multibyte sequence is encountered in the input, and the input byte sequence terminates after it. In this case it sets errno to EINVAL and returns (size_t)(-1). *inbuf is left pointing to the beginning of the incomplete multibyte sequence.
    4. The output buffer has no more room for the next converted character. In this case it sets errno to E2BIG and returns (size_t)(-1).
  • A different case is when inbuf is NULL or *inbuf is NULL, but outbuf is not NULL and *outbuf is not NULL. In this case, the iconv function attempts to set cd's conversion state to the initial state and store a corresponding shift sequence at *outbuf. At most *outbytesleft bytes, starting at *outbuf, will be written. If the output buffer has no more room for this reset sequence, it sets errno to E2BIG and returns (size_t)(-1). Otherwise it increments *outbuf and decrements *outbytesleft by the number of bytes written.
  • inbuf가 NULL 또는 *inbuf가 NULL이며, outbuf가 NULL 또는 *outbuf가 NULL인 경우에는, iconv() 함수는 변환기 핸들 cd내의 변환 상태를 초기 상태로 변경합니다.

3.2.2반환값 #

  • iconv() function returns the number of characters converted in a non-reversible way during this call; reversible conversions are not counted.
  • 오류가 발생하면 errno에 오류코드를 넣고 (iconv_t)(-1)를 반환합니다.

3.2.3errno #

E2BIG*outbuf에 공간이 부족합니다
EILSEQ부정확한 다중바이트 문자열이 입력값으로 들어왔습니다.
EINVAL완료되지않은 다중바이트문자열이 입력값으로 들어왔습니다.

3.3iconv_close() #

          #include <iconv.h>
          
          int iconv_close (iconv_t cd);
          

3.3.1설명 #

iconv_open()에서 생성된 변환기 핸들 cd를 해제합니다.

3.3.2반환값 #

'Language > C' 카테고리의 다른 글

typedef & struct (1)  (0) 2012.05.23
inconv source (iconv_open, iconv, iconv_close)  (0) 2012.05.16
read() 파일 읽기  (0) 2012.05.11
execl 다른 프로그램 실행  (0) 2012.04.26
Why use select() instead of sleep()?  (0) 2012.04.26
:

read() 파일 읽기

Language/C 2012. 5. 11. 10:00

설명

open() 함수로 열기를 한 파일의 내용을 읽기를 합니다.

헤더unistd.h
형태ssize_t read (int fd, void *buf, size_t nbytes)
인수int fd파일 디스크립터
 void *buf파일을 읽어 들일 버퍼
 size_t nbytes퍼버의 크기
반환ssize_t정상적으로 실행되었다면 읽어들인 바이트 수를, 실패했다면 -1을 반환
예제

예제에 사용할 test.txt 파일에는 아래와 같이 5바이트씩 문장이 입력되어 있습니다.

12345
ABCDE
abcde
67890

표준 출력함수 fgets() 에는 버퍼가 크더라도 파일의 첫 번째 행만 읽어서 반환하지만 read()는 버퍼의 크기만큼 읽을 수 있다면 모두 읽어 들입니다. 그러므로 read()에서 사용할 버퍼의 크기가 파일 보다 크다면 파일의 모든 내용을 읽어 들이게 됩니다.

아래는 test.txt의 파일 내용보다 버퍼의 크기가 1024로 크기가 크므로 한번에 읽기를 해서 파일의 모든 내용을 출력하는 예제입니다.

#include <stdio.h>         // puts()
#include <string.h>        // strlen()
#include <fcntl.h>         // O_WRONLY
#include <unistd.h>        // write(), close()

#define  BUFF_SIZE   1024

int main()
{
   char   buff[BUFF_SIZE];
   int    fd;

   if ( 0 < ( fd = open( "./test.txt", O_RDONLY)))
   {
      read( fd, buff, BUFF_SIZE);
      puts( buff);
      close( fd);
   }
   else
   {
      printf( "파일 열기에 실패했습니다.\n");
   }
   return 0;
}
]$ ./a.out
12345
ABCDE
abcde
67890
]$

만일 파일 내용보다 버퍼 크기가 작다면 여러번 읽기를 시도할 수 있습니다.

#include <stdio.h>         // puts()
#include <string.h>        // strlen()
#include <fcntl.h>         // O_WRONLY
#include <unistd.h>        // write(), close()

#define  BUFF_SIZE   5     // 버퍼의 크기가 작습니다.

int main()
{
   char     buff[BUFF_SIZE];
   int      fd;
   ssize_t  rd_size;

   if ( 0 < ( fd = open( "./test.txt", O_RDONLY)))
   {
      while( 0 < ( rd_size = read( fd, buff, BUFF_SIZE-1)))   //  4 byte씩 읽씁니다.
      {
         buff[rd_size]  = '\0';      // puts()를 위해 NULL을 대입
         puts( buff);
      }
      close( fd);
   }
   else
   {
      printf( "파일 열기에 실패했습니다.\n");
   }
   return 0;
}
]$ ./a.out
1234
5
AB
CDE

abcd
e
67
890
]$
  

'Language > C' 카테고리의 다른 글

inconv source (iconv_open, iconv, iconv_close)  (0) 2012.05.16
inconv.h  (0) 2012.05.16
execl 다른 프로그램 실행  (0) 2012.04.26
Why use select() instead of sleep()?  (0) 2012.04.26
아스키 코드 0x00 ~ 0x20 까지  (0) 2012.04.10
:

execl 다른 프로그램 실행

Language/C 2012. 4. 26. 17:46

설명

다른 프로그램을 실행하고 자신은 종료합니다. exec 함수에는 아래와 같이 여러 가지가 있습니다.

함수 이름
int execl( const char *path, const char *arg, ...);
int execlp( const char *file, const char *arg, ...);
int execle( const char *path, const char *arg ,..., char * const envp[]);
int execv( const char *path, char *const argv[]);
int execvp( const char *file, char *const argv[]);
int execve (const char *filename, char *const argv [], char *const envp[]);

각 함수는 아래와 같은 차이가 있습니다.

함수 이름프로그램 지정명령라인 인수함수 설명
execl디렉토리와 파일 이름이 합친 전체 이름인수 리스트환경 설정 불가
execlp파일 이름인수 리스트환경 설정 불가
execle디렉토리와 파일 이름이 합친 전체 이름인수 리스트환경 설정 가능
execv디렉토리와 파일 이름이 합친 전체 이름인수 배열환경 설정 불가
execvp파일 이름인수 배열환경 설정 불가
excve전제 경로 명인수 배열환경 설정 가능

프로그램 지정에서 디렉토리와 파일 이름이 합친 전체 이름으로 지정하는 것과 파일 이름만 지정하는 것의 차이는, execl()처럼 전체 이름을 요구하는 함수는 지정한 디렉토리의 프로그램을 실행하지만 execlp()함수처럼 프로그램 이름만 요구하는 함수는 현재 PATH 환경에 등록된 디렉토리를 있는 프로그램을 실행합니다.

즉,

execl()은 아래와 같이 프로그램이 들어 있는 디렉토리명까지 입력해 주어야 합니다.

execl( "/bin/ls", "/bin/ls", NULL);

아래와 같이 프로그래명만 입력하면 실행이 안 됩니다.

execl( "ls", "ls", NULL);

그러나 execlp()는 PATH에 등록된 모든 디렉토리에 있는 프로그램을 실행하므로 프로그램 이름만 입력해도 실행이 됩니다.

execlp( "ls", "ls", NULL);
인수 중복 이유그런데 설명을 보면 프로그램 이름을 중복해서 입력했는데, 이는 프로그램을 실행하면 첫번째 인수가 실행한 프로그램의 전체 이름이기 때문입니다.

다시 말씀 드려 man()함수의 인수준 *argv[]의 첫번째 문자열 argv[0]은 실행한 프로그램의 이름입니다.

int main( int argc, char *argv[])
{
   printf( "%s\n", argv[0]);
   return 0;
}

예로

   ]$ ./a.out

으로 실행했다면 argv[0]은 "./a.out"이 되며,

   ]$ /home/jwjw/prjs/test_fun/a.out

으로 실행했다면 argv[0]은 "/home/jwjw/prjs/test_fun/a.out"이 됩니다. 또한 어떤 프로그램은 이 첫번재 인수를 이용할 수 있으므로 파일의 전체 이름을 입력해 주는 것이 좋습니다.

헤더unistd.h
형태int execl( const char *path, const char *arg, ...);
인수char *path실행 파일의 디레토리 포함 전체 파일 명
 const char *arg인수 목록
반환int실패일 때만 -1
예제
#include <stdio.h>
#include <unistd.h>

int main()
{
   execl( "/bin/ls", "/bin/ls", "-al", "/tmp", NULL);

   printf( "이 메시지가 보이면 지정된 프로그램이 \
없거나 어떤 문제로 실행되지 못한 것입니다.\n");
}
]$ ./a.out
합계 32
drwxrwxrwt  6 root root 4096  9월 30 05:47 .
drwxr-xr-x 24 root root 4096  9월 29 21:20 ..
srwxrwxr-x  1 jwjw jwjw    0  9월 13 14:39 process_b
srwxrwxr-x  1 jwjw jwjw    0  9월 14 08:34 uds_a
srwxrwxr-x  1 jwjw jwjw    0  9월 14 08:36 uds_b
]$

'Language > C' 카테고리의 다른 글

inconv.h  (0) 2012.05.16
read() 파일 읽기  (0) 2012.05.11
Why use select() instead of sleep()?  (0) 2012.04.26
아스키 코드 0x00 ~ 0x20 까지  (0) 2012.04.10
How to print out thread id from pthread_t  (0) 2012.04.06
:

Why use select() instead of sleep()?

Language/C 2012. 4. 26. 10:29

출처 :  http://stackoverflow.com/questions/3125645/why-use-select-instead-of-sleep

 

답변 :

Select allow for accurate sub second wait, and is more portable than sleep. There are other ways to wait, see this question.

 

답변 :

Well, sleep(3) may be implemented by using signals. It depends on the platform.

When you use select(2) and poll(2), you know that no signals will be involved, which is often very useful. For example, if you are using alarm(2), you should not use sleep(3) as well, because "mixing calls to alarm and sleep is a bad idea" (according to the man page.)

Also, select and poll give you millisecond granularity when sleeping, but sleep only has a granularity in terms of seconds.


'Language > C' 카테고리의 다른 글

read() 파일 읽기  (0) 2012.05.11
execl 다른 프로그램 실행  (0) 2012.04.26
아스키 코드 0x00 ~ 0x20 까지  (0) 2012.04.10
How to print out thread id from pthread_t  (0) 2012.04.06
Programmiersprache C/C++  (0) 2012.03.16
: