[C] C99

1.가변 길이 배열(VLA : variable-length array)

c90 에서만 해도 배열의 선언시 크기는 반드시 상수여야 했다.

그러나 c99 에서 부터는 배열의 크기를 변수로 지정할 수 있게 되었다.

그러나 이는 사실 원래 가능한 기능이였다. 그런 기능중 하나는 alloca 함수이다.

linux

#include<alloca.h>
void* alloca(size_t size);  

alloca 는 malloc과 인터페이스가 같으나, 힙 영역에 할당을 하지 않고 스택영역에 할당 한다.

당연히 할당 속도도 malloc에 비해 빠르다. 다만 실패시 NULL을 반환하는 malloc과 달리 alloca는 세그먼테이션 오류를 뿜으며 강렬히 전사한다.

VS

#include<malloc.h>
void* _alloca(size_t size);  

Visual 에서는 malloc.h에 정의 되어 있으며, 앞에 _ 가 붙는다. 할당에 실패할경우 Stack over flow를 뿜으며 죽는다.

Windows의 __try 를 사용해 스택오버플로우를 Detect 할 수 있다.

#include<stdio.h>
#include<malloc.h>
#include<Windows.h>
int main() {  
    __try{
        void* p = _alloca(10000000);
        printf("success : %p\n", p);
    }
    __except (GetExceptionCode() == STATUS_STACK_OVERFLOW) {
        puts("fail");
    }
    return 0;
}

이런 alloca 함수도 있으므로 VLA를 선언할 수 있다. 하지만 사용예시가 극히 드물며, 안전성면이나 호환성면에서도 malloc을 사용하는것이 낮다. (주로 제네릭 프로그래밍을 할때 가끔 사용된다. Swap함수등을 구현할때라던지,)

#include<stdio.h>
int main(){  
    int n=5;
    int arr[n]={0};
    return 0;
}
이렇게 변수로 배열의 크기를 지정하는것이 가능하다.

2. inline function

inline 키워드는 더이상 C++에서만 보는 키워드가 아니다.

인라인 함수를 모르는 자가 있는가? 사실 이런 인라인 함수가 있다해도 define 키워드를 많이 사용한다.

더 솔직히 말하면 컴파일러가 알아서 최적화를 해주므로 인라인화에 대해 정확히 모르면 따로 쓸 필요는 없다.

inline void swap(void* a,void* b,size_t size) {  
    void* tmp = alloca(size);
    memcpy(tmp, a, size);
    memcpy(a, b, size);
    memcpy(b, tmp, size);
}

위의 alloca와 함께 사용한 인라인 swap 함수이다.

VC에는 __inline 키워드를 사용한다.(언더바가 왜 붙었을까? 하나짜리는 뭐고 두개 붙은건 무엇일까?)

내가 볼땐 언더바가 많이 붙을수록 잘 안쓴다거나....아니면 쓰지말라는 의미인것 같다.


3. 변수 선언 위치

뭐 적을 것도 없다. 더이상 C언어에서도 변수를 아무데서나 선언할 수 있다.

그러나 실제로 C를 프로그래밍 할때 예전 습관떄문인지, 아니면 가독성 때문인지 앞쪽에 선언을 주로 하게 된다.


4. bool, complex

#include<stdbool.h>

C언어에서도 bool 이라는 자료형이 있다. 하지만 사용하는것은 약간 비 추천한다.

이유는 기존의 C언어 함수들은 bool(True,False) 가 아니라 int(1,0,-1)로 주로 호환되기 때문이다. C프로그래머 역시 그렇고....

vs

#include<complex.h>
_DComplex a;    //언더바가 또 붙었네?  

linux

#include<complex.h>
double complex a;  

http://en.wikichip.org/wiki/c/complex.h

거지같다 정말 ㅡㅡ 실제로 거지 같다.

잘 쓰지도 않는내용이고 만일 쓴다해도, 만들어서 사용하길 바란다. (개인적으로 복소수 필요하면 C++로 짜는것을 권함)


