الأكثر فائدة من C-MACROS من صنع المستخدم (في مجلس التعاون الخليجي ، أيضا C99)؟ [مغلق

StackOverflow https://stackoverflow.com/questions/1772119

  •  21-09-2019
  •  | 
  •  

سؤال

ماذا او ما ج الماكرو في رأيك هو الأكثر فائدة؟ لقد وجدت ما يلي ، الذي أستخدمه لإجراء حساب ناقل في ج:

#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);
...
هل كانت مفيدة؟

المحلول

حلقة ECH في 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);
}

نصائح أخرى

#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 Min ، Max ، ABS وما إلى ذلك.

لاحظ ، راجع للشغل ، أنه لا يمكن تنفيذ أي مما سبق عن طريق وظيفة في 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++)) (حسنًا ، يستخدمون ملحقات دول مجلس التعاون الخليجي ولكن المفهوم هو نفسه).

أحب استخدام وحدات الماكرو حيث يساعد على جعل الأمور أكثر وضوحًا لكنها أداة حادة! ربما هذا هو ما أعطاهم هذه السمعة السيئة ، وأعتقد أنها أداة مفيدة للغاية وكان C سيكون أكثر فقراً إذا لم تكن موجودة.

أرى أن الآخرين قد قدموا أمثلة على النقطة 2 (وحدات الماكرو كوظائف) ، اسمحوا لي أن أعطي مثالًا على إنشاء بناء C جديد: آلة الحالة المحدودة. (لقد نشرت هذا بالفعل على ذلك ، لكن لا يمكنني أن أكون قادرًا على العثور عليه)

 #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 التي تحتاجها.

قد لا يعجب شخص ما هذا المثال ، لكنني أجد أنه من المفيد توضيح مدى بساطة وحدات الماكرو التي تجعل الكود أكثر وضوحًا والتعبير.

إذا كنت بحاجة إلى تحديد البيانات عدة مرات في سياقات مختلفة ، يمكن أن تساعدك وحدات الماكرو في تجنب الاضطرار إلى استعادة نفس الشيء عدة مرات.

على سبيل المثال ، دعنا نقول أنك تريد تحديد تعداد الألوان ودالة التعداد إلى السلسلة ، بدلاً من سرد جميع الألوان مرتين ، يمكنك إنشاء ملف للألوان (الألوان):

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 متعمد. يطبع سلسلة منسقة مع موقع المصدر مسبقا. أنا أعمل في أنظمة مضمنة في الوقت الفعلي في كثير من الأحيان ، وفي كثير من الأحيان أقوم أيضًا بتضمين طابع زمني في الإخراج أيضًا.

foreach حلقة لجامعة كاليفورنيا ، وتحديدا C99 مع امتدادات GNU. يعمل مع السلاسل والصفائف. يمكن استخدام المصفوفات المخصصة ديناميكيًا عن طريق إلقاءها على مؤشر إلى صفيف ، ثم إزالةها.

#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.

تعبيرات Lambda (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);
}
#define COLUMNS(S,E) [ (E) - (S) + 1 ]


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

وفر لنفسك بعض الخطأ المعرض

ذكر شخص آخر Container_of (), ، ولكن لم تقدم تفسيرا لهذا الماكرو مفيد حقا. لنفترض أن لديك هيكل يشبه هذا:

struct thing {
    int a;
    int b;
};

الآن إذا كان لدينا مؤشر ل ب, ، يمكننا ان نستخدم Container_of () للحصول على مؤشر ل شيء بطريقة آمنة من النوع:

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

هذا مفيد في إنشاء هياكل بيانات مجردة. على سبيل المثال ، بدلاً من اتباع قائمة انتظار النهج. تتطلب إنشاء أشياء مثل الشتلط (أطنان من وحدات الماكرو المجنونة لكل عملية) ، يمكنك الآن كتابة تنفيذ الشريحة يبدو مثل هذا:

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);

هذا واحد من Linux kernel (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))

ابحث عن أقرب عدد صحيح غير موقّع 32 بت أكبر من x. أستخدم هذا لمضاعفة حجم المصفوفات (أي علامة المياه العالية).

بايت بايت ، الكلمات ، dwords في الكلمات ، dwords و qwords:

#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) 

الحجج الأقواس هي دائمًا ممارسة جيدة لتجنب الآثار الجانبية على التوسع.

أيضا الحد الأدنى من النوع متعدد الحدود والأكثر من هذا القبيل

//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))

واحد (من بين القلائل القليلة) التي أستخدمها بانتظام هو ماكرو لإعلان حجة أو متغير غير مستخدم. الحل الأكثر توافقًا لملاحظة هذا (IMHO) يختلف حسب برنامج التحويل البرمجي.

هذا رائع:

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

وأنا أستخدمه مثل:

object = NEW(object_type, 1);

يبدو صحيحا وكاذبة شعبية.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top