有人可以解释 C 使用 #define UMAX (a, b) ((a) > (b) ? (a) : (b)) 指令的硬逻辑错误,该指令返回较低的值,在 2 个编译器中

Posted

技术标签:

【中文标题】有人可以解释 C 使用 #define UMAX (a, b) ((a) > (b) ? (a) : (b)) 指令的硬逻辑错误,该指令返回较低的值,在 2 个编译器中【英文标题】:Can someone explain hard ilogical bug on C use of #define UMAX (a, b) ((a) > (b) ? (a) : (b)) directive, that returns lower value, in 2 compilers 【发布时间】:2021-02-14 00:27:00 【问题描述】:

我正在尝试确定 MUD c 代码库中发生错误的原因。 #define UMAX(a, b) ((a) > (b) ? (a) : (b)) 的使用用于返回两个值的最大值。 有时会返回较低的值,甚至调试也找不到原因。

我隔离了相关代码以进行复制,并在两个编译器中运行它,https://www.tutorialspoint.com/compile_c_online.php (GCC) 的在线 C 编译器和使用 Visual Studio 2019。 两者都有以下代码吐出负值:

dur = UMAX(0, 4 - number_fuzzy(level / 10));
printf("Dur = %i\n", dur);

基本上,UMAX 用于防止计算中出现非负值。 我们将级别除以 10,然后使用模糊/随机函数将其随机更改为 -1/+0/+1。我们从 4 中减去这个值,如果该值低于零,我们使用 UMAX 返回 0。 但是对于 50 到 59 级,这有时会吐出负值,如下所示。但我从未见过它对 60-69 有负面影响。 我知道模糊随机是随机改变值,但 UMAX 守卫应该始终有效,而不仅仅是某些级别。

完整代码如下:

//==================================================================
//= SLAP ME ON https://www.tutorialspoint.com/compile_c_online.php =
//==================================================================

#include <stdio.h>
#include<time.h> // pd 5

#define UMAX(a, b)      ((a) > (b) ? (a) : (b))
#define OLD_RAND true
static  int     rgiState[2+55];

// Replacement
//https://p2p.wrox.com/c-programming/37911-my-coin-flip-game.html
void init_mm()

    int *piState;
    int iState;
    piState = &rgiState[2];
    piState[-2] = 55 - 55;
    piState[-1] = 55 - 24;
    piState[0] = ((int) time(NULL))&((1 << 30)- 1);
    piState[1] = 1;
    for(iState = 2; iState < 55; iState++)
    
        piState[iState] = (piState[iState - 1] + piState[iState - 2])
        & ((1 << 30)- 1);
    
    return;





long number_mm( void )

#if defined (OLD_RAND)
    int *piState;
    int iState1, iState2, iRand;
    piState             = &rgiState[2];
    iState1             = piState[-2];
    iState2             = piState[-1];
    iRand               = (piState[iState1] + piState[iState2]) & ((1 << 30) - 1);
    piState[iState1]    = iRand;
    if ( ++iState1 == 55 )
        iState1 = 0;
    if ( ++iState2 == 55 )
        iState2 = 0;
    piState[-2]         = iState1;
    piState[-1]         = iState2;
    return iRand >> 6;
#else
    //return random() >> 6;
#endif


// db.c line 6267
int number_bits( int width )

    return number_mm( ) & ( ( 1 << width ) - 1 );


// db.c line 6195
/* Stick a little fuzz on a number. */
int number_fuzzy( int number )

    switch ( number_bits( 2 ) )
    
    case 0:  number -= 1; break;
    case 3:  number += 1; break;
    
    return UMAX( 1, number );


int lixo(int a)
    return UMAX( 1, a );
    


int main()

    init_mm(); // Needed cause I guess this is for seeding for random number generation


int level = 50;
int dur = 0;
dur = UMAX(0, 4 - number_fuzzy(level / 10));
printf("Dur = %i\n", dur);
dur = UMAX(0, 4 - number_fuzzy(level / 10));
printf("Dur = %i\n", dur);
dur = UMAX(0, 4 - number_fuzzy(level / 10));
printf("Dur = %i\n", dur);
dur = UMAX(0, 4 - number_fuzzy(level / 10));
printf("Dur = %i\n", dur);
dur = UMAX(0, 4 - number_fuzzy(level / 10));
printf("Dur = %i\n", dur);
dur = UMAX(0, 4 - number_fuzzy(level / 10));
printf("Dur = %i\n", dur);
dur = UMAX(0, 4 - number_fuzzy(level / 10));
printf("Dur = %i\n", dur);

int test = 0;
test = UMAX(0, 4 - lixo(level / 10));
printf("Test = %i\n", test);

可以返回/打印以下内容:

Dur = 0
Dur = 0
Dur = 0
Dur = -1
Dur = 0
Dur = -1
Dur = 0
Test = 0