5. tgmath.h

별로 볼것이 없다. 실수와 허수 타입을 모두 지원하겠다는 뜻이다.

제네릭 타입의 매크로를 이용해 double과 complex를 모두 지원하는 수학함수를 만들었다는 뜻인데....말했다 시피 복소수를 쓸일이없기에 이것도 그닥.....

http://pubs.opengroup.org/onlinepubs/009695399/basedefs/tgmath.h.html

에서 자세한 설명을 볼 수 있다. 더군다나 VS에서는 지원도 없다.

VS 는 더이상 C 컴파일러가 아니다. C++ 컴파일러다.

더이상 C++은 C를 하위호환 시키지 않는다.


6. 가변 인수 매크로

사실 이것도 별로 볼것이 없다.

먼저 가변인자에 대해 잠깐 짚고 가자.

#include<stdarg.h>
int max(int count, ...) {  
    int val = 0,m=INT_MIN;
    va_list ptr;
    va_start(ptr, count);
    for (int i = 0; i < count; i++){
        val = va_arg(ptr, int);
        if (val>m)m = val;
    }
    va_end(ptr);
    return m;
}

위는 최대값을 찾아주는 max 함수이다.

max(5,2,5,1,3,4); 와 같이 사용하며 앞에 반드시 1개 이상의 고정타입인수가 들어가야 한다.

va_list는 단순한 char* 이고 va_start 로 가변인수 파라매터의 시작 점을 가르키게 하고, va_arg 로 파라매터를 점프한다.

va_end는 해당 va_listNULL로 초기화 하여 사용을 끝낸다.

간단하다.

이런 가변인자 함수를 매크로와 쉽게 연결 시키기 위해서 나온게 가변인자 매크로 이다.

#define PRINTF(F,...)    printf(F,__VA_ARGS__)

가변인자함수에 인수로 넣을수 있게한다. __VA_ARGS__가 키워드이다.

뭐야 고작 이거?

아니다. 이런것도 가능하다. 위에서 만든 max 함수의 경우 맨 앞에 반드시 개수를 지정해야 하는 불편함이 있었다. (이런 불편함이 당신의 실력을 증대 시킬것이다.)

앞의 숫자를 컴퓨터가 계산하게 할 수 없을까? 가 한단계 나아가게 하는 질문이다.

해답은 가변인수들을 문자열로 취급한뒤, 콤마의 개수+1 을 하는것이다.

해답은 아래와 같다.

#include<stdio.h>
#include<stdarg.h>
#include<limits.h>
int max(int count, ...) {  
    int val = 0,m=INT_MIN;
    va_list ptr;
    va_start(ptr, count);
    for (int i = 0; i < count; i++){
        val = va_arg(ptr, int);
        if (val>m)m = val;
    }
    va_end(ptr);
    return m;
}
int GetComma(char* s) {  
    int r = 0;
    int b = 0;
    while (*s){
        if (*s == '(')b++;
        if (*s == ')')b--;
        if (!b && *s == ',')r++;
        s++;
    }
    return r;
}
#define TO_STR(S)    #S
#define MAX(F,...)    max(GetComma(TO_STR(__VA_ARGS__))+1,__VA_ARGS__)
int main() {  
    printf("%d\n", MAX(2,5,1));
    printf("%d\n", MAX(3,4));
    printf("%d\n", MAX(2,5,MAX(3,6),1));
    return 0;
}

매크로에서 앞에 # 을 붙이면 그 매크로는 문자열이 된다.

이제 맨앞 고정인수를 넣지 않아도 된다.


7. restrict 한정자

restrict 한정자는 해당 메모리영역을 가르키는 포인터가 단 하나임을 보장하는 키워드로 해당 값을 재 load 하지 않아 컴파일러 최적화에 도움을 주는 키워드이다.

근데...정확하게 이해하지 못하면 아예 사용하지 않는것이 좋다.

쓴다고 엄청 빨라지는것도 아니고 말이다...


변경이력

  • 2016년 6월 20일 글 등록

Rerefence