高级声明:

int (*f)();

这里声明有两个括号,第二个括号是函数调用,第一个括号是聚组作用。(*f)是一个函数,所以f是指向返回整型的函数的指针。程序中的每个函数都位于,内存中某个位置,所以存在指向那个位置的指针。

int *f[];

下标的优先级高,所以f是个数组,数组中元素为指向int的指针。

int (*f[])()

首先按照优先级进行分析,括号内(*f[])先求值,所以*f[], f是数组,数组元素是指向某种类型的指针,结合外面的看,f是一个数组,数组元素是指向返回int类型的函数的指针。

函数指针:

指针在使用前需要进行初始化,对于函数指针,访问前需要初始化为指向摸个函数。

int fuc(int);

int (*fPtr)(int) = &fuc;
//在函数初始化之前需要具有fuc的原型,否则,编译器无法检查fuc的类型是否与fPtr一致。

初始表达式中的&操作符是可选的,因为函数名被使用时,总是由编译器把它转换为函数指针,&操作符只是显示的说明了编译器将隐式执行任务。

函数指针被声明后有三种方式调用函数:

int result;

result = func(25);
result = (*fPtr)(25);
result = fPtr(25);

第一种方法,直接调用,执行时函数名首先被转换成一个函数指针,该指针指向函数在内存中的位置。然后,函数调用操作符调用该函数,执行位于这个地址的代码

第二种方法,先间接访问,把函数指针转换成函数名,这个转换时不必要的,执行时还会像第一种方法一样被转换到函数指针。

第三种方法,间接访问时不必要的,编译器需要的是一个函数指针,这里直接使用函数指针。

函数指针也可以作为值,传入其他函数,叫做回调函数。例如下面的类型无关的链表查询函数。

#include <stdio.h>

typedef struct Node {
struct Node *next;
int value;
}Node; /*
* 这里值以及比较函数均是void类型,需要在使用时根据类型自己进行转换
* */
Node *search_list(Node *root, void const *value, int (*compare)(void const *, void const *))
{
Node *current = root;
while(current != NULL){
//回调函数进行比较
if(compare(&current -> value, value) == 0){
break;
}
current = current -> next;
} return current;
}
//声明为void *类型,和compare保持一致
int int_compare(void const *num1, void const * num2){
//根据类型进行转换
if(*(int *)num1 == *(int *)num2){
return 0;
}else {
return 1;
}
} int main()
{
Node third = {NULL, 3};
Node second = {&third, 2};
Node first = {&second, 1};
int target = 2; Node *result = search_list(&first, &target, int_compare); if(result == NULL){
printf("No Found\n");
}else{
printf("got it: %d", result -> value);
} return 0;
}

运行:

如果希望在value为字符的链表中查找,只需要将比较函数改为字符类型既可以。

指针数组的第二个应用就是转移表:

#include <stdio.h>

double add(double, double);
double sub(double, double);
double mul(double, double);
double div(double, double); //转移表中保存函数的指针,确保指针类型相同
double (*transTable[])(double, double) = {add, sub, mul, div}; int main()
{
printf("%g\n", transTable[0](1, 1));
printf("%g\n", transTable[1](2, 1));
printf("%g\n", transTable[2](3, 5));
printf("%g\n", transTable[3](4, 3)); return 0;
} double add(double a, double b)
{
return a + b;
} double sub(double a, double b)
{
return a - b;
} double mul(double a, double b)
{
return a * b;
} double div(double a, double b)
{
return a / b;
}

运行结果:

要注意转移表使用时,越界访问。

命令行参数:

命令行参数是指向指针的指针的另一个用武之地,参数传给C程序main时包含两个形参,argc和argv,argc是命令行参数的数目,argv是保存参数指针的数组。第一个是指向程序名称,末尾是一个NULL指针。

#include <stdio.h>

int main(int argc, char **argv)
{
int count = argc;
printf("count: %d\n", count); for(int idx = 0; idx < count; idx++)
{
printf("[%s]\n", argv[idx]);
} return 0;
}

编译选项添加两个参数

运行结果:

字符串常量:

字符串常量出现于表达式中,它的值是一个指针常量,编译器把这些指定字符拷贝一份储存在内存的某个位置,并储存一个指向第一个字符的指针。我们可以对他进行下标引用。

char *ch = "abcd"  + 1;

ch为指向b的指针。可以把字符串常量和数组名一样看待。

最新文章

  1. Eclipse设置风格
  2. android自定义控件(1)-点击实现开关按钮切换
  3. 对Cookie和Session的深入理解
  4. Linq之扩展方法
  5. Linux合并文件、去除重复行的命令
  6. 【 D3.js 高级系列 — 3.0 】 堆栈图
  7. oracle srvctl 命令
  8. CSS3随笔系列之transform(一)—— transform-origin
  9. 100个linux常用命令
  10. CJOJ 1976 二叉苹果树 / URAL 1018 Binary Apple Tree(树型动态规划)
  11. 处理 NCBI taxonomy tree
  12. MySQL STR_TO_DATE函数
  13. 关于python3链接虚拟机MongoDB 遇到的问题总结
  14. 【搜索】Shuffle&#39;m Up
  15. 常见排序算法总结(java版)
  16. 小结java自带的跟锁相关的一些类
  17. C#基础第六天-作业答案-利用面向对象的思想去实现名片
  18. DevOps Workshop 研发运维一体化(北京第二场) 2016.04.27
  19. CRUX下实现进程隐藏(3)
  20. 文本操作 $(..).text() $(..).html() $(..).val()最后一种主要用于input

热门文章

  1. AC日记——C’s problem(c) TYVJ P4746 (清北学堂2017冬令营入学测试第三题)
  2. [zz]如何在C语言程序中处理汉字
  3. Linux常用命令(二)
  4. ZeroClipboard跨浏览器复制粘贴
  5. BZOJ1002[FJOI2007]轮状病毒
  6. android第一行代码-1.项目结构
  7. jsp response对象
  8. LeetCode:3Sum, 3Sum Closest, 4Sum
  9. 如何设置ASP.NET页面的运行超时时间
  10. JS组件系列——Bootstrap Table 冻结列功能IE浏览器兼容性问题解决方案