有意思,我一直没注意到这点。
在读LCC的第二章时,一开始就遇到了这么两个宏:
alloc.c
- #define NEW(p,a) ((p) = allocate(sizeof *(p), (a)))
- #define NEW0(p,a) memset(NEW((p),(a)), 0, sizeof *(p))
书上特别说明了:
引用
注意,NEW和NEW0对p都只计算一次,所以,调用这两个宏时,即使传递的参数表达式有副作用也无妨,如NEW(a[i++])。
可是作为参数的p确实在赋值的两边各出现了一次,为什么p只计算了一次呢?
另外写了两个测试,
第一个,把LCC里的NEW宏简化,只观察sizeof与副作用的关系
test_macro_sideeffect.c
- #include
-
- #define FOO(x) (*(x) = sizeof *(x))
-
- void main( int argc, char* argv[] ) {
- int array[4] = { 0 };
- int* ptr = array;
- FOO( ptr++ );
- printf( "%d, %d, %d, %d\n", array[0], array[1], array[2], array[3] );
- printf( "%x, %x\n", array, ptr );
- }
-
-
-
-
-
第二个,不用宏,直接把sizeof放在表达式里:
- #include
-
- void main( int argc, char* argv[] ) {
- int array[4] = { 40, 30, 20, 10 };
- int* ptr = array;
- sizeof ((*(ptr++))++);
- printf( "%d, %d, %d, %d\n", array[0], array[1], array[2], array[3] );
- printf( "%x, %x\n", array, ptr );
- }
-
-
-
-
-
可以看到LCC里的NEW宏中p之所以只被计算了一次,并不是因为宏的特殊性,而是因为sizeof运算符的特殊性:它所接收的参数并不会被实际求值,而是由编译器判断出参数的表达式的类型后直接换算成常量。
既然不被求值,sizeof参数的表达式中的任何副作用也就不会发生。(不定长数组作为参数的例外)
根据,6.5.3.4 The
sizeof operator:
引用
The sizeof operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type. The size is determined from the type of the operand. The result is an integer. If the type of the operand is a variable length array type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an integer constant.
如果sizeof运算符的参数是一个不定长数组(variable length array),则该参数会被计算;除此之外其它类型的表达式都不会被计算。“不定长数组”的例子如下:
引用
- #include
-
- size_t fsize3(int n)
- {
- char b[n+3];
- return sizeof b;
- }
-
- int main()
- {
- size_t size;
- size = fsize3(10);
- return 0;
- }
"Variable length array"(VLA)到底怎么翻我抓不太准。“可变长数组”或者“不定长数组”似乎都可以,但又都有点模糊。这里我选择了后者。
关于VLA的具体规定请参照规范中的6.7.5.2节:
引用
If the size is not present, the array type is an incomplete type. If the size is * instead of being an expression, the array type is a variable length array type of unspecfied size, which can only be used in declarations with function prototype scope; such arrays are nonetheless complete types. If the size is an integer constant expression and the element type has a known constant size, the array type is not a variable length array type; otherwise, the array type is a variable length array type.