C pointer

C pointer

十二月 15, 2017

地址跳转

1
void(*reset)(void)= (void(*)(void))0

void(*reset)(void)就是函数指针定义,(void(*)(void))0是强制类型转换操作,将数值0强制转换为函数指针地址0,通过调用reset()函数,程序就会跳转到程序执行的0地址处重新执行。在一些其他高级单片机Bootloader中,如NBoot、UBoot、EBoot,经常通过这些Bootloader进行下载程序,然后通过函数指针跳转到要执行程序的地址处。

1
2
3
4
5
void (*theUboot)(void);
。。。。
theUboot = (void (*)(void))(0x30700000);
theUboot();
。。。。。
1
(*(void (*)(void))(0x30700000))();

强制类型转换,将一个绝对地址转换为一个函数指针,并调用这个函数以跳转到前面提到的绝对地址.
翻译成汇编就是:

1
2
mov r0,0x30700000;
mov pc,r0

对于(*(void (*)(void))(0x30700000))();
可以这样理解:
首先(void( * )(void) )是一个强制类型转换符,他将后面的0x30700000这个无符号整数强制转化为一个函数指针,该函数指针所指向的函数入口参数为void,返回值也是void 如果到这步你看懂了,那么设(void (*)(void))(0x30700000)fp; 那么上面的表达式就可以简化为 (*fp)(); OK,这下就清楚了吧,我们将上面转化好的函数指针进行引用(也就是调用函数指针指向的函数)。

右左法则

右左规则:首先从最里面的括号看起,然后往右看,再往左看。每当遇到圆括号时,就应该掉转阅读方向。一旦解析完圆括号里面所有的东西,就跳出圆括号。重复这个过程直到整个声明解析完毕。

原文作者对这里进行了修正:应该是从未定义的标识符开始阅读,而不是从括号读起,之所以是未定义的标识符,是因为一个声明里可能有多个标识符,但未定义的标识符只会有一个

1
int (*func) (int *p);

首先找到未定义标识符,就是func,它的外面有一对圆括号,而且左边是一个*,这说明func是一个指针。然后跳出括号,看右边,也是一个括号,这说明(*func)是一个函数,而func是一个指向这类函数的指针,也就是一个函数指针。这类函数具有int*类型的参数,返回值类型是int


1
int (*func)(int *p, int (*f)(int *));

func被一对括号包含,且左边有一个*号,说明func是一个指针,然后跳出这个圆括号,先看右边,也是一个圆括号,说明func是一个函数指针。这类函数具有int *int (*)(int *)这样的形参,返回值是int。对于int (*f)(int *)的形参,分析方法跟func是一致的.


1
int (*func[5])(int *p);

func右边是一个[]运算符,说明func是一个具有5个元素的数组,func的左边有一个*,说明func的元素是指针,要注意这里的*不是修饰func的,而是修饰func[5]的,原因是[]运算符的优先级比*高,func先跟[]结合,因此*修饰的是func[5].跳出这个括号,看右边,也是一对圆括号,说明func数组的元素是函数类型的指针,它指向的函数具有int*类型的形参,返回值类型是int


1
int (*(*func)[5])(int *p);

func被一对圆括号包围,左边又有一个*,那么func是一个指针,跳出括号,右边是一个[]运算符号,说明func是一个指向数组的指针,现在往左看,左边有一个*号,说明这个数组的元素是指针,再跳出括号,向右看,右边又有一个括号,说明这个数组的元素是指向函数的指针。总结一下就是:func是一个指向数组的指针,这个数组的元素是函数指针,这些指针具有int *形参,返回值为int类型的函数


1
int (*(*func)(int *p))[5];

func是一个函数指针,这类函数具有int *类型的形参,返回值是指向数组的指针,所指向的是具有5int类型元素的数组。