首先有个问题:指针和数组有什么关系呢?
答案:什么关系都没有。
指针就是指针,在32位平台下,永远占4个字节,其值为某一个内存的地址。

数组就是数组,其大小与元素的类型和个数有关。定义数组时必须指定其元素的类型和个数。

1.指针数组

指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身决定。它是“储存指针的数组”的简称。

int *a[10];//指针数组,“[]”的优先级比“*”要高,这是一个数组,其包含10个指向int类型数据的指针即变量名与哪一标识符先结合,即为哪种类型。

2.数组指针

数组指针:首先它是一个指针,它指向一个数组。在32位系统下永远是占4个字节,它是“指向数组的指针”的简称。

int(*p)[10];
//解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针
那数组的地址如何来存储?
int arr[10] = {0};
int *p1 = &arr;//错误,&arr 类型为数组指针,p1的类型为int*,类型不匹配
int (*p2)[10] = &arr;//正确,数组指针补充:若所指向大小不同,则类型也不同,如:
int (*p2)[2]=&arr;//错误

3.函数指针

首先来了解数组和指针是如何传参的?
函数本身是没有类型的,只有函数的返回值才有类型

a.数组传参
数组传参时,编译器将其解析为指向其首元素首地址的指针。
实际传递数组的大小与形参指定数组的大小无关。

b.指针传参
指针传参时创建了临时变量。

当一个函数的参数部分为一级指针的时候,函数能接收什么参数?
比如:
void test1(int *p)
{}
//test1函数能接收什么参数?//答案是:若int a[]={1,2};
//则能接受 &a,a[],*a;

举个栗子吧:看看有什么错误?

void GetMemory(char * p, int num)
{p = (char *)malloc(num*sizeof(char));
}
int main()
{char *str = NULL;GetMemory(str,10);strcpy(str,”hello”);free(str);//free并没有起作用,内存泄漏return 0;
}原因:在运行strcpy(str,”hello”)语句的时候发生错误。这时候观察str的值,发现仍然为NULL。也就是说str本身并没有改变,我们malloc的内存的地址并没有赋给str,而是赋给了_str。而这个_str是编译器自动分配和回收的,我们根本就无法使用。那么如何解决呢?解决方案:
第一:用return。
char * GetMemory(char * p, int num)
{p = (char *)malloc(num*sizeof(char));return p;
}
int main()
{char *str = NULL;str = GetMemory(str,10);strcpy(str,”hello”);free(str);return 0;
}这样就复制成功了!
还有一个方法:
用二级指针。
void GetMemory(char ** p, int num)
{*p = (char *)malloc(num*sizeof(char));return p;
}
int main()
{char *str = NULL;GetMemory(&str,10);strcpy(str,”hello”);free(str);return 0;
}注意,这里的参数是&str而非str。这样的话传递过去的是str的地址,是一个值。所以malloc分配的内存地址是真正赋值给了str本身。

接下来进入主题:
c.函数指针

顾名思义,函数指针就是函数的指针。它是一个指针,指向一个函数。

我们用个代码来说明吧:


