programing

가장 유용한 사용자 제작 C마크로스(GCC에서도 C99)?

css3 2023. 10. 27. 22:05

가장 유용한 사용자 제작 C마크로스(GCC에서도 C99)?

당신이 생각하는 C macro는 어떤 것이 가장 유용합니까?C에서 벡터 연산을 할 때 사용하는 다음과 같은 것을 발견했습니다.

#define v3_op_v3(x, op, y, z) {z[0]=x[0] op y[0]; \
                               z[1]=x[1] op y[1]; \
                               z[2]=x[2] op y[2];}

다음과 같이 작동합니다.

v3_op_v3(vectorA, +, vectorB, vectorC);
v3_op_v3(vectorE, *, vectorF, vectorJ);
...
#define IMPLIES(x, y) (!(x) || (y))

#define COMPARE(x, y) (((x) > (y)) - ((x) < (y)))
#define SIGN(x) COMPARE(x, 0)

#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*a))

#define SWAP(x, y, T) do { T tmp = (x); (x) = (y); (y) = tmp; } while(0)
#define SORT2(a, b, T) do { if ((a) > (b)) SWAP((a), (b), T); } while (0)

#define SET(d, n, v) do{ size_t i_, n_; for (n_ = (n), i_ = 0; n_ > 0; --n_, ++i_) (d)[i_] = (v); } while(0)
#define ZERO(d, n) SET(d, n, 0)

그리고 물론 MIN, MAX, ABS 등 다양한 것들이 있습니다.

참고로, BTW, 위의 어떤 것도 C의 함수에 의해 구현될 수 없습니다.

위의 을 입니다. 저는 아마 위의 내용을 골라낼 겁니다.IMPLIES매크로는 가장 유용한 것 중 하나입니다.그것의 주된 목적은 에서 처럼 더 우아하고 읽을 수 있는 주장의 작성을 용이하게 하는 것입니다.

void foo(int array[], int n) {
  assert(IMPLIES(n > 0, array != NULL));
  ...

C 매크로의 핵심은 적절히 사용하는 것입니다.제 생각에는 세 가지 범주가 있습니다. (상수에 설명적인 이름을 부여하기 위해 사용하는 것은 고려하지 않습니다.)

  1. 반복하고 싶지 않은 코드의 축약어로
  2. 일반 사용 기능 제공
  3. C 언어의 구조를 수정합니다(분명히).

첫 번째 경우에는 매크로가 프로그램 내에서만(일반적으로 파일만) 유지되므로 매개 변수와 용도의 이중 평가에 대해 보호되지 않는 매크로를 게시한 것과 같은 매크로를 사용할 수 있습니다.{...};(잠재적으로 위험합니다!).

두 번째 경우(그리고 세 번째 경우에는 더 많이) 매크로가 실제 C 구성 요소인 것처럼 올바르게 동작하도록 극도로 주의해야 합니다.

당신이 GCC에서 올린 매크로(min과 max)는 이것의 예시이며, 그들은 글로벌 변수를 사용합니다._a그리고._b이중 평가의 위험을 피하기 위해 (에서와 같이)max(x++,y++)) (음, GCC 확장을 사용하지만 개념은 동일합니다.)

나는 매크로를 사용하는 것을 좋아하는데 매크로는 상황을 더 명확하게 만드는 데 도움이 되지만 그것들은 날카로운 도구입니다!아마도 그래서 그들의 평판이 좋지 않았던 것 같아요, 저는 그들이 매우 유용한 도구이고 그들이 없었다면 C는 훨씬 더 가난했을 것이라고 생각합니다.

다른 사람들이 점 2(함수로서의 매크로)의 예를 제공한 것을 봅니다. 새로운 C 구성체인 유한 상태 기계를 만드는 예를 들어보겠습니다.(이미 SO에 올린 글인데 못찾는것 같습니다)

 #define FSM            for(;;)
 #define STATE(x)       x##_s 
 #define NEXTSTATE(x)   goto x##_s

이 방법을 사용하는 경우:

 FSM {
    STATE(s1):
      ... do stuff ...
      NEXTSTATE(s2);

    STATE(s2):
      ... do stuff ...
      if (k<0) NEXTSTATE(s2); 
      /* fallthrough as the switch() cases */

    STATE(s3):
      ... final stuff ...
      break;  /* Exit from the FSM */
 } 

당신이 필요로 하는 FSM의 맛을 얻기 위해 이 테마에 다양한 것을 추가할 수 있습니다.

누군가가 이 예를 좋아하지 않을 수도 있지만 간단한 매크로가 코드를 더 가독성 있고 표현력 있게 만드는 방법을 보여주는 것이 완벽하다고 생각합니다.

C99의 각 루프에 대해:

