浅谈指针---指针的含义,用法,机器的字节序,指针和数组的关系
指针初阶
点击此处可以查看一些简单的题目[指针和数组的烧脑题目]
(https://blog.****.net/wang_yiduo/article/details/88530349)
-指针定义
指针是编程语言中的一个对象,利用地址,它的值直接指向存在电脑存储器中另一个地方的值,存这个这个值的地方被称作这个值的地址,这个值的地址就是指针,意思是可以通过指针来找到这个值所在的内存单元,然后进而找到这个值具体是多少
- 指针变量
指针是一个变量,用来存放内存单元的地址(编号)
代码演示:
#include <stdio.h>
int main()
{
int a = 1;//在内存中开辟一块空间
int *p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
//将a的地址存放在p变量中,p就是一个之指针变量。
printf("*p=%d\np=%p\n&a=%p",*p,p,&a);
return 0;
}
结果为:
注意:int*指的是整形指针变量,*p指的是指针变量解引用,二者中的 *是不同的,第一个代表它是指针变量,第二个代表解引用,指的是求存在指针p这个地址的值
结论:可以明显看出指针变量p和变量a的地址是相同的,所以指针变量就是存放变量的地址(存放在指针中的值都被当作地址来处理)
-指针变量的大小
首先一个字节占8个位,所以在32位的系统上,一个指针变量的大小就是4个字节(32/8=4),在64位系统上,一个指针变量的大小就是8个字节(64/8=8)
- 指针编址的空间
-
在32位的系统上,指针最多能存储232个地址,也就是4G的空间
(232=232/1024KB=232/1024/1024MB=232/1024/1024/1024GB=4GB) - 在64位的系统上,指针最多能存储264个地址,这个空间就非常大了,目前是用不完的
-指针类型
指针是一个地址,地址里面可以存各种各样的数据,所以相对的地址存的数据是什么类型,那么指针就是什么类型的指针变量
char* p1=NULL;
int* p2=NULL;
short* p3=NUL;
long* p4=NULL;
结论:
- 指针的定义方式是: type + *
- char* 类型的指针是为了存放 char 类型变量的地 址。 short* 类型的指针是为了存放 short 类型变量的地址。 int* 类型的指针是为了存放 int 类型变量的 地址。
-指针的解引用
-指针运算
已知存放数据的地址要求求出此地址所存放的数据
代码演示:
#include <stdio.h>
int main()
{
int a = 1;
int *p1 = &a;
char* p2=&a;
printf("*p1=%d\n,*p2=%d",*p1,*p2);
return 0;
}
结果为:*p1=1,*p2=1
总结: 指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。 比如: char* 的指针解引用 就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。
这时就会出现一个问题,为什么p2解引用结果是1?按理来说一个整形数字占4个字节解引用也最多解4个字节,所以p1解引用后用16进制表示为00000001,此时将一个整型变量按照字符型变量来解引用,一个字符占1个字节,所以解引用也最多只能解1个字节,那么p2解引用解的存储的数据用16进制表示方式应该是00,可是实际结果却是01,这是为什么?这是因为数据在地址中存储的时候是按小端序(机器的字节序)来存储的
-机器的字节序
假设现在有一个指针变量指向一个16进制的数字a,那么这个数字a在地址中按照字节序的存储会有两种情况
将它赋给一个字符指针变量,如果解引用的结果为a(10进制输出为10),说明它是按照小端序来排列的(0a000000),如果解引用为0,说明他是按照大端序来排列的(0000000a)
结论:
- 大端序:低位在高地址上
- 小端序:低位在低地址上
-指针±整数
指针加几就是指指针向后跳几个指针类型的长度
指针减几就是指指针向前挑几个指针类型的长度
代码演示:
//指针+整数
#include <stdio.h>
int main()
{
int*p1 = NULL;
p1=p1+1;
int* p2=NULL;
p2=p2+2;
printf("p1=%d\n,p2=%d\n",p1,p2);
return 0;
}
结果为:p1=4,p2=8
//指针-整数
#include <stdio.h>
int main()
{
double*p1 = 20;
p1=p1-1;
double* p2=20;
p2=p2-2;
printf("p1=%d\n,p2=%d\n",p1,p2);
return 0;
}
结果为:p1=12,p2=4
原因:p1-1就是前跳1个double类型的长度,所以为12(20-8=12),p1-2就是向前跳2个double类型的长度(20-16=4)
结论::指针的类型决定了指针向前或者向后走一步有多大(距离)。
-指针-指针
实质:两个指针之间隔了多少个元素
代码演示:
#include<stdio.h>
int Strlen(char* str)
{
char* p=str;
while(*p!='\0')
++p;
return p-str;
}
int main()
{
char str[]="hello,world";
printf("字符串的实际长度为%d\n",Strlen(str));
return 0;
}
结果为:字符串的实际长度为:11
原因:利用p得出当p指向\0的时候的地址,再减去str的首地址,结果为11,正好是两个指针之间隔得元素个数
-指针和数组
-
指针和数组名
我们知道数组名在做函数的参数的时候会隐式转换为指针,而这种转换不止会体现在数组名作参数的时候,当数组名加减一个整数的时候数组名也会隐式转换为指针
代码演示:#include <stdio.h> int main() { int arr[] = {1,2,3,4,5}; printf("%p\n", arr); printf("%p\n", arr+1); return 0; }
结果为:
可以看出此时arr和arr+1都被因式转换为了指针,arr指向数组元素1,arr+1指向数组元素2
当数组名做指针的时候会隐式转换成为指针,那么arr,&arr[0]以及&arr有什么区别呢?
代码演示:
#include <stdio.h>
int main()
{
int arr[] = {1,2,3,4,5};
printf("arr=%p\n&arr[0]=%p\n&arr=%p\n", arr,&arr[0],&arr);
return 0;
}
结果为:
可以看出arr,&arr[0],&arr它们在数值上没有任何区别,但是实际上他们真的没有区别吗?看下面的代码
代码演示:
#include <stdio.h>
int main()
{
int arr[] = {1,2,3,4,5};
printf("arr=%p\n&arr[0]=%p\n&arr=%p\n", arr,&arr[0],&arr);
printf("\n");
printf("arr+1= %p\n&arr[0]+1=%p\n&arr+1= %p\n", arr+1, &arr[0]+1, &arr+1);
return 0;
}
结果为:
此时可以看出arr+1和&arr[0]+1的地址还是相同的,但是&arr+1的地址却不在和他俩相同,比他俩要大16个字节,也就说相差了4个元素
原因:arr在隐式转换成为指针之后会指向数组的首元素,是数组首元素的地址,而&arr[0]也是数组首元素的地址所以它们俩是完全相同的,但是&arr,是数组指针,指的是整个数组的地址,指向的是整个数组的数据,只不过恰好数组的地址用数组的首元素来表示,所以&arr的数值也和它们俩相等,但仅仅是数值,实际的意义是不同的,所以在都加1之后arr+1和&arr[0]+1,都指向了数组的第二个元素2,而&arr+1由于指针+1要跳过它1个指针类型的数据,所以它要跳过整个数组,所以此时&arr+1可以说是指向了5后面的一个数据和数组元素2正好差了4个元素
-
指针和数组
数组名既然可以当做地址存在指针里面,那我们也就可以通过对指针(数组名)的解引用来访问数组的元素
代码演示:int main() { int arr[] = { 1, 2, 3, 4, 5}; int *p = arr; //指针存放数组首元素的地址 int sz = sizeof(arr) / sizeof(arr[0]); int i = 0; //值的比较 for (i = 0; i<sz; i++) { printf("*(p+%d)=%d<===>arr[%d]=%d\n", i,*(p + i),i,arr[i]); } //地址的比较 for (i = 0; i<sz; i++) { printf("p+%d=%p<===>&arr[%d]=%p\n", i,p+i,i,&arr[i]); } return 0; }
结果为:
可以看出p+i解引用的值就是arr下标为i解引用的值;p+i的地址就是arr下标为i的地址
-二级指针
既然指针变量是一个地址,用来存放变量的值,那么指针变量也是一个变量,那指针变量的地址就被称作二级指针
图示: