指针的进阶
1. 字符指针
2. 数组指针
3. 指针数组
4. 数组传参和指针传参
5. 函数指针
6. 函数指针数组
7. 指向函数指针数组的指针
8. 回调函数
9. 指针和数组面试题的解析
字符指针
在指针的类型中我们知道有一种指针类型为字符指针 char* 。
int main()
{
char* pstr = "hello bit.";//这里是把一个字符串放到pstr指针变量里了吗?
printf("%s\n", pstr);
return 0;
}
在字符指针pstr中存储的不是字符串“hello bit.”,存放的是首字符h的地址,在进行输出时,%s是以字符串的形式进行输出,因此打印hello bit.。
int main()
{
char str1[] = "hello bit.";
char str2[] = "hello bit.";
char* str3 = "hello bit.";
char* str4 = "hello bit.";
if (str1 == str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");
if (str3 == str4)
printf("str3 and str4 are same\n");
else
printf("str3 and str4 are not same\n");
printf("%p\n", str1);
printf("%p\n", str2);
printf("%p\n", str3);
printf("%p\n", str4);
return 0;
}
str1和str2是两个字符数组,在进行存储时候,是在内存中分别开辟一个空间,故两个地址不相同,str3和str4作为两个字符指针变量,指向的是同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当几个指针指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。
数组指针
能够指向数组的指针,它是指针。
int* p1[10];
int (*p2)[10]=&arr;//存放数组的地址为数组指针
对于优先级来说:()>[]>*,[]优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。指针数组是多个指针变量(下图可以看出),以数组形式存在内存当中,占有多个指针的存储空间。
p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。数组指针只是一个指针变量(下图可以看出),似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间,存放数组的地址。
指针数组和数组指针的内存布局
指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身的大小决定,每一个元素都是一个指针,在32 位系统下任何类型的指针永远是占4 个字节。它是“储存指针的数组”的简称。
数组指针:首先它是一个指针,它指向一个数组。在32 位系统下任何类型的指针永远是占4 个字节,至于它指向的数组占多少字节,不知道,具体要看数组大小。它是“指向数组的指针”的简称。
&数组名VS数组名
数组名是首元素的地址,&arr 表示的是整个数组的地址,而不是数组首元素的地址。(细细体会一下)
数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40。
数组参数、指针参数
一维数组传参
二维数组传参
一级指针传参
二级指针传参
函数指针
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
return 0;
}
可以看出对&函数名和函数名取地址,结果其地址是一样的,这一点不同于数组指针。
&函数名VS函数名
函数名和&函数名都是函数的地址,那么函数的地址怎么存放呢?可以从数组指针中看到,int (*p2)[10]=&arr,根据优先级,(*pa)说明是一个指针变量,还是指向数组[10]的指针,int(*)[10]为数组指针类型,同理可以理解,在下面的代码中,
int add(int x,int y)
{
int z = 0;
z = x +y;
return z;
}
int main()
{
int a = 10;
int b = 20;
//*int arr[] = {0};
//int (*pa)[] = &arr; 数组地址的存储
printf("%d\n",add(a,b));
int (*pa)(int, int) = add;
//int (*pa)(int ,int)=add; 函数名是函数的地址,存储函数地址-函数指针
//首先(*pa)保证其为一个指针变量,还是指向函数(int,int)的指针,int(*)(int,int)为函数指针类型
printf("%d\n",(*pa)(3,5));//证明了pa确实存储了函数的地址
return 0;
}
函数指针数组
顾名思义,函数指针数组是一个数组,一个可以存放多个函数地址的数组。
int (*pa)(int,int)=add//函数指针
int (*parr[4])(int,int)={add,sub,mui,div}//int (*)(int,int)为数组元素类型,整体为函数指针数组,存放四个函数地址的数组
指向函数指针数组的指针
指向函数指针数组的指针是一个指针,指针指向一个数组,数组的元素都是函数指针 。
void test(const char* str)
{
printf("%s\n", str);
}
int main()
{
//函数指针pfun
void (*pfun)(const char*) = test;
//函数指针的数组pfunArr
void (*pfunArr[5])(const char* str);
pfunArr[0] = test;
//指向函数指针数组pfunArr的指针ppfunArr
void (*(*ppfunArr)[10])(const char*) = &pfunArr;
return 0;
}
一维数组的笔试题解析
字符数组的笔试题解析
二维数组的笔试题解析
指针笔试题解析
1、
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);
printf( "%d,%d", *(a + 1), *(ptr - 1));
return 0;
}
&a为整个数组的地址,+1后跳过整个数组=ptr的地址位置,(int*)只是改变类型,并不改变值的大小。
2、
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//结构体的大小是20个字节
int main()
{
p=(struct Test*)0x100000;
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}
HEX=16进制 DEC=十进制 BIN=二进制 指针+-整数取决于指针类型
(1)首先p的类型为struct Test*,0x100000为int类型,所以用struct Test*将p类型转换成结构体地址。
p里面存储的是0x100000,0x1还是1,p+1相当于跳过一个结构体,而一个结构体是20个字节空间大小。
0x100000+0x100014(20转换成16进制),按照%p打印,所以结果为0x00100014
(2)首先类型转换,0x100000转换成10进制为1048576,+1后变成1048577,变成16进制为0x00100001
(3)p类型转换成无符号整型,一个无符号整型大小为4个字节,0x00100004
3、
int main()
{
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;
}
4、
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int *p;
p = a[0];
printf( "%d", p[0]);
return 0;
}