如何正确使用作为其他定义参数的定义? C++

How to properly use defines that are parameters to other defines? C++

我有以下定义宏:

#define NHID 5
#define NENT 10
#define NOUT 4
#define NWEIS (NENT + 1) * NHID + (NHID + 1) * NOUT

因此,每次编译器找到 "NWEIS" 时,它都会将 "NEWIS" 替换为“(NENT + 1) * NHID + (NHID + 1) * NOUT”。但这不是我想要的。我希望它用实际值 = 79 替换 "NWEIS",而不必在内存中声明额外的变量。有没有好的方法来做到这一点?

宏替换主要是1一个迭代过程。

在宏替换之后,您将得到一个带有常量的表达式。并且任何体面的编译器都能够折叠这些常量(在编译时评估它们)为您提供 79.

的单个值

例如,考虑程序:

#define NHID 5
#define NENT 10
#define NOUT 4
#define NWEIS (NENT + 1) * NHID + (NHID + 1) * NOUT
int main (void) { return NWEIS; }

这是 gcc -E 的预处理器输出:

int main (void) { return (10 + 1) * 5 + (5 + 1) * 4; }

这是它用 gcc -S 生成的相关汇编代码行(return 值被放入 eax 寄存器):

movl   , %eax

话虽如此,但由于您拥有常量 "variables"、对编译器的内联建议、枚举类型等,因此不再有使用宏的宝贵理由,宏过去非常有用对于.

当然,我仍然发现自己在使用宏来编写快速的'n'dirty 代码,但这主要是因为我是一个老手,在我们甚至没有原型之前的早期 C 时代伪造:-)

可能也值得重新考虑您对它们的使用,因为您可以用类似的东西替换它:

const int nhid  =  5;
const int nent  = 10;
const int nout  =  4;
const int nweis = (nent + 1) * nhid + (nhid + 1) * nout;

一个聪明的编译器应该仍然能够在编译时优化计算,你很可能会发现调试器中的变量对你来说是可用的,这是宏通常不会发生。


1 可以在 C++11 标准的 16.3 Macro replacement.

节中找到完整的详细信息

足以说明 ### 在宏中的某些用途可以防止进一步替换 that 令牌(前者替换令牌带有字符串文字,后者将多个标记组合成一个不同的标记)。

由于您没有使用这些,因此与此处无关。

您正在使用的宏不会占用您额外的内存。你已经达到了你想要的。

让我们看看合理的编译器会做什么。

假设你有这段代码。

#define NHID 5
#define NENT 10
#define NOUT 4
#define NWEIS (NENT + 1) * NHID + (NHID + 1) * NOUT

int f()
{
    return NWEIS;
}

一个合理的编译器显然会把它展开成:

int f()
{
    return (NENT + 1) * NHID + (NHID + 1) * NOUT;
}

下一步将是:

int f()
{
    return (10 + 1) * 5 + (5 + 1) * 4;
}

由于此算术表达式仅由硬编码数字(常量表达式)组成,因此编译器也可以将整个事物视为常量。

int f()
{
    return 79;
}

注意这个函数很小,合理的编译器会尽量内联这个函数。


但是,这样做更可取:

constexpr int NHID = 5;
constexpr int NENT = 10;
constexpr int NOUT = 4;
constexpr int NWEIS = (NENT + 1) * NHID + (NHID + 1) * NOUT;

只需使用

const int NHID = 5;
const int NENT 10;
const int NOUT 4;
const int NWEIS = (NENT + 1) * NHID + (NHID + 1) * NOUT;

一个好的优化器会在编译时替换这些值并且不会在内存中放置任何变量,除非你做一些事情,比如获取它们的地址。然后你就拥有了 C++ 的类型安全和作用域,而没有宏邪恶。

(大写名称是为宏保留的约定,因此您可能需要稍微重命名它们)