[C] C11

23537 단어 CC

C11에 새롭게 추가된 문법들을 소개한다. 아쉽지만 Windows에서는 C11을 제대로 지원하는 무료 컴파일러는 없다. ICC가 지원한다고는 하나 확인해 보진 못했고 유료이다.
매우 아쉽게도 c11을 제대로 사용하려면 Linux로 가야한다.

_Noreturn, noreturn

_Noreturn 또는 noreturn 키워드는 c11표준으로 등장했다.
이 키워드는 해당 함수가 컴파일러에게 반환값이 없음을 명시하여 최적화에 도움을 준다.
예를들어 아래 fatal함수같은 경우는 절대로 반환값이 나올 수 없다.

#include <stdlib.h>
#include <stdio.h>
#if defined(__STDC_VERSION__) && __STDC_VERSION__>=201112
#   include <stdnoreturn.h>
#   if defined(_WIN32) || defined(_WIN64)
#       define NORETURN __declspec(noreturn)
#   else
#       define NORETURN noreturn
#   endif
#else
#   define NORETURN
#endif

NORETURN void fatal(void)
{
    fprintf(stderr,"error message\n");
    exit(1);
}

int main(void)
{
    int i = 1;
    if(i%2)
        fatal();
    puts("This code is never executed.");
}

c11이 지원되는 컴파일러의 경우 stdnoreturn.h을 사용하고 Visual Studio의 경우는 _-declspec(noreturn) 을 사용하면 된다.

_Alignof, _Alignas, alignas, alignof

_Alignas 또는 alignas 키워드는 해당 변수(의 시작주소)가 지정한 상수배수의 위치에 정렬되도록 한다. 이는 구조체의 패딩과도 관련이 깊다. 구조체의 패딩을 결졍하는것이 아니라 해당 변수에게만 지정된 상수의 배수 주소에 선언하게 하는 키워드이다.

이 키위드를 이해하기 전에 구조체 패킹 키워드인 __attribute__((__packed__))#pragma pack(N) 부터 제대로 알고 가야 한다.

__attribute__((__packed__))__attribute__((packed, aligned(N))) 은 struct를 선언할 시 struct 키위드 뒤에다 사용하면 된다.

아래의 2가지 방법 모두 사용 가능하다.

struct Z{
    char a;
    int b;
    float c;
}__attribute__((packed, aligned(1)));
struct __attribute__((packed, aligned(1))) Z{
    char a;
    int b;
    float c;
};

또한 __attribute__((__packed__))__attribute__((packed, aligned(1)))은 동일하다.

이 키워드는 구조체의 정렬 옵션을 제한한다. 구조체 내부의 모든 변수를 지정된 정렬배수에 맞추어 메모리 구조를 할당한다.

#pragma pack(N) 역시 마찬가지이다. 이 또한 구조체 내부의 모든 변수를 지정된 정렬배수에 맞추어 메모리 구조를 할당한다.

이제 _Alignas 키워드를 살펴 보자. 이 키워드는 해당 변수를 지정된 정렬 배수에 맞추어 메모리 구조를 할당한다.
당연히 나머지 변수의 키워드는 __attribute__((packed, aligned(N)))에서 지정한 대로 동작한다.

하지만 #pragma pack()을 사용했을 경우에는 _Alignof는 무시된다.

그러니까 우선순위를 정리하자면

#pragma pack()__attribute__((packed, aligned(N)))를 같이 사용하면 pragma pack 무시됨.

#pragma pack()_Alignas 를 같이 사용하면 _Alignas 무시됨.

_Alignas__attribute__((packed, aligned(N)))는 같이 사용 가능. 여기서 _Alignas로 지정하지 않은 변수는 __attribute__((packed, aligned(N))) 에서 지정한 정렬 배수를 따라가지만 _Alignof 키워드에서는 가장 큰 _Alignas 정렬 배수를 출력한다.

소름돋는 결과인 3가지 키워드를 모두 사용하면 #pragma pack에 의해 _Alignas는 무시되고
__attribute__((packed, aligned(N)))에 의해 pragma pack은 무시되어서
__attribute__((packed, aligned(N)))만 남게 된다.

이미 선언된 구조체의 정렬 배수를 알고 싶다면 _Alignof를 사용한다.

#include<stdio.h>
#include<stdalign.h>    //alignas, alignof
#include<stddef.h>      //offsetof

struct Z{
    alignas(4) char a[10];
    alignas(8) int b;
    char c;
    float d;
}__attribute__((packed, aligned(1)));

int main(){
    printf("sizeof = %ld\n",sizeof(struct Z));
    printf("alignof = %ld\n",alignof(struct Z));

    printf("offsetof a = %ld\n",offsetof(struct Z,a));
    printf("offsetof b = %ld\n",offsetof(struct Z,b));
    printf("offsetof c = %ld\n",offsetof(struct Z,c));
    printf("offsetof d = %ld\n",offsetof(struct Z,d));
    return 0;
}

_Exit

이 함수는 자원을 정리하지 않고 그냥 프로세스를 종료한다. 즉, int atexit( void (*func)(void) ); 함수나 int at_quick_exit( void (*func)(void) );를 호출하지 않는다.

이게 왜 C11이냐? 함수 프로토타입이 바뀌었다. 리턴값이 없으므로 함수 원형이 아래와 같이 바뀌었다.

_Noreturn void _Exit( int exit_code );

_Pragma

_Pragma(string-literal) 은 이미 알고 있는 #pragma와 동일하다.
왜 있는지 모르겠다.

#include<stdio.h>
#include<stdalign.h>    //alignas, alignof
#include<stddef.h>      //offsetof

_Pragma("pack(1)")  //same as #pragma pack(1)
struct Z{
    char a[10];
    int b;
    char c;
    float d;
};

int main(){
    printf("sizeof = %ld\n",sizeof(struct Z));
    printf("alignof = %ld\n",alignof(struct Z));

    printf("offsetof a = %ld\n",offsetof(struct Z,a));
    printf("offsetof b = %ld\n",offsetof(struct Z,b));
    printf("offsetof c = %ld\n",offsetof(struct Z,c));
    printf("offsetof d = %ld\n",offsetof(struct Z,d));
    return 0;
}

func

함수 이름을 지정하는 문자열 값의 매크로이다.... 기존에 있던 __FUNCTION__ 과의 차이는 모르겠다.

#include<stdio.h>
void foo(){
    puts(__func__);
}
void bar(){
    puts(__FUNCTION__);
}
int main(){
    printf("%s\n",__func__);
    foo();
    bar();
    return 0;
}

_Generic

매크로 관련 키워드이다. 아래와 같이 사용하며 이를 이용한 제네릭 프로그래밍이 가능하다.

#include<stdio.h>
void foof(float v){
    printf("float value v is : %f\n",v);
}
void fooi(int v){
    printf("int value v is : %d\n",v);
}
void food(double v){
    printf("double value v is : %f\n",v);
}
#define foo(V)  _Generic((V),float: foof,int: fooi,double: food,default: fooi)(V)

int main(){
    foo(5);
    foo(4.4F);
    foo(4.7);
    return 0;
}

좋은 웹페이지 즐겨찾기