#define foreach(item, array) \
    for(int keep=1, \
            count=0,\
            size=sizeof (array)/sizeof *(array); \
        keep && count != size; \
        keep = !keep, count++) \
      for(item = (array)+count; keep; keep = !keep)

int main() {
  int a[] = { 1, 2, 3 };
  int sum = 0;
  foreach(int const* c, a)
    sum += *c;
  printf("sum = %d\n", sum);

  // multi-dim array
  int a1[][2] = { { 1, 2 }, { 3, 4 } };
  foreach(int (*c1)[2], a1)
    foreach(int *c2, *c1) 
      printf("c2 = %d\n", *c2);
}

다른 컨텍스트에서 데이터를 여러 번 정의해야 하는 경우 매크로를 사용하면 동일한 항목을 여러 번 다시 나열하지 않아도 됩니다.

예를 들어, 색상의 열거와 열거-대- 문자열 함수를 정의하고 모든 색상을 두 번 나열하는 대신 색상의 파일(colors.def)을 생성할 수 있습니다.

c(red)
c(blue)
c(green)
c(yellow)
c(brown)

이제 c 파일에서 열거형과 문자열 변환 함수를 정의할 수 있습니다.

enum {
#define c(color) color,
# include "colors.def"
#undef c
};

const char *
color_to_string(enum color col)
{
    static const char *colors[] = {
#define c(color) #color,
# include "colors.def"
#undef c
    };
    return (colors[col]);
};
#if defined NDEBUG
    #define TRACE( format, ... )
#else
    #define TRACE( format, ... )   printf( "%s::%s(%d)" format, __FILE__, __FUNCTION__,  __LINE__, __VA_ARGS__ )
#endif

다음 사이에 쉼표가 없음을 유의하십시오."%s::%s(%d)"그리고.format고의적입니다.원본 위치를 앞에 붙여 형식화된 문자열을 인쇄합니다.저는 실시간 임베디드 시스템에서 일하기 때문에 출력에 타임스탬프도 포함하는 경우가 많습니다.

GCC의 각 루프, 특히 C99와 GNU Extensions.문자열과 배열로 작동합니다.동적으로 할당된 배열은 배열에 대한 포인터로 캐스팅한 후 재참조하여 사용할 수 있습니다.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#define FOREACH_COMP(INDEX, ARRAY, ARRAY_TYPE, SIZE) \
  __extension__ \
  ({ \
    bool ret = 0; \
    if (__builtin_types_compatible_p (const char*, ARRAY_TYPE)) \
      ret = INDEX < strlen ((const char*)ARRAY); \
    else \
      ret = INDEX < SIZE; \
    ret; \
  })

#define FOREACH_ELEM(INDEX, ARRAY, TYPE) \
  __extension__ \
  ({ \
    TYPE *tmp_array_ = ARRAY; \
    &tmp_array_[INDEX]; \
  })

#define FOREACH(VAR, ARRAY) \
for (void *array_ = (void*)(ARRAY); array_; array_ = 0) \
for (size_t i_ = 0; i_ && array_ && FOREACH_COMP (i_, array_, \
                                    __typeof__ (ARRAY), \
                                    sizeof (ARRAY) / sizeof ((ARRAY)[0])); \
                                    i_++) \
for (bool b_ = 1; b_; (b_) ? array_ = 0 : 0, b_ = 0) \
for (VAR = FOREACH_ELEM (i_, array_, __typeof__ ((ARRAY)[0])); b_; b_ = 0)

/* example's */
int
main (int argc, char **argv)
{
  int array[10];
  /* initialize the array */
  int i = 0;
  FOREACH (int *x, array)
    {
      *x = i;
      ++i;
    }

  char *str = "hello, world!";
  FOREACH (char *c, str)
    printf ("%c\n", *c);

  /* Use a cast for dynamically allocated arrays */
  int *dynamic = malloc (sizeof (int) * 10);
  for (int i = 0; i < 10; i++)
    dynamic[i] = i;

  FOREACH (int *i, *(int(*)[10])(dynamic))
    printf ("%d\n", *i);

  return EXIT_SUCCESS;
}

이 코드는 GCC, ICC 및 Clang과 함께 GNU/Linux에서 작동하도록 테스트되었습니다.

람다 식(GCC만 해당)

#define lambda(return_type, ...) \
  __extension__ \
  ({ \
    return_type __fn__ __VA_ARGS__ \
    __fn__; \
  })

int
main (int argc, char **argv)
{
  int (*max) (int, int) = 
    lambda (int, (int x, int y) { return x > y ? x : y; });
  return max (1, 2);
}

다른 사람이 container_of()를 언급했지만 이 정말 편리한 매크로에 대한 설명을 제공하지 않았습니다.다음과 같은 구조를 가지고 있다고 가정해 보겠습니다.

struct thing {
    int a;
    int b;
};

