循环#在C中定义

时间:2019-04-19 10:46:57

标签: c random c-preprocessor

请注意-我没有编写此代码,我只是在尝试维护/改进它。

我的头文件之一包含以下几行:

/* macros to seed the random number generator */
/* This is needed on Windows which throws an error due to 'random' 
not being defined on MingW.  I'll clean it up later. */
#define srandom srand
#define random rand

/* New random seed function. */
#define srand srandom
#define rand random

#define rnd(x)  ((int)(rand() % (x)) + 1)
#define rund(x) ((int)(rand() % (x)))

此外,我具有所有四个功能randsrandrandomsrandom的手册页,因此我假设它们都是有效的C函数。 / p>

您能帮我理解/弄清楚我打电话给rnd(10)时会发生什么吗?这是未定义的行为吗?一个定义是否以某种方式“替代”另一个?

(我问是因为我觉得我的实际程序中有很多数字,即使我的测试程序似乎可以正常工作并且随机分布。)


请快速注意一下,我几乎肯定我所看到的“大量低数”可能是运算符优先级和括号中的错误,导致长布尔值始终评估为“ true”。

5 个答案:

答案 0 :(得分:3)

递归预处理器宏的效果是明确定义的:在扩展宏foo的定义时,如果再次找到名称foo,则将其保留。因此,在您的情况下,rand扩展到random,然后扩展到rand,然后再不扩展。

从技术上讲,如果您的代码还包含stdlib.h,则这些定义的行为是不确定的,因为如果您定义(或取消定义)其名称也是标准库函数名称的宏,则该行为是不确定的由包含的标头拉入。实际上,如果标准库头仅将这些名称定义为函数而不是宏,则这些宏定义将不起作用。如果它们将名称定义为宏,则会出现编译错误。

函数randsrand由核心C语言定义,并且只要您包含stdlib.h就可以使用(某些嵌入式实现不提供完整的C库除外) 。 randomsrandom是Unix / POSIX的附加内容,要使用它们,您需要在#define _XOPEN_SOURCE 500之前定义一个符号,例如#include <stdlib.h>

我很困惑这些定义如何解决MinGW上的任何问题。如果MinGW根本没有定义random,则预处理器会将调用random()扩展为random(),并且仍然尝试调用未定义的函数。看起来好像是一系列连续的黑客攻击造成了无害的剩余杂物,或者有人摸索着解决了一个问题,最终以不同的方式解决了这个问题,但是即使这并没有对解决方案有所帮助,还是犯了这部分。

答案 1 :(得分:2)

对于给定的扩展,C预处理器不会多次评估同一宏扩展。也就是说,一旦识别出周期,它将打破周期。因此,在您的情况下,将发生以下扩展:

rnd(10) // list of available macros: [srandom, random, srand, rand, rnd(x), rund(x)]
((int)(rand() % (10)) + 1) // list of available macros: [srandom, random, srand, rand, rund(x)]
((int)(random() % (10)) + 1) // list of available macros: [srandom, random, srand, rund(x)]
((int)(rand() % (10)) + 1) // list of available macros: [srandom, srand, rund(x)]

在最后一步,预处理器将找不到更多可用的扩展(已完成rand的操作)并将放弃。

这与为什么您看到的小数字多于大数字无关。这可能与RAND_MAX的不同定义有关。在一个非常人为的示例中,如果RAND_MAX = 10,则0出现的可能性为18%,而其他数字出现的可能性为9%。不仅如此,典型的rand实现对于较低位倾向于具有较低的熵。如果对您来说重要的是要有一个统一的发行版,那么您可能需要在标准库之外查找。

答案 2 :(得分:0)

您的编译器有一个神奇的选项-E来查看扩展。

https://godbolt.org/z/_PIvet

int foo(int x)
{
    return rand(x);
}

int foo(int x)
{
    return rnd(x);
}

扩展到

int foo(int x)
{
    return rand(x);
}

int foo(int x)
{
    return ((int)(rand() % (x)) + 1);
}

BTW IMO最好改用内联函数。

答案 3 :(得分:0)

blue paint algorithm中,一个符号不能被多次评估。 rand返回后,random被涂成蓝色,因此rand将不再扩展为random

答案 4 :(得分:-2)

似乎第一个预处理器将更新源并将srand替换为srandom,然后它将再次更新源并将srandom替换为srand。我已经检查过cpp,看起来还可以。程序也可以正常工作。我对srand宏有个新的想法,因为stdlib.h中有一个函数具有相同的名称,但没有发生任何不好的事情。