1

如何编写独立于类型说明符的标准 C(C89 或 C99 兼容)宏?

具体来说,我想编写一个宏来比较两个可以是有符号类型或无符号类型的数字,8 位、16 位、32 位、64 位。

我正在寻找的是这样的:

           int c,  a , b;
           uint16_t p, q, r;

                .
                .
                .

           c = min(a, b);

                .
                .
                .

           r = min(p, q);
                .
                .
                .

即,相同的宏“min”可用于比较两个整数——任何宽度,有符号或无符号。

我知道一个可以工作的基本宏可以这样写:

          #define min(x, y)   (x) < (y) ? (x) : (y)

但这涉及两次评估表达式 'x' 和 'y' 的副作用,这是我不想要的。

为了鲁棒性,表达式必须只计算一次。

到目前为止,我的方法似乎是这样的:

          #define min(type, x, y)  ( {  \ 
                  type __tmp1 = (x);                      \
                  type __tmp2 = (y);                      \
                  __tmp1 < __tmp2 ? __tmp1 : __tmp2; } )

          int a, b, c;
          uint16_t p, q, r;

                   .
                   .
                   .

          c = min(int, a, b);

C 编译器抛出错误:“expecting ; found (”,以及许多其他错误。

我怎样才能重新编写这个宏,以便使用这种形式:

          <result> = min( <type>, <parm1>, <parm2> );

似乎可以编写“min”宏,使其用法为:

           min( <result>, <type>, <parm1>, <parm2> );  

但最好有第一种形式。

2012 年 9 月 4 日更新#1:

谢谢你们,您的答案/评论中提供的所有信息使我对可能性有了更广泛的了解。好吧,我没有使用 gcc,我的编译器也不支持 C11 规范,所以似乎唯一可用的选项是:

  1. 使用这种形式的宏:

           // min( <result>, <type>, <parm1>, <parm2> ); 
    
           #define min(result, type, x, y)  {                   \ 
                  type __tmp1 = (x);                      \
                  type __tmp2 = (y);                      \
                  result = __tmp1 < __tmp2 ? __tmp1 : __tmp2; } 
    
            int p, q, r;
                .
                .
                .
    
            min(r, int, p, q);
    
  2. 另一种方法是:

          inline char min_char(char x, char y)       { return x < y ? x : y; }
          inline short min_short(short x, short y)    { return x < y ? x : y; }
          inline int min_int(int x, int y)          { return x < y ? x : y; }
          inline long min_long(long x, long y)      { return x < y ? x : y; }
    
          inline unsigned char min_uchar(unsigned char x, unsigned char y)    { return x < y ? x : y; }  
          inline unsigned short min_ushort(unsigned short x, unsigned short y) { return x < y ? x : y; }   
          inline unsigned int min_uint(unsigned int x, unsigned int y)       { return x < y ? x : y; }  
          inline unsigned long min_ulong(unsigned long x, unsigned long y)   { return x < y ? x : y; }  
    
    
          #define   min(type, x, y)    min_##type(x, y)
    
    
          int p, q, r;
               .
               .
               .
    
          r = min(int, p, q);
    

第二种方法是否有任何副作用,在某些情况下可能会产生意想不到的结果,特别是关于“短”和“字符”类型 - 有符号或无符号。

欢迎任何改进或建议。

编辑:

用户将如何指定以下功能:

            'min_uchar'

他/她必须写:

             r = min(uchar, p, q);

有没有其他方法可以编写函数'uchar',以便用户可以简单地编写:

             r = min(unsigned char, p, q);

在这种情况下,我不能将函数命名为“min_unsigned char”,因为它在 C 中是不允许的。是否有任何其他方式或用户必须在宏“min”中将类型指定为“uchar”而不是“unsigned char” '。

2012-09-10 更新:

谢谢大家的帮助。由于我需要接受答案,因此我接受Eitan T的回答,但我感谢大家的回复。谢谢。

4

4 回答 4

3

当然,您的宏可以独立于类型说明符,这是使宏如此强大的原因之一。您可以:

  1. 传递变量名和类型,让​​宏处理类型。
  2. 传递不带类型的变量名,并假定宏对该变量执行合法操作,而不管类型如何。

您的实际问题是,在第一种形式中,您希望宏返回一个表达式,但在其中您声明了变量等等。我相信正因为如此,您将花括号放入宏中,现在它扩展为: ({ bla bla }),这将在 ISO C99 中为您提供以下错误消息:

ISO C forbids braced-groups within expressions

不幸的是,在 ISO C99 中,您不能同时拥有一个表达式并在其中声明临时变量。这就是功能的用途。

因此,要么使用您描述的第二种形式,即将“结果”变量传递给宏:

#define min(type_, x_, y_, result_)                    \
    do {                                               \
        type_ __tmp1 = (x_);                           \
        type_ __tmp2 = (y_);                           \
        result_ = (__tmp1 < __tmp2 ? __tmp1 : __tmp2); \
    } while(0)

或改用内联函数。

PS:
此外,在编写 C 宏时, thisthis值得一读,以获得良好的实践。

于 2012-09-04T09:25:41.587 回答
2

如果您使用gcc 语句表达式,那么您还可以使用 gcc扩展关键字typeof(或__typeof__):

      #define min(x,y)  ( {                                \ 
              typeof(x) __tmp1 = (x);                      \
              typeof(y) __tmp2 = (y);                      \
              __tmp1 < __tmp2 ? __tmp1 : __tmp2; } )

_GenericC11 使用关键字引入类型泛型表达式:

int min_int(int x, int y) { return x < y ? x : y; }
long min_long(long x, long y) { return x < y ? x : y; }
#define min(x,y) _Generic((x + y),                        \
                          int: min_int,                   \
                          long: min_long)(x, y)

鉴于您愿意在宏调用中编写类型,您可以使用:

int min_int(int x, int y) { return x < y ? x : y; }
long min_long(long x, long y) { return x < y ? x : y; }
#define min(type,x,y) min_ ## type(x, y)

这是在旧版 C 中唯一有效的语法。

于 2012-09-04T08:59:11.807 回答
1

宏函数不能执行类型检查。如果您想对指定类型的操作数进行操作(如果参数类型不兼容,则强制编译器警告),您必须使用函数(也许inline如果您考虑性能)。

您的方法令人愉悦,但带有表达式的大括号组是 gcc 扩展(非标准),因此标准编译器无法将您的宏函数评估为具有特定值的表达式。

此外,您的\字符后面有一个空格,这会导致语法错误。

/* GCC-compatible */
#define min(t, x, y) ({ \
    t __tmp1 = (x); \
    t __tmp2 = (y); \
    __tmp1 < __tmp2 ? __tmp1 : __tmp2; \
})
于 2012-09-04T08:41:53.193 回答
0

由于所有解决方案似乎都需要指定类型,因此您不妨调用特定的宏来进行比较。您还可以推广到仅两个宏,并为每个有符号/无符号使用最大类型:

#define smin(x_, y_, result_)                     \
  do {                                            \
    int64_t __tmp1 = (x_);                        \
    int64_t __tmp2 = (y_);                        \
    result_ = __tmp1 < __tmp2 ? __tmp1 : __tmp2;  \
  } while(0)
#define umin(x_, y_, result_)                     \
  do {                                            \
    uint64_t __tmp1 = (x_);                       \
    uint64_t __tmp2 = (y_);                       \
    result_ = __tmp1 < __tmp2 ? __tmp1 : __tmp2;  \
  } while(0)

确保只比较相同标牌的类型。

于 2012-09-04T17:34:39.163 回答