int main() { return (********puts)("Hello"); }为何可以运行?
=>> Except when it is the operand of the sizeof operator or the unary & operator, a function designator with type ‘‘function returning type’’ is converted to an expression that has type ‘‘pointer to function returning type’’.
=>> * is Right associative operator
补充说明:C语言标准规定,函数指示符(function designator,即函数名字)既不是左值,也不是右值。
除了作为sizeof或取地址&的操作数,函数指示符在表达式中自动转换为函数指针类型右值。因此通过一个函数指针调用所指的函数,不需要在函数指针前加取值或反引用(*)运算符
#include <stdio.h>
// fptr is a pointer, point to a function
void (*fptr) ();
// an empty function
void test() {
;
}
int main() {
fptr = test;
printf("test: %x, fptr: %x
", test, fptr);
return 0;
}void (*fptr)();
void test();
fptr = test;test: void (), 会被转成 function pointer: void (*) ()
fptr is a pointer to function with returning type,
(*fptr) is a function designator, a function designator will be converted to pointer.
type of (*fptr): void ()
我们可以利用 gdb 去查看这些数值,搭配 print 指令
(gdb) print test
$1 = {void ()} 0x400526 <test>
(gdb) print test
$2 = {void ()} 0x400526 <test>
(gdb) print fptr
$3 = (void (*)()) 0x400526 <test>
(gdb) print (*fptr)
$4 = {void ()} 0x400526 <test>
(gdb) print (**fptr)
$5 = {void ()} 0x400526 <test>test 是一个 function designator ,由于不是搭配 &, sizeof 使用,所以会被转成为 pointer to function
(*fptr) 是一個 function pointer 搭配 * (dereference, indirection) operator 使用,则它的结果会被转成为一个 function designator
所以 (**fptr) 要拆成两个部分: (* ( *fptr) ), 里面的 *fptr 是个 function designator, 但是还是会被转成一个 pointer to function,我们可注记为 fptr。
又,外面又有一个 * operator, ( *fptr’ 是個 function designator, 最后还是会被转化为 pointer to function
但是,0x400526 这个数值是怎么来的呢?
我们可以使用以下指令观察:
$ gdb -o fp -g fp.c ` & ` objdump -d fp `参考输出里面的一段:
0000000000400526 <test>:
400526: 55 push %rbp
400527: 48 89 e5 mov %rsp,%rbp
40052a: 90 nop
40052b: 5d pop %rbp
40052c: c3 retq这个数值实则是可执行文件反编译后的函数入口,但是这个数值并非是在实际内存中的数值,而是虚拟存储器的地址。
由于puts 的 function prototype 是 int puts(const char *s),因此每次经过 * operator 运算后得到的结果是依旧是 int。所以,* 的数目不會影响结果。最后return 的值是根据 s 的长度加上 ’ ’。而这个例子 return 给 main 的值是 6。
对应到 C99/C11 规范 [ 6.5.3.2 ],& 所能操作的 operand 只能是:
* - operand 本身
[] - & 会消失,而 [] 会被转换成只剩 + (注:原本 [] 会是 + 搭配 *)
例如: &(a[5]) == a + 5
bit-field:一种在 struct 或 union 中使用用来节省内存空间的object;
特別的用途:沒有名称的 bit-field 可以做为padding
除了遇到 [] 或 * 外,使用 & 的结果基本上都是得到 pointer to the object 或是 function 的 address
思考以下程序:
char str[123];为何 str == &str 呢?
规范中表明:除非遇到 sizeof 或是 & 之外,array of type (在这就是指 str) 都会被直接解释成 pointer to type (在這就是 pointer to char),而这个type 是根根据 array 的第一個元素来
Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. (C99 6.3.2.1)
上面提到:遇到 & 时,str 不会被解读为pointer to type,而是作为原本的 object,在这就是 array object,而 address of array object 也就是这个 array object 的起始地址,当然也就会跟第一个元素的地址一样
除了用值一样來解释外,规范在提到 equality operators 时,也有说到类似情景
Two pointers compare equal if and only if both are null pointers, both are pointers to the same object (including a pointer to an object and a subobject at its beginning) or function (C99 6.5.9)
char * const pContent;
const char * pContent;
char const * pContent;
const char * const pContent;
Are there any cases of multi-dimensional arrays? Because those actually have semantic meaning outside of sizeof(), just in things like adding offsets. Eg something like
int fn(int a[][10])int fn(int (*a)[10])and "a+1" is actually 40 bytes ahead of "a", so it does not act like an "int *". (And I might have screwed that up mightily - C multidimensional arrays and the conversions to pointers are really easy to get confused about. Which is why I hope we don’t have them)
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
`char *`