C语言笔记之指针
1.指针的引入
指针==地址,指针就是地址,地址就是指针
变量四种概念:
- 类型
- 变量名
- 值
- 内存地址
例:
int data = 80;
//类型: int ;变量名:data ;值:80 ;内存地址 4
#include <stdio.h>
int main(){
printf("win 10系统下int类型的大小为:%d",sizeof(int));
return 0;
}
运行结果:
内存地址大小因操作系统而异。
一个变量访问可以通过变量名访问,也可以通过地址的方式访问。
#include <stdio.h>
int main()
{
int data = 80;
printf("data的值为:%d\n",data); //通过变量名访问值
printf("data的地址为:0x%p\n",&data); //&:取地址符 0x:16进制呈现
printf("data的值为:%d\n",*(&data)); //通过地址访问值,*取值运算符,把后面跟的内存地址的数据取出来
return 0;
}
运行结果:
2.指针变量的引入
指针变量==存放地址的变量
*的标识作用:只产生在指针变量定义或声明的时候。
*的运算作用:取地址内容的时候。
#include <stdio.h>
int main()
{
int data = 80;
int *p;//这里的*是一个标识符,告诉系统我是一个指针变量,是用来保存别人地址的,和下方的运算服不同
p = &data; //给指针变量赋值
printf("变量名访问data的值为:%d\n",data); //通过变量名访问值
printf("data的地址为:0x%p\n",&data); //&:取地址符 0x:16进制呈现
printf("地址访问data的值为:%d\n",*(&data)); //通过地址访问值,*取值运算符,把后面跟的内存地址的数据取出来
printf("指针变量访问data的值为:%d\n",*p); //通过指针变量间接访问
return 0;
}
运行结果:
3.指针变量为什么要求类型
通过结果看出差别:
#include <stdio.h>
int main()
{
int a = 0x1234;
int *p = &a;
char *c = &a;
printf("p = %p\n",p);
printf("c = %p\n",c);
printf("a = %x\n",*p);//取值的时候出了问题,取值运算符会根据指针变量类型,访问不同大小的空间
printf("a = %x\n",*c);
printf("++p = %p\n",++p);
printf("++c = %p\n",++c);
return 0;
}
运行结果:
红色方框内为警告:警告原因是不兼容的指针类型’int *'初始化’char * ,也就是为什么要求指针变量类型要一致的原因。指针变量类型要一致:决定指向内存空间的大小,也决定增量的大小。
4.指针场景一
传递实参地址到形参内存空间,通过*访问实参地址内容,进而实现交换两数
#include <stdio.h>
void chageData(int *pdata1,int *pdata2)
{
int temp;
temp = *pdata1;
*pdata1 = *pdata2;
*pdata2 = temp;
}
int main()
{
int data1 = 2;
int data2 = 8;
printf("data1 = %d,data2 = %d\n",data1,data2);
chageData(&data1,&data2); //传地址
printf("交换data1 = %d,data2 = %d\n",data1,data2);
return 0;
}
运行结果:
5.指针场景二
指向固定的区域
#include <stdio.h>
int main()
{
int a = 10;
printf("address of a is 0x%p\n",&a);
volatile unsigned int *p = (volatile unsigned int *)0x0000000000610000;//volatile:防止编译器优化 ;unsigned int:无符号整形数
printf("p = 0x%p\n",p);
return 0;
}
运行结果:
6.指针回顾&小练习
一、
-
指针是什么?
指针等同于地址。
-
指针变量是什么?
存放地址的变量
二、
*的标识作用:只产生在指针变量定义或声明的时候。
*的运算作用:取地址内容的时候。
三、
变量名的两种访问方式:
1. 直接访问(变量名访问)
2. 间接访问(*取地址访问)
四、
指针讲解内存大小:指针变量类型要一致:决定指向内存空间的大小,也决定增量的大小。 不同类型跨度不一样。
五、
为什么要用指针?
例如场景一,通过指针的方式,传递地址,修改参数。
小练习:
输入三个数a,b,c;要求不管怎么输入,输出时从大到小的顺序输出。(使用函数封装)
#include <stdio.h>
void changDataThree(int *data1,int *data2,int *data3)
{
int max[3] = {0};
max[0] = *data1;
max[1] = *data2;
max[2] = *data3;
int i;
int j;
for(i = 0; i < 3;i++){
printf("%d",max[i]);
}
int maxnum = 0;
for(i=0;i < 2;i++){
for(j=0;j<2-i;j++){
if(max[j] < max[j+1]){
maxnum=max[j+1];
max[j+1]=max[j];
max[j] = maxnum;
}
}
}
*data1=max[0];
*data2=max[1];
*data3=max[2];
}
int main()
{
int data1;
int data2;
int data3;
printf("请输入第一个数:\n");
scanf("%d",&data1);
printf("请输入第二个数:\n");
scanf("%d",&data2);
printf("请输入第三个数:\n");
scanf("%d",&data3);
changDataThree(&data1,&data2,&data3);
printf("从大到小输出结果:%d %d %d",data1,data2,data3);
return 0;
}
运行结果:
7.定义一个指针变量指向数组
定义一个指针变量指向一个数组元素
int data[5]={8,46,15,3,45}; //定义data为包含5个整型数据的数组
int *p; //定义p为指向整型变量的指针变量
p = &data[0]; //把data[0]元素的地址赋给指针变量p
//指向数组首元素的地址
p = &data[0]; //p的值是data[0]的地址
p = data; //p的值是数组data首元素(即data[0])的地址
//等于数组名:指向数组起始位置
测试:
#include <stdio.h>
int main()
{
int array[3]={45,98,69};
int *p;
//p = &array[0];
p = array; //数组名 就是第一个元素的地址;
printf("array的地址:%p\n",p);
printf("首元素是:%d\n",*p);
return 0;
}
运行结果:
8.指针偏移遍历数组
#include <stdio.h>
int main()
{
int array[3]={45,98,69};
int *p;
//p = &array[0];
p = array; //数组名 就是第一个元素的地址;
printf("array的地址:%p\n",p);
printf("第一个元素是:%d\n",*p);
printf("第二个元素是:%d\n",*(p+1));//*(p+1) : p+1:不是地址+1,而是地址偏移了一个类型(在这里是int类型,也就是4个字节)的字节数 ,再取p偏移 //一个类型的字节数后地址的内容
printf("第三个元素是:%d\n",*(p+2));//p+2 偏移两个类型(在这里是int类型,也就是4个字节)字节数
return 0;
}
运行结果:
char *p时:
#include <stdio.h>
int main()
{
char array[3]={'d','g','p'};
char *p;
//p = &array[0];
p = array; //数组名 就是第一个元素的地址;
printf("array的地址:%p\n",p);
printf("第一个元素是:%c\n",*p);
printf("第二个元素是:%c\n",*(p+1));//*(p+1) : p+1:不是地址+1,而是地址偏移了一个类型(在这里是char类型,也就是1个字节)的字节数 ,再取p偏移 //一个类型的字节数后地址的内容
printf("第三个元素是:%c\n",*(p+2));//p+2 偏移两个类型(在这里是char类型,也就是1个字节)字节数
return 0;
}
运行结果:
指针遍历数组:
#include <stdio.h>
int main()
{
int arr[3] = {3,4,5};
int *p;
p = arr;
int i;
for(i = 0; i < 3; i++){
printf("地址:0x%p ,值:%d\t",&arr[i],arr[i]);
}
puts("");
for(i = 0; i < 3; i++){
printf("地址:0x%p ,值:%d\t",(p+i),*(p+i));
//printf("%d\t",*p++); //*优先级>++,先取内容再++;
/*
printf("%d\t",*p);
p++;
*/
}
return 0;
}
运行结果:
char类型时:
#include <stdio.h>
int main()
{
char arr[3] = {'b','g','m'};
char *p;
p = arr;
int i;
for(i = 0; i < 3; i++){
printf("地址:0x%p ,值:%c\t",&arr[i],arr[i]);
}
puts("");
for(i = 0; i < 3; i++){
printf("地址:0x%p ,值:%c\t",(p+i),*(p+i));
//printf("%d\t",*p++); //*优先级>++,先取内容再++;
/*
printf("%d\t",*p);
p++;
*/
}
return 0;
}
运行结果:
上面两个案列比较可以看出:指针变量类型要一致的重要性!按住Ctrl点我:四
访问数组元素:
-
下标法(更简单,容易理解)
-
指针法:通过指针引用数组:偏移,取内容 (容易出错)
在正确访问时:指针法访问数组效率高于下标法,
9.指针偏移的补充,重新赋值数组首地址
指针偏移的要点,记得重新赋数组首地址
#include <stdio.h>
int main()
{
int arr[3] = {3,4,5};
int *p;
p = arr;
int i;
for(i = 0; i < 3; i++){
printf("%d\t",*(p+i));
//printf("%d\t",p[i]); //p存放的是arr的地址,用地址去遍历
}
p = arr; //重新赋数组首地址,不然会造成数组的越界
for(i = 0; i < 3; i++){
printf("%d\t",*(p+i));
}
return 0;
}
10.指针和数组名的一些写法!!!
#include <stdio.h>
int main()
{
int arr[3] = {3,4,5};
char arr2[3] = {'b','g','m'};
int *p;
char *p2;
p = arr;
int i;
printf("%d\n",*arr);
printf("%d\n",*p);
for(i = 0; i < 3; i++){
//printf("%d\t",*(p+i));
printf("%d\t",*p++); //p是指针变量,存放是的是arr[3]数组的首地址
}
putchar('\n');
p = arr; //重新定义到首地址
// 区别
for(i = 0; i < 3; i++){
printf("%d\t",*p++); //指针变量,保存地址的变量,保存的地址可变
}
putchar('\n');
for(i = 0; i < 3; i++){
printf("%d\t",*(arr+i));
//错误写法 printf("%d\t",*arr++); //arr是常量指针,不能++,arr表示该数组首地址固定位置
}
putchar('\n');
printf("sizeof arr is %d\n",sizeof(arr)); //int类型 有三个元素 4*3 = 12
printf("sizeof arr is %d\n",sizeof(p)); //操作系统用8个字节表示一个地址
printf("int :sizeof arr is %d\n",sizeof(int ));
printf("int *:sizeof arr is %d\n",sizeof(int *));
printf("char :sizeof arr is %d\n",sizeof(char ));
printf("char *:sizeof arr is %d\n",sizeof(char *));
printf("sizeof arr is %d\n",sizeof(arr2));
printf("sizeof arr is %d\n",sizeof(p2));
return 0;
}
运行结果:
由此可见:在win10操作系统下,只要是指针就是8个字节
11.练习函数指针数组结合
函数指针数组
#include <stdio.h>
//name , params, returnValue
void initArray(int *parr, int size)
{
int i;
for(i=0;i<size;i++){
printf("请输入第%i个元素的数据:\n",i+1);//%i 可以匹配八进制、十进制、十六进制表示的整数。
//如若输入的数字有前缀 0,%i将会把它当作八进制数来处理,
//当然如若是前缀0x ,它将以十六进制来处理。
scanf("%d",parr++);
}
}
void printArray(int *parr, int size)
{
int i;
for(i=0;i<size;i++){
printf("%d ",*parr++);
}
}
int main()
{
int arry[5];
int size = sizeof(arry)/sizeof(arry[0]);
initArray(arry,size);//实际参数,数组的首地址: 名,首个元素的地址
printArray(&arry[0],size);
return 0;
}
运行结果:
12.练习数组翻转
数组翻转
#include <stdio.h>
void printfArr(int *parr,int size)
{
int i;
for(i = 0; i < size; i++){
printf("%d\t",*parr++);
}
}
void mirrorArr(int *parr,int size) //翻转数组(镜像)
{
int i;
int j;
int temp;
for(i = 0; i < size/2;i++){
j = size-1-i;
temp = *(parr+i);
*(parr+i) = *(parr+j);
*(parr+j) = temp;
}
for(i = 0 ; i < size ; i++){
printf("%d\t",*parr++);
}
}
int main()
{
int arr[] = {1,2,3,4,5};
int size = sizeof(arr)/sizeof(arr[0]);
printfArr(arr,size);
putchar('\n');
mirrorArr(arr,size);
return 0;
}
运行结果:
13.二维数组的地址认知一
数组名=地址
父子数组
行:父数组:a
列:子数组:a[0] a[1] a[2]
a[0] :是元素 1 3 5 7 的数组名
a[1] :是元素 9 11 13 15的数组名
a[2] :是元素17 19 21 23 的数组名
printf("%d\n",a+1);// a+1 :9
printf("%d\n",a[0]+1);// a+1 :3
二维数组本质还是数组,不通电是数组元素还是个数组(子数组)
14.二维数组的地址认知二
&a[0][0]= a[0];//a[0] 是数组名 a[0][0]首元素的地址
&a[1][0]= a[1];//a[1] 是数组名 a[1][0]首元素的地址
&a[2][0]= a[2];//a[2] 是数组名 a[2][0]首元素的地址
*a = *(a+0)
a[0] = *(a+0) = &a[0][0]
a[0]+1 =*(a+0)+1 = &a[0][1]
15.编程验证二位数组的地址
arr是二维数组,对二维数组取内容,每一子内容元素都是数组,计算机对数组操作不是操作找那个个数组,而是操作数组首地址
#include <stdio.h>
int main()
{
int arr[3][4] ={
{25,26,44,84},
{94,64,1,64},
{64,14,9,4}
};
int i;
int j;
for(i = 0;i < 3;i++)
{
for(j = 0;j < 4;j++){
printf("arr[%d][%d]的地址:%p\t值:%d\t",i+1,j+1,&arr[i][j],arr[i][j]);
}
puts("");
}
puts("");
printf("arr是父亲地址: %p\t偏移1后是%p\n",arr,arr+1);
printf("arr[0]是子地址: %p\t偏移1后是%p\n",arr[0],arr[0]+1);
printf("arr[0][0]是子数组a[0]中的首地址:%p\t偏移1后是%p\n",&arr[0][0],&arr[0][1]);
printf("*(arr+0)是子数组(a[0])地址: %p\t偏移1后是%p\n",*(arr+0),*(arr+0)+1);
printf("(arr+0)是子数组(a[0])地址: %p\t偏移1后是%p\n",(arr+0),(arr+0)+1);
printf("*(arr+1)是子数组(a[1])地址: %p\t偏移1后是%p\n",*(arr+1),*(arr+1)+1);
printf("(arr+1)是子数组(a[1])地址: %p\t偏移1后是%p\n",(arr+1),(arr+1)+1);
return 0;
}
运行结果:
16.二维数组的地址写法应用
二维数组应用:
#include <stdio.h>
int main()
{
int arr[3][4] ={
{25,26,44,84},
{94,64,1,64},
{64,14,9,4}
};
int i;
int j;
for(i = 0;i < 3;i++)
{
for(j = 0;j < 4;j++){
printf("arr[0][0]是子数组a[0]中的首地址:0x%p\t值是%d\n",&arr[i][j],arr[i][j]);
printf("arr[0][0]是子数组a[0]中的首地址:0x%p\t值是%d\n",arr[i]+j,*(arr[i]+j));
printf("arr[0][0]是子数组a[0]中的首地址:0x%p\t值是%d\n",*(arr+i)+j,*(*(arr+i)+j));
}
puts("================================================================================");
}
return 0;
}
运行结果:
17.数组指针
数组指针:定义一个指针,指向数组
#include <stdio.h>
//arr,arr[0]
int main()
{
int arr[3][4] = {{11,22,33,44},{12,13,15,16},{22,66,77,88}};//arr+
int i,j;
int *p;//p++
// p = &arr[0][0];
//p = arr;
//能不能定义一个指针,让指针偏移的时候,也偏移对应大小的数组?
//数组指针,定义一个指针,指向一个数组!
//数组指针才是真正等同于二维数组名
int (*p2)[4];
p2 = arr;
// printf("p2=%p\n",p2);
// printf("++p2=%p\n",++p2);
for(i=0;i<3;i++){
for(j=0;j<4;j++){
printf("%d\n",*(*(p2+i)+j));
}
}
return 0;
}
运行结果:
18.数组指针和二维数组的配合应用
实现查找二维数组的数据:
#include <stdio.h>
int getTheData(int (*p)[4],int hang,int lie) //获取行列数据
{
int data;
data = *(*(p+hang)+lie);
return data;
//return p[hang][lie];
}
void tipsInputHangLie(int *pm, int *pn) //输入行列
{
printf("输入行列值:\n");
scanf("%d%d",pm,pn);
puts("done!");
}
//arr,arr[0]
int main()
{
int arr[3][4] = {{11,22,33,44},{12,13,15,16},{22,66,77,88}};//arr+
int ihang,ilie;
int data;
//1. 提示用户输入行列值
tipsInputHangLie(&ihang,&ilie);
//2. 找出对应行列值的那个数
data = getTheData(arr,ihang,ilie);
//3. 打印出来
printf("%d行%d列的值是%d\n",ihang,ilie,data);
}
运行结果:
19.函数指针的认知
函数指针:函数地址
函数名就是地址
如果在程序中定义了一个函数,在编译时,编译系统为函数代码分配一段存储空间,这段存储空间的起始地址(又称入口地址)称为这个函数的指针。
定义函数指针:
int sendData(int a;int b);
int (*p)(int a,int b);//函数指针
函数调用概念和变量一样
- 直接访问:变量名(函数名)
- 间接访问:指针(函数指针)
函数指针:
#include <stdio.h>
int addData(int data)
{
return ++data;
}
void printfWelcome()
{
printf("Welcome\n");
}
int main()
{
void (*p)(); //定义函数指针变量
int (*p2)(int data);
printfWelcome();
p = printfWelcome; //指向函数
p2 = addData;
(*p)(); //调用
printf("%d\n",(*p2)(10));
return 0;
return 0;
}
运行结果:
根据程序运行过程的不同情况,调用不同的函数。
20.函数指针–回调函数
函数指针–回调函数
#include <stdio.h>
#include <stdlib.h>
int addData(int data1, int data2)
{
return data1+data2;
}
int subData(int data1, int data2)
{
return data1-data2;
}
int retData(int data1, int data2,int (*p)(int ,int)) // 类型很重要, 名是访问方式
{
int ret;
ret = (*p)(data1,data2);
return ret;
}
int main()
{
int ret;
int cmd = 0;
int data1 ;
int data2 ;
int (*p)(int data1,int data2);
printf("请输入第一个数\n");
scanf("%d",&data1);
printf("请输入第二个数\n");
scanf("%d",&data2);
printf("请选择指令:1,加法;2,减法\n");
scanf("%d",&cmd);
switch(cmd){
case 1: p = addData;
printf("add\n");
break;
case 2: p = subData;
printf("add\n");
break;
default: printf("请输入正确指令\n");
exit(-1);
break;
}
ret = retData(data1,data2,p);
printf("%d\n",ret);
return 0;
}
运行结果:
21指针数组的概念和实现
指针数组定义:
int *p[4]; //指针数组,,元素均为指针类型数据。每一个元素都是存放一个地址,相当于指针变量。
int (*p)[4];//数组指针
指针数组:
#include <stdio.h>
int main()
{
int a = 4;
int b = 5;
int c = 6;
int d = 7;
int i;
int *p[4] = {&a,&b,&c,&d};
for(i = 0; i < 4;i++){
printf("%d\t",*(p[i])); //[] 比 * 优先级高,所以p先与[4]结合 ,建议还是写上小括号方便阅读
}
return 0;
}
运行结果:
函数指针数组
#include <stdio.h>
#include <stdlib.h>
int addData(int data1, int data2)
{
return data1+data2;
}
int subData(int data1, int data2)
{
return data1-data2;
}
int retData(int data1, int data2,int (*p)(int ,int))
{
int ret;
ret = (*p)(data1,data2);
return ret;
}
int main()
{
int cmd = 0;
int data1 ;
int data2 ;
int i;
int ret;
int (*p)(int data1,int data2); //函数指针
printf("请输入第一个数\n");
scanf("%d",&data1);
printf("请输入第二个数\n");
scanf("%d",&data2);
int (*p3[2])(int ,int) = {addData,subData}; //int (*p3[2])(int ,int) 函数指针数组 ,存放同类型(同形参)的函数地址(函数名)
for(i = 0; i < 2;i++){
ret = (*p3[i])(data1,data2);
printf("ret = %d\n",ret);
}
return 0;
}
运行结果:
22.指针函数的的概念和实现
概念:
一个函数可以返回一个整型值、字符值、实型值(小数)等,也可以返回指针型的数据,即地址。其概念与以前类似,只是返回的值的类型是指针类型而已。
例如“inta(intx,inty);”,a是函数名,调用它以后能得到一个int型(指向整型数据)的指针,即整型数据的地址。x和y是函数a的形参,为整型。
请注意在a两侧没有括号,在a的两侧分别为运算符和()运算符。而()优先级高于,因此a先与()结合,显然这是函数形式。这个函数前面有一个*,表示此函数是指针型函数(函数值是指针)。最前面的int表示返回的指针指向整型变量。
int *p;//定义一个指针变量
int* p;//定义一个int类型指针类型变量
函数指针:返回指针的函数
指针在意的起始地址 和 偏移地址
指针函数
#include <stdio.h>
#include <stdlib.h>
int *pfuns(int number,int (*p)[4])
{
int *p2;
p2 = (int *)(p+number);
printf("(int *)p地址:%p\n",(int *)p);
printf("(int *)(p+number)地址:%p\n",(int *)(p+number));
printf("(int *)p+number地址:%p\n",(int *)p+number);
return p2;
}
int main()
{
int arr[3][4]={
{45,86,94,78},
{78,91,84,21},
{54,56,98,62}
};
int i;
int (*p)[4];
int number;
int ret;
int *studentNumber;
p = arr;
puts("请输入想看第几个学生的成绩");
scanf("%d",&number);
studentNumber = pfuns(number,p); //二维数组地址传回来后,地址给了int *studentNumber,此时应看为子数组
for(i = 0; i < 4; i++){
printf("%d\t",*studentNumber++);//*studentNumber++,是访问子数组的元素数据
}
return 0;
}
运行结果:
23.二级指针的认知
二级指针
#include <stdio.h>
int main()
{
int data = 10;
int *p = &data;
printf("data的值:%d\n",data);
printf("data的地址:%p\n",&data);
printf("p自己的地址:%p\n",&p);
printf("p保存的是data的地址:%p\n",p);
printf("*p:取p保存地址的值:%d\n",*p);
printf("===========================================\n");
int **p2 = &p; //二级指针
printf("p2自己的地址:%p\n",&p2);
printf("p2保存的是p的地址:%p\n",p2);
printf("*p2访问p的内容:%p\n",*p2); //*p2 访问p的内容(也就是p保存data的地址)
printf("**p2:取p保存的内容的值:%d\n",**p2); //*p2:取p2地址保存的地址的值 p2 = &p;也就是取p保存的地址的内容(data的数据)
printf("===========================================\n");
int ***p3;
p3 = &p2;
printf("p3保存的是p2的地址:%p\n",p3);
printf("*p3:访问p2的内容(也就是p的地址):%p\n",*p3); //p2的地址
printf("**p3:访问p2内容的值:%p\n",**p3); //也就是data的地址
printf("***p3:访问p2内容的值的值:%d\n",***p3); //也就是data的数据
return 0;
}
运行结果:
24.二级指针的应用
应用:
#include <stdio.h>
#include <stdlib.h>
void pfuns(int number,int (*p)[4],int **studentNumber) // 类似int *p2;int **p = &p2; 操作p2保存的地址
{
*studentNumber = (int *)(p + number);
}
int main()
{
int arr[3][4]={
{45,86,94,78},
{78,91,84,21},
{54,56,98,62}
};
int i;
int (*p)[4];
int number;
int ret;
int *studentNumber;
p = arr;
puts("请输入想看第几个学生的成绩");
scanf("%d",&number);
pfuns(number,p,&studentNumber); //二维数组地址传回来后,地址给了int *studentNumber,此时应看为子数组
for(i = 0; i < 4; i++){
printf("%d\t",*studentNumber++);//*studentNumber++,是访问子数组的元素数据
}
return 0;
}
运行结果:
25.二级指针和二维数组的闭坑指南
以下工作中不会使用:
#include <stdio.h>
#include <stdlib.h>
#define SQR(X) X * X
int main()
{
int arr[3][4]={
{15,45,54,58},
{15,124,51,45},
{48,1940,6,656,}
};//int (*p)[4]
int (*p2)[4] = arr;
int **p ;
p = &p2;
**p = 666;
printf("%d\n",arr[0][0]);
/*
p = arr;
printf("arr:%p\n",arr);
printf("p:%p\n",p);
printf("*p:%p\n",*p);//*p是野指针,不是哦我们认为的会变成列的地址
*/
return 0;
}
运行结果:
警告是因为类型不匹配!
26.指针总结
各种指针的定义:
1.一个整形数:int a;
2.一个指向整形数的指针:int *a;
3.一个指向指针的指针,它指向的指针指向一个整形数:int **a;
4.一个有10个整形数的数组:int a[10];
5.一个有10个指针的数组,每个指针指向一个整形数:int *a[10];
6.一个指向有10个整形数的数组的指针:int (*a)[10];
7.一个指向指针的指针,被指向的指针指向一个有10个整形数的数组:int (**a)[10];
8.一个指向数组的指针,改数组有10个整形指针:int *(*a)[10];
9.一个指向函数的指针,该函数有一个整形参数并返回一个整数:int (*a)(int);
10.一个有10个指针的数组,每个指针指向一个函数,该函数有一个整形参数并返回一个整形数:int (*a[10](int));
11.一个函数的指针,指向的函数的类型是有两个整形参数并且返回一个函数指针的函数,
返回的函数指针指向又一个整形参数且返回整形数的函数:int(*(*a)(int,int))(int)