指针¶
指针也就是内存地址,指针变量是用来存放内存地址的变量,在同一CPU构架下,不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。有了指针以后,不仅可以对数据本身,也可以对存储数据的变量地址进行操作。
指针描述了数据在内存中的位置,标示了一个占据存储空间的实体,在这一段空间起始位置的相对距离值。在 C/C++语言中,指针一般被认为是指针变量,指针变量的内容存储的是其指向的对象的首地址,指向的对象可以是变量(指针变量也是变量),数组,函数等占据存储空间的实体。
有10个指针的数组,该指针指向一个整数:int* a[10]
指向有10个整型数组的指针:int (* a)[10]
指针与数组¶
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1); //&a取出整个数组的地址;&a+1跳过一个数组
//&a的类型为:数组指针 int(*)[5] 所以要强转
//a为数组名,首元素地址,即为1的地址,+1,跳过一个元素,即为2的地址
printf( "%d,%d", *(a + 1), *(ptr - 1)); // 2 5
return 0;
}
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); // 4 2000000
return 0;
}
int ptr1 = (int *)(&a + 1): 取出数组的地址+1,跳过一个数组,因为&a的类型为数组指针:int()[4] 类型不匹配,所以强转为int类型;
此时的a代表的首元素地址,地址值是一个常量,整数+1:相当于跳过一个字节,注意要考虑小端存放,读取时倒着读取的问题,所以ptr2指向的是00 00 00 02这四个字节,所以打印结果为:02000000
int main()
{
int a[5][5];
int(*p)[4]; //p是数组指针,指向的数组有4个元素
p = a;
printf( "%p,%d\n", &p[4][2]-&a[4][2], &p[4][2]-&a[4][2]); //指针-指针得到的是二者之间的元素个数
return 0;
}
警告
a是二维数组,对应数组指针的类型为:int(*)[5],指向的是有5个元素的一维数组,而p是数组指针,指向的数组只有4个元素,所以会有警告,可以写成 int(p)[4] = (int()[4])a 消除警告
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) }; //逗号表达式-结果为最后一个表达式的结果,所以只是初始化了{ 1, 3, 5 }
int *p;
p = a[0]; //a[0] : 二维数组第一行的数组名,在这里是首元素地址,即第一行第一个元素的地址
printf("%d ",p[0]); //1
return 0;
}
int main()
{
int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int *ptr1 = (int *)(&aa + 1);
int *ptr2 = (int *)(*(aa + 1));
printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1)); // 5 10
return 0;
}
&aa:取出二维数组的地址;&aa+1:跳过二维数组,&二维数组应该使用数组指针接收,现在保存到整形指针,所以要强转。
aa:没有单独放在sizeof内部,没有&数组名,所以代表的是二维数组首元素地址,即二维数组第一行的地址;aa+1:跳过一行
*(aa+1) : 相当于拿到了第二行的数组名,等价于 aa[1]
指针与结构体¶
提示
指针+1 的步长取决于指针指向的数据的类型,整数+1 ->跳过一个字节,执行普通的加减运算,而整形指针+1 -> 跳过四个字节
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p; //这里告知结构体的大小是20个字节,假设p的值为0x100000。
int main()
{
p = 0x00100000;
//0x1-->对应的值就是1 相当于0x00000001
printf("%p\n", p + 0x1);//p为结构体指针,指向一个结构体,+1,跳过一个结构体,即跳过20个字节,
// 0x00100000+20 -> 0x00100020 错误, 要将20转化为16进制再加,或者将16进制0x00100000转化为10进制之后加上20,然后再转化为16进制
// 20-> 0X00000014
//所以最终结果为:0x00100014
printf("%p\n", (unsigned long)p + 0x1);//将p转化为长整形,+1,即为整形+1, 例如:500+1= 501,
//所以结果为: 0x00100001
printf("%p\n", (unsigned int*)p + 0x1);
//将p强转为无符号整形,+1跳过一个整形->跳过4个字节
//所以结果为:0x00100004
return 0;
}
指针与字符¶
#include <stdio.h>
int main()
{
char *a[] = {"work","at","alibaba"}; //a是数组,元素类型为:char* ,存放指向字符串首字符地址,根据后面初始化内容确定数组的大小
char**pa = a; //char**pa :一颗*说明pa是指针,另一颗*说明pa指向的类型是char*
pa++; //pa+1:跳过char*
printf("%s\n", *pa); //打印结果为:at
return 0;
}
int main()
{
char *c[] = {"ENTER","NEW","POINT","FIRST"};
char**cp[] = {c+3,c+2,c+1,c};
char***cpp = cp;
printf("%s\n", **++cpp); //cpp先自增,此时cpp存放了指向存放c+2地址的空间(地址),打印结果为:POINT
printf("%s\n", *--*++cpp+3); //*++cpp拿到存放c+1地址的空间,再--自减c+1的地址,把存放c+1的地址变成存放c的地址,"ENTER"首字符后+3打印:ER
printf("%s\n", *cpp[-2]+3); //打印结果为:ST
printf("%s\n", cpp[-1][-1]+1);//打印结果为:EW
return 0;
}
cpp-2:从指向存放c地址空间又变为了指向存放c+3地址的空间
*(cpp-2):得到cpp现在指向的内容,即c+3的地址
**(cpp-2):得到c+3空间的内容(首字符F的地址)
**(cpp-2)+3 :从首字符F的地址向后+3,即为S的地址
cpp[-1] ==>*(cpp-1); cpp[-1][-1] ==> *(*(cpp-1)-1); cpp[-1][-1] +1 ==> *(*(cpp-1)-1) +1
此时的cpp指向为第二条表达式之后的状态,cpp存放c的地址,cpp-1指向存放c+2地址的空间,*(cpp-1)-1自减变成了c+1的地址,即得到了c+1的地址,再+1字符输出。