void test()
{
printf("hehe\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)();
void *pfun2();答案:pfun1可以存放。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void。

来看个有趣的例子吧:
((void() ())0)()——这是什么?

乍一看,也太复杂了吧,what is it?

没有发狂吧?下面我们就来分析分析:
第一步:void(*) (),可以明白这是一个函数指针类型。这个函数没有参数,没有返回值。
第二步:(void(*) ())0,这是将0强制转换为函数指针类型,0是一个地址,也就是说一个函数存在首地址为0的一段区域内。
第三步:((void() ())0),这是取0地址开始的一段内存里面的内容,其内容就是保存在首地址为0的一段区域内的函数。
第四步:((void() ())0)(),这是函数调用。

4.函数指针数组

char * (*pf[3])(char * p);

这是定义一个函数指针数组。它是一个数组,数组名为pf,数组内存储了3个指向函数的指针。这些指针指向一些返回值类型为指向字符的指针、参数为一个指向字符的指针的函数。

函数指针数组怎么使用呢?

#include <stdio.h>
#include <string.h>
char *fun1(char *p)
{printf("%s\n",p);return p;
}
char *fun2(char *p)
{printf("%s\n",p);return p;
}
char *fun3(char *p)
{printf("%s\n",p);return p;
}
int main()
{char *(*pf[3])(char *p);pf[0] = fun1; //可以直接用函数名pf[1] = &fun2; //可以用函数名加上取地址符pf[2] = &fun3;pf[0]("fun1");pf[0]("fun2");pf[0]("fun3");return 0;
}

5.函数指针数组的指针

看着这个标题没发狂吧?反正我是不好了…..
指向函数指针数组的指针是一个 指针 .
指针指向一个 数组 ,数组的元素都是 函数指针 ;

char *(*(*pf)[3])(char *p);分析一下:
pf这个指针指向一个包含了3个元素的数组;这个数组里面存的是指向函数的指针;这些指针指向一些返回值类型为指向字符的指针、参数为一个指向字符的指针的函数。

说完了上面这些概念问题,接下来据多个例子讲解吧:

练习1:intmain()
{int a[4] = { 1, 2, 3, 4};int *ptr1 = (int*)(&a + 1);int *ptr2 = (int*)((int)a + 1);printf( "%x,%x", ptr1[-1], *ptr2);return 0;
}

讲解:
ptr1:将&a+1的值强制转换成int*类型,赋值给int* 类型的变量ptr,ptr1肯定指到数组a的下一个int类型数据了。ptr1[-1]被解析成*(ptr1-1),即ptr1往后退4个byte。所以其值为0x4。
ptr2:(int)a+1的值是元素a[0]的第二个字节的地址。然后把这个地址强制转换成int*类型的值赋给ptr2,也就是说*ptr2的值应该为元素a[0]的第二个字节开始的连续4个byte的内容。
其内存布局如下图:

练习2:

int main()
{int a[5] = { 1, 2, 3, 4, 5};int *ptr = (int *)(&a + 1);printf( "%d,%d", *(a + 1), *(ptr - 1));//输出2,5return 0;
}
//程序的结果是什么?

练习3

structTest
{int Num;char *pcName;short Date;char cha[2];short Ba[4];
}*p;
假设p 的值为0x100000。 如下表表达式的值分别为多少?
p + 0x1= 0x___ ?
(unsigned long)p + 0x1= 0x___ ?
(unsigned int*)p + 0x1= 0x___ ?

解析:p + 0x1= 0x100014;
p+0x1的值为0x100000+sizof(Test)*0x1。至于此结构体的大小20byte。
(unsigned long)p + 0x1=0x100001;
这个表达式其实就是一个无符号的长整型数加上另一个整数。
(unsigned int*)p + 0x1= 0x100004;
p被强制转换成一个指向无符号整型的指针;

练习4:
注意有坑哦!

#include <stdio.h>
int main(int argc, char * argv[])
{int a[3][2] = { (0, 1), (2, 3), (4, 5) };//花括号内为小括号,逗号表达式,相当于int a [3][2]={ 1, 3,5};int *p;p = a[0];printf( "%d", p[0]);//输出 1,3,5
}

练习5:

int main()
{int a[5][5];int (*p)[4];//p是指向一个包含4个元素的数组的指针p = a;printf( "a_ptr=%#p,p_ptr=%#p\n", &a[4][2], &p[4][2]);printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);return 0;
}

那么 FFFFFFFC是什么呢:
-4原码:
1000 0000 0000 0000 0000 0000 0000 0100
反码
1111 1111 1111 1111 1111 1111 1111 1100
F F F F F F F C

练习5:

int main()
{char *c[] = { "ENTER", "NEW", "POINT", "FIRST" };char **cp[] = { c + 3, c + 2, c + 1, c };char ***cpp = cp;printf("%s\n", **++cpp);printf("%s\n", *--*++cpp + 3);printf("%s\n", *cpp[-2] + 3);printf("%s\n", cpp[-1][-1] + 1);system("pause");return 0;
}

解析:

如果还有不懂可参考《c语言深度解剖》这本书!

以上是我的总结,请多指教!

指针数组,数组指针,函数指针,函数指针数组 ,指向函数指针数组的指针相关推荐

  1. go 指针变量和普通变量的转化_C语言 | 指向结构体变量的指针变量

    "要成为绝世高手,并非一朝一夕,除非是天生武学奇才,但是这种人-万中无一" --包租婆这道理放在C语言学习上也一并受用.在编程方面有着天赋异禀的人毕竟是少数,我们大多数人想要从C语 ...

  2. 深入理解指针数组、数组指针、函数指针、函数指针数组、指向函数指针数组的指针

    指针数组 表达式为:int *p[5] 理解:下标运算符[ ]的优先级是要高于指针运算符*,因此p先和下标运算符结合,决定了p首先是个数组,其类型int *,表明数组的元素都是都是指针.而数组占多少个 ...

  3. 指针数组,数组指针,存放数组指针的数组,指向存放数组指针数组的指针,函数指针,函数指针数组,指向函数指针数组的指针

    数组: 一组数据的集合称为数组,它所包含的每一个数据叫做数组元素,例如 int a[4]: 它定义了一个长度为4的整型数组,名字是a . 一般的定义数组可以用 :数据类型 数组名 [数组长度]:来声明 ...

  4. C七:指向函数的指针 ------ 函数指针(function pointer)

    函数具有可赋值给指针的物理内存地址,一个函数的函数名就是一个指针,它指向函数的代码.一个函数的地址是该函数的进入点,也是调用函数的地址.函数的调用可以通过函数名,也可以通过指向函数的指针来调用.函数指 ...

  5. 指向函数的指针--转

    http://book.51cto.com/art/200908/146363.htm 5.1.2 指向函数的指针 C语言通过&和*操作符来操作数据的地址,但它并没有提供一个用一般的方式来操作 ...

  6. 指向函数的指针 ------ 函数指针(function pointer)

    函数具有可赋值给指针的物理内存地址,一个函数的函数名就是一个指针,它指向函数的代码.一个函数的地址是该函数的进入点,也是调用函数的地址.函数的调用可以通过函数名,也可以通过指向函数的指针来调用.函数指 ...

  7. 【数据结构基础】指向函数的指针

    文章目录 一.函数的指针 二.指向函数的指针变量 三.调用函数的两种方式 四.用指向函数的指针作函数参数(重点) 五.为什么要将指向函数的指针变量作为函数的形参(重点) 一.函数的指针 首先,函数名代 ...

  8. 20返回指针的函数与指向函数的指针

    一.返回指针的函数 指针也是C语言中的一种数据类型,因此一个函数的返回值肯定可以是指针类型的. 返回指针的函数的一般形式为:类型名 * 函数名(参数列表) 比如下面这个函数,返回一个指向char类型变 ...

  9. typedef 指向函数的指针

    对复杂变量建立一个类型别名的方法很简单,你只要在传统的变量声明表达式里用类型名替代变量名,然后把关键字typedef加在该语句的开头就行了. // 定义四个函数 int add(int, int); ...

  10. C语言中的指针应用,函数指针,指针函数,结构体中定义函数指针。

    指针函数 指针函数就是一个返回指针的函数,其本质是一个函数,该函数的返回值是一个指针. 声明一个指针函数 int* fun(int x,int y); 例子 typedef struct _Data{ ...

最新文章

  1. [PHP] 自动加载的实现
  2. 再议.Net中null的使用规范
  3. 哪家中国公司为Java 16贡献最多?Java第一大厂居然不是第一的...
  4. java怎么学好 用好接口_Java公开课|Java的实现接口怎么用,这才是学习好Java必备的实用函数,你会吗...
  5. 0515 银行转帐功能演练
  6. Matlab 实现低通/高通/带通滤波器
  7. 泛娱乐出海走出水土不服,元宇宙社交诞生新机会!
  8. 竞品分析:网易云音乐和QQ音乐,音乐类app的战场
  9. office工具栏不见_如何在Office 2013中的快速访问工具栏上显示文件的位置
  10. html 磁贴自动布局,也来“玩”Metro UI之磁贴(一)_html/css_WEB-ITnose
  11. 【openlayers】ol3入门一基础篇
  12. 招聘路径规划算法工程师岗位
  13. 什么是模块化?模块化的好处是什么?
  14. C语言界杠把子的书籍,你读过几本?
  15. Docker Hub
  16. css网页favicon_网站常用的favicon.ico文件详解
  17. 【linux进阶2】linux的高级存储管理(lvm卷的扩展和缩减)
  18. 在投资的不确定性中寻找确定性(二)
  19. 台达变频器调试软件 软件可通过电脑调试台达各系列变频器
  20. TDK| 电源——反激变压器设计过程

热门文章

  1. 解决Intellij编译Scala编译报错问题
  2. pixi 平铺精灵 demo (二)
  3. mp3转换wav文件_如何将WAV文件转换为MP3
  4. ftp mac上传文件到服务器,mac ftp 如何上传文件到服务器
  5. c语言wscript.echo用法,2.4.3 用Wscript.Echo显示简单的文本信息
  6. Android 歌词界面实现(继承Scrollview)
  7. 【在线支付】在线支付代码详解
  8. maxcms支持mysql吗_[转载]maxcms的Access数据库转Mssql数据库超详细教程
  9. thinkphp使用flash上传验证登录问题
  10. STM32F103输出PWM