使用 Visual Studio 2019 我继续组装,如下所示:

    dur = UMAX(0, 4 - number_fuzzy(level / 10));
00971A89  mov         eax,dword ptr [level]  
00971A8C  cdq  
00971A8D  mov         ecx,0Ah  
00971A92  idiv        eax,ecx  
00971A94  push        eax  
00971A95  call        _number_fuzzy (0971181h)  
00971A9A  add         esp,4  
00971A9D  mov         edx,4  
00971AA2  sub         edx,eax  
00971AA4  jns         main+72h (0971AB2h)  
00971AA6  mov         dword ptr [ebp-100h],0  
00971AB0  jmp         main+93h (0971AD3h)  
00971AB2  mov         eax,dword ptr [level]  
00971AB5  cdq  
00971AB6  mov         ecx,0Ah  
00971ABB  idiv        eax,ecx  
00971ABD  push        eax  
00971ABE  call        _number_fuzzy (0971181h)  
00971AC3  add         esp,4  
00971AC6  mov         edx,4  
00971ACB  sub         edx,eax  
00971ACD  mov         dword ptr [ebp-100h],edx  
00971AD3  mov         eax,dword ptr [ebp-100h]  
00971AD9  mov         dword ptr [dur],eax  
    printf("Dur = %i\n", dur);
00971ADC  mov         eax,dword ptr [dur]  
00971ADF  push        eax  
00971AE0  push        offset string "Dur = %i\n" (0977B30h)  
00971AE5  call        _printf (09710D2h)  
00971AEA  add         esp,8  
    dur = UMAX(0, 4 - number_fuzzy(level / 10));

但是找不到原因。这很可能是因为我的组装技术不高。

一位朋友建议这可能与无符号整数有关。 但我编写了 Lixo 函数,它不会吐出负数。 我知道这个错误很容易通过用 IF 替换 UMAX 来纠正,但由于技术上的好奇心,我对这个错误非常感兴趣。此外,这个古老的 MUD 代码充满了 UMAX 调用,因此了解它发生的原因有些重要。

那么,比我更精通 C 语言的人能否解释为什么会发生这种行为?

【问题讨论】:

对宏做什么的典型示例。谢谢。 【参考方案1】:

您发现了类函数宏的众多问题之一。凭借其简单的文本替换功能,代码:

#define UMAX(a, b) ((a) > (b) ? (a) : (b))
dur = UMAX(0, 4 - number_fuzzy(level / 10));

最终会变成:

dur = ((0) > (4 - number_fuzzy(level / 10))) ? (0) : (4 - number_fuzzy(level / 10)));

如果number_fuzzy() 在使用相同的参数调用时可以返回一个不同的 值,那不会按您的预期工作。

如果你必须使用宏(a),你可以使用类似下面的东西,它只对每个参数求值一次:

#define UMAX_ASSIGN(var, val1, val2)  \
    int var1 = val1; \
    int var2 = val2; \
    var = var1 > var2 ? var1 : var2; \


UMAX_ASSIGN(dur, 0, 4 - number_fuzzy(level / 10));

(a) 一个可疑的命题。你可能应该把它变成一个函数,让宏只用于非函数类的东西。

类似函数的宏在 C 语言的早期非常方便,当时编译器相对笨拙,计算机相对较慢。如今,它们的优势已大大减少。

这样的事情应该是一个好的开始:

int UMax(int a, int b)  // use inline suggestion if desired.
    if (a > b) return a;
    return b;

【讨论】:

【参考方案2】:

你有这个代码:

#define UMAX(a, b) ((a) > (b) ? (a) : (b))
dur = UMAX(0, 4 - number_fuzzy(level / 10));

相当于:

fuzz1 = 4 - number_fuzzy(level / 10);
fuzz2 = 4 - number_fuzzy(level / 10);
dur = 0 > fuzz1 ? 0 : fuzz2;

问题是fuzz1fuzz2不一样,因为它们是使用随机性生成的。

一个简单的修复:

fuzz1 = 4 - number_fuzzy(level / 10);
dur = UMAX(0, fuzz1);

这当然更有效,也更正确。

【讨论】:

原来,我认为fuzz2对应的代码并不总是被评估。 @JonathanLeffler:确实如此,虽然它只会削弱低效率,不会影响(不)正确性。

以上是关于有人可以解释 C 使用 #define UMAX (a, b) ((a) > (b) ? (a) : (b)) 指令的硬逻辑错误,该指令返回较低的值,在 2 个编译器中的主要内容,如果未能解决你的问题,请参考以下文章

max_int和-1

C 语言中typedef和define的区别

有人可以解释一下 C 中 signal() 语法的含义吗? [复制]

有人可以解释如何在 C 编程中将元素附加到数组中吗?

我是stl c ++的新手,有人可以解释[](int x)吗? [复制]

有人可以解释一下这里发生了啥吗?存在普遍量化