3.10.4 Duplication of Side Effects
Many C programs define a macro min
, for “minimum”, like this:
#define min(X, Y) ((X) < (Y) ? (X) : (Y))
When you use this macro with an argument containing a side effect, as shown here,
next = min (x + y, foo (z));
it expands as follows:
next = ((x + y) < (foo (z)) ? (x + y) : (foo (z)));
where x + y
has been substituted for X
and foo (z)
for Y
.
The function foo
is used only once in the statement as it appears in the program, but the expression foo (z)
has been substituted twice into the macro expansion. As a result, foo
might be called two times when the statement is executed. If it has side effects or if it takes a long time to compute, the results might not be what you intended. We say that min
is an unsafe macro.
The best solution to this problem is to define min
in a way that computes the value of foo (z)
only once. The C language offers no standard way to do this, but it can be done with GNU extensions as follows:
#define min(X, Y) \
({ typeof (X) x_ = (X); \
typeof (Y) y_ = (Y); \
(x_ < y_) ? x_ : y_; })
The `({ ... })' notation produces a compound statement that acts as an expression. Its value is the value of its last statement. This permits us to define local variables and assign each argument to one. The local variables have underscores after their names to reduce the risk of conflict with an identifier of wider scope (it is impossible to avoid this entirely). Now each argument is evaluated exactly once.
If you do not wish to use GNU C extensions, the only solution is to be careful when using the macro min
. For example, you can calculate the value of foo (z)
, save it in a variable, and use that variable in min
:
#define min(X, Y) ((X) < (Y) ? (X) : (Y))
...
{
int tem = foo (z);
next = min (x + y, tem);
}
(where we assume that foo
returns type int
).
http://developer.apple.com/library/mac/#documentation/DeveloperTools/gcc-4.0.1/cpp/Duplication-of-Side-Effects.html#Duplication-of-Side-Effects
<!-- /CONTENTS --><!-- INSERT BREADCRUMB --> <script src="/library/webstats/pagetracker.js" type="text/javascript"></script><script type="text/javascript"></script>
分享到:
相关推荐
### C语言宏定义中的##连接符和#符详解 在C语言编程中,宏定义是一种常用的预处理功能,它能够帮助开发者实现代码的简化、复用以及特定逻辑的快速构建。本文将详细介绍C语言宏定义中两个特殊符号的使用:连接符`##`...
C语言宏定义是一种预处理机制,它允许程序员创建符号常量和简单的代码替换规则,以增强代码的可读性、可维护性和通用性。宏定义主要有两种类型:不带参数的宏定义和带参数的宏定义。 不带参数的宏定义通过`#define`...
标题《C语言宏定义技巧.pdf》表明本文档主要涉及C语言中宏定义的使用技巧。宏定义是C语言中一种预处理指令,通过它可以定义一些可以替换的值或代码片段,这有助于提高代码的可读性、可维护性和可移植性。在描述中...
3. **避免副作用**:由于宏是在编译阶段进行文本替换,所以它们可能会引起意外的副作用。例如,`#define INC(x) x++`,如果在表达式中使用此宏,如`a = INC(b) + c;`,可能会导致意料之外的结果,因为宏展开后,`b`...
C语言宏的使用能够提高代码的灵活性和可维护性,但也需要注意避免滥用,因为宏不遵循作用域规则,可能会导致意外的副作用。在编写宏时,应尽量确保它们是安全的,避免引入潜在的错误,例如使用`#define`定义函数式...
### C语言宏:定义、使用及其最佳实践 #### 引言 在C语言中,宏是一种重要的预处理器特性,它提供了强大的文本替换功能,能够帮助开发者定义并复用代码片段,进而提高代码的可读性、可维护性,并增强编程的灵活性。...
以下是一些常见的C语言宏定义技巧: 1. **防止头文件重复包含**: 使用`#ifndef`、`#define`、`#endif`三元组可以避免头文件在多个地方被包含时导致的编译错误。例如,当头文件`comdef.h`被包含时,如果已经定义了...
- **副作用处理**:如果宏中包含有副作用的操作(如函数调用),则需要特别小心。例如,考虑下面的宏定义: ```c #define min(x, y) ((x) > (y) ? (y) : (x)) ``` 当这样使用时: ```c int result = min(a...
然而,宏定义也有一些潜在的问题,如宏展开可能引发意料之外的副作用,特别是当宏涉及运算符重载或者括号不当时。因此,在使用宏定义时需谨慎,确保其行为符合预期,并尽可能利用C语言的函数和常量来替代简单的宏...
### C语言中宏定义的重要性与应用技巧 #### 宏定义概览 宏定义是C语言中一项非常重要的功能,它允许程序员定义一个符号...正确的实践是在确保不会引起副作用的前提下,合理地利用宏定义来增强代码的清晰度和灵活性。
C语言宏定义是预处理器(Preprocessor)提供的一种文本替换机制,它允许程序员在编译期间替换特定的标识符(宏名称)为指定的文本(宏字符串)。在宏定义中,宏名称和宏字符串的区分至关重要,它们之间通过空格进行...
- **避免副作用**:宏定义应避免副作用,确保其行为是可预测的。例如: ```c // 错误的宏定义,有副作用 #define INC(x) (++x) // 正确的宏定义,无副作用 #define INC(x) ((x) + 1) ``` - **避免复杂的...
在C语言中,宏定义是通过预处理器进行文本替换,因此在使用宏时需要注意宏的参数列表以及可能引入的副作用。 关于操作符的使用,笔记中提到了操作符的优先级和宏#define的用法。操作符优先级决定了在没有括号的情况...
它们可能会导致一些问题,如宏展开后的副作用、类型安全问题等。例如,下面的宏在某些情况下可能会出现问题: ```c #define INC(x) x++ ``` 如果在表达式中使用这个宏,可能会导致预期之外的结果,因为宏展开不...
- 宏展开可能引起副作用,如`ADD(a+b)*c`。 11. **数组名作为参数**: - 数组名作为参数时,实际上传递的是数组的首地址。 12. **指针操作**: - 指针变量不能直接相加,但可以比较和相减,也可以指向同一个...
6. **预处理器宏**:预处理器宏在C语言中用于文本替换,虽然方便但也可能导致一些问题,如宏展开的副作用、名字冲突等。了解宏定义的规则和使用注意事项,可以避免一些陷阱。 7. **文件操作**:C语言通过`fopen()`,...
理解宏定义的使用,以及宏展开时可能出现的问题,如宏参数的副作用,是C语言基础的重要部分。 4. **结构体与联合体**: 结构体允许将不同类型的数据组合成一个整体,而联合体则是在同一内存空间内存储不同类型的...
例如,宏替换可能会产生宏展开后的代码超出了原有语句的逻辑边界,或者宏定义中的参数未被正确替换,造成宏的副作用。 总之,C语言的预处理是C语言编程的一个非常强大的工具,它为C语言的编译提供了必要的前期处理...
理解预处理器的工作机制,合理使用宏定义可以提高代码的灵活性和复用性,但也要避免滥用导致的副作用。 四、结构体与联合体 结构体和联合体是C语言中组织复杂数据结构的方式。结构体允许我们将不同类型的变量组合...