C 中的循环#defines
circular #defines in C
请注意——我没有编写此代码,我只是想维护/改进它。
我的一个头文件包含这些行:
/* 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)))
此外,我有所有四个函数的手册页 rand
、srand
、random
、srandom
,所以我假设这些都是有效的 C 函数.
你能帮我理解/弄清楚当我调用 rnd(10)
时会发生什么吗?这是未定义的行为吗?一个定义 "override" 是否以某种方式定义另一个?
(我问是因为我觉得我在实际程序中得到了很多低数字,即使我的测试程序似乎工作正常并且随机分布。)
请注意,我几乎可以肯定,我看到的 "lot of low numbers" 可能是运算符优先级和括号中的错误,导致长布尔值始终计算为 "true"。
看起来第一个预处理器将更新源代码并将 srand
替换为 srandom
然后它将再次更新源代码并将 srandom
替换为 srand
。我用 cpp
检查过,看起来没问题。程序也有效。我正在重新考虑 srand
宏,因为 stdlib.h
中有一个函数具有相同的名称,但没有发生任何不好的事情。
您的编译器有一个神奇的选项 -E 来查看扩展。
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);
}
顺便说一句,IMO 最好改用内联函数。
递归预处理器宏的作用是well-defined:在扩展宏foo
的定义时,如果再次找到名称foo
,则保留它。因此,在您的情况下,rand
扩展为 random
,后者扩展为 rand
,不会进一步扩展。
从技术上讲,如果您的代码还包含 stdlib.h
,则这些定义的行为是未定义的,因为如果您定义(或取消定义)名称也是标准库函数名称的宏,则行为是未定义的它由包含的 header 拉入。实际上,如果标准库 headers 只将这些名称定义为函数而不是宏,那么这些宏定义将不会有任何效果。如果他们将名称定义为宏,您将遇到编译错误。
函数 rand
和 srand
由核心 C 语言定义,只要您包含 stdlib.h
就可以使用(某些不提供完整 C 语言的嵌入式实现除外)图书馆)。 random
和srandom
是Unix/POSIX的加法,要使用它们需要在#include <stdlib.h>
.
前定义一个符号如#define _XOPEN_SOURCE 500
我很困惑这些定义如何解决 MinGW 上的任何问题。如果 MinGW 根本没有定义 random
,那么调用 random()
会被预处理器扩展为 random()
并且仍然会尝试调用未定义的函数。这看起来要么是一系列连续的 hack 导致了无害的遗留问题,要么是有人笨手笨脚地解决了一个问题,最终以不同的方式解决了它,但尽管它对解决方案没有贡献,但还是提交了那部分内容。
对于给定的展开,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 实现往往具有较低位的低熵。如果统一分布对您很重要,您可能需要查看标准库之外的内容。
在 blue paint algorithm 中,一个符号不会计算超过一次。 rand
被random
返回后被涂成蓝色,所以rand
不会再扩展为random
。
请注意——我没有编写此代码,我只是想维护/改进它。
我的一个头文件包含这些行:
/* 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)))
此外,我有所有四个函数的手册页 rand
、srand
、random
、srandom
,所以我假设这些都是有效的 C 函数.
你能帮我理解/弄清楚当我调用 rnd(10)
时会发生什么吗?这是未定义的行为吗?一个定义 "override" 是否以某种方式定义另一个?
(我问是因为我觉得我在实际程序中得到了很多低数字,即使我的测试程序似乎工作正常并且随机分布。)
请注意,我几乎可以肯定,我看到的 "lot of low numbers" 可能是运算符优先级和括号中的错误,导致长布尔值始终计算为 "true"。
看起来第一个预处理器将更新源代码并将 srand
替换为 srandom
然后它将再次更新源代码并将 srandom
替换为 srand
。我用 cpp
检查过,看起来没问题。程序也有效。我正在重新考虑 srand
宏,因为 stdlib.h
中有一个函数具有相同的名称,但没有发生任何不好的事情。
您的编译器有一个神奇的选项 -E 来查看扩展。
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);
}
顺便说一句,IMO 最好改用内联函数。
递归预处理器宏的作用是well-defined:在扩展宏foo
的定义时,如果再次找到名称foo
,则保留它。因此,在您的情况下,rand
扩展为 random
,后者扩展为 rand
,不会进一步扩展。
从技术上讲,如果您的代码还包含 stdlib.h
,则这些定义的行为是未定义的,因为如果您定义(或取消定义)名称也是标准库函数名称的宏,则行为是未定义的它由包含的 header 拉入。实际上,如果标准库 headers 只将这些名称定义为函数而不是宏,那么这些宏定义将不会有任何效果。如果他们将名称定义为宏,您将遇到编译错误。
函数 rand
和 srand
由核心 C 语言定义,只要您包含 stdlib.h
就可以使用(某些不提供完整 C 语言的嵌入式实现除外)图书馆)。 random
和srandom
是Unix/POSIX的加法,要使用它们需要在#include <stdlib.h>
.
#define _XOPEN_SOURCE 500
我很困惑这些定义如何解决 MinGW 上的任何问题。如果 MinGW 根本没有定义 random
,那么调用 random()
会被预处理器扩展为 random()
并且仍然会尝试调用未定义的函数。这看起来要么是一系列连续的 hack 导致了无害的遗留问题,要么是有人笨手笨脚地解决了一个问题,最终以不同的方式解决了它,但尽管它对解决方案没有贡献,但还是提交了那部分内容。
对于给定的展开,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 实现往往具有较低位的低熵。如果统一分布对您很重要,您可能需要查看标准库之外的内容。
在 blue paint algorithm 中,一个符号不会计算超过一次。 rand
被random
返回后被涂成蓝色,所以rand
不会再扩展为random
。