이제 b에 대한 포인터가 있으면 container_of()를 사용하여 안전한 유형으로 포인터를 얻을 수 있습니다.

int *bp = ...;
struct thing *t = container_of(bp, struct thing, b);

이것은 추상적인 데이터 구조를 만들 때 유용합니다.예를 들어, SLIST(모든 작업에 대해 수많은 미친 매크로)와 같은 것을 생성하기 위해 approach queue.h 테이크를 수행하는 대신, 이제 다음과 같은 형태의 slist 구현을 작성할 수 있습니다.

struct slist_el {
    struct slist_el *next;
};

struct slist_head {
    struct slist_el *first;
};

void
slist_insert_head(struct slist_head *head, struct slist_el *el)
{
    el->next = head->first;
    head->first = el;
}

struct slist_el
slist_pop_head(struct slist_head *head)
{
    struct slist_el *el;

    if (head->first == NULL)
        return NULL;

    el = head->first;
    head->first = el->next;
    return (el);   
}

미친 매크로 코드가 아닙니다.오류에 대한 좋은 컴파일러 줄 번호를 제공하고 디버거와 잘 작동합니다.또한 구조체가 여러 유형을 사용하는 경우를 제외하고는 상당히 유형 안전합니다(예: 아래 예제에서 구조체 색상을 단순히 색상만 나열한 것보다 더 연결된 목록에 표시할 수 있도록 허용한 경우).

사용자는 이제 다음과 같이 라이브러리를 사용할 수 있습니다.

struct colors {
    int r;
    int g;
    int b;
    struct slist_el colors;
};

struct *color = malloc(sizeof(struct person));
color->r = 255;
color->g = 0;
color->b = 0;
slist_insert_head(color_stack, &color->colors);
...
el = slist_pop_head(color_stack);
color = el == NULL ? NULL : container_of(el, struct color, colors);
#define COLUMNS(S,E) [ (E) - (S) + 1 ]


struct 
{
    char firstName COLUMNS ( 1, 20);
    char LastName  COLUMNS (21, 40);
    char ssn       COLUMNS (41, 49);
}

오류가 발생하기 쉬운 계산을 절약합니다.

이것은 리눅스 커널(gcc 특정)에서 가져온 것입니다.

#define container_of(ptr, type, member) ({                  \
const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) ); })

다른 답변에서 누락된 또 다른 내용:

#define LSB(x) ((x) ^ ((x) - 1) & (x))   // least significant bit

이것도 마음에 듭니다.

#define COMPARE_FLOATS(a,b,epsilon) (fabs(a - b) <= epsilon * fabs(a))

매크로를 싫어하는 사람들은 어떻게 공정한 부동 소수점 비교를 할 수 있습니까?

기본적인 것들만:

#define LENGTH(array) (sizeof(array) / sizeof (array[0]))
#define QUOTE(name) #name
#define STR(name) QUOTE(name)

하지만 거기엔 아주 멋진 것이 없습니다.

#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))

x보다 큰 32비트 부호 없는 가장 가까운 정수를 구합니다. 나는 이것을 사용하여 배열 크기를 두 배로 늘립니다(즉, 하이 워터 마크).

그와 같은 multi-type Minimum and Maximum.

//NOTE: GCC extension !
#define max(a,b) ({typeof (a) _a=(a); typeof (b) _b=(b); _a > _b ? _a:_b; })
#define min(a,b) ({typeof (a) _a=(a); typeof (b) _b=(b); _a < _b ? _a:_b; })

부동 소수점 x가 숫자가 아닌지 확인하는 중:

#define ISNAN(x) ((x) != (x))

바이트, 워드, dword를 워드, dword 및 qword로 팩:

#define ULONGLONG unsigned __int64
#define MAKEWORD(h,l) ((unsigned short) ((h) << 8)) | (l)
#define MAKEDWORD(h,l) ((DWORD) ((h) << 16)) | (l)
#define MAKEQWORD(h,l) ((ULONGLONG)((h) << 32)) | (l) 

논쟁을 괄호로 묶는 것은 확장에 대한 부작용을 피하는 것이 항상 좋은 방법입니다.

이건 정말 끝내줘요.

#define NEW(type, n) ( (type *) malloc(1 + (n) * sizeof(type)) )

그리고 나는 이것을 다음과 같이 사용합니다.

object = NEW(object_type, 1);

제가 정기적으로 사용하는 것 중 하나는 인수나 변수를 사용되지 않은 것으로 선언하는 매크로입니다.이에 주목하는 가장 호환되는 솔루션(IMHO)은 컴파일러마다 다릅니다.

TRUE와 FALSE가 인기가 많은 것 같습니다.

언급URL : https://stackoverflow.com/questions/1772119/the-most-useful-user-made-c-macros-in-gcc-also-c99