一、指针的作用
指针的作用:用来存放内存地址。
二、计算机寻址范围 以及 存储单元的一些知识
关于存储单元 详细可以查看百度百科:
存储单元
关于寻址范围 可以查看:
不要再被误导了,64位X86 CPU是没有64位寻址能力的!
CPU寻址范围(寻址空间)一系列问题
转 32位系统只能寻址4G空间,64位则是128G
为何微软不在新的操作系统中让 32 位支持大于 4GB 的内存?
64位CPU可以访问多大内存
下面是粗略介绍:
- 存储单元:
- 以8位二进制作为一个存储单元,即一个存储单元位一个字节
- 程序中的变量和主存储器的存储单元相对应:
- 变量的名字对应着存储单元的地址(存储地址一般用是十六进制表示)
- 变量的内容对应着存储单元中的数据(存储地址中的数据一般用二进制存放)
- 存储大小的单位换算:
- 存储单位:从小到大
- Bit , B , KB , MB , GB , TB , PB , EB , ZB ,YB
- 除了 8Bit(位)= 1 Byte (字节) 外
- 相邻的两个单位的换算的比例都为 1024:1 , 即1024KB = 1MB,1024MB = 1GB
- 寻址范围 :
- 可以暂时如此理解,因为CPU的寻址能力未必与CPU的位宽挂钩
- 在32位系统中里面一共有 2^32 个地址,即从0x0000 0000 到 0xffff ffff , 对应4G的字节
- 每个地址 对应 一个存储单元(1个字节)
- 在32位系统中,地址用32位(二进制)表示,即一个地址需要的内存空间为4个字节
- 在64位系统中,地址用64位(二进制)表示,即一个地址所需要的内存空间为8个字节
三、指针的定义
- 指针的定义:
- 类型说明 *变量名;
- 例如:char *p;
- 注:在32位机中,不管什么类型的指针都是4个字节大小
- 例1:
- 不同类型指针所需要的内存空间大小 是 相同的!
- 在32位机中,一个地址所需要的内存大小为4个字节
- 在64位机中,一个地址所需要的内存大小为8个字节
- 代码如下:
#include<stdio.h>
void main(int argc, char const *argv[])
{
//定义不同类型的指针
char *a;
short *b;
int *c;
long *d;
float *e;
double *f;
long long *g;
long double *h;
//打印指针的占的内存大小
printf("sizeof(char *a) = %d\n", sizeof(a));
printf("sizeof(short *b) = %d\n", sizeof(b));
printf("sizeof(int *c) = %d\n", sizeof(c));
printf("sizeof(long *d) = %d\n", sizeof(d));
printf("sizeof(float *e) = %d\n", sizeof(e));
printf("sizeof(double *f) = %d\n", sizeof(f));
printf("sizeof(double *g) = %d\n", sizeof(g));
printf("sizeof(long double *h) = %d\n", sizeof(h));
}
输出结果,如图所示:
- 例2:
- 指针存放地址的过程,示意图,如下图所示:
四、不同类型的指针的区别
- 不同类型的指针的区别:
- 虽然不同类型指针在内存中所占的大小都一样,但是不同类型的指针,偏移量的计算不同
- 例1:
- 不同类型的指针虽然所占内存大小一样,但是偏移量的计算不同
- 测试代码如下:
#include<stdio.h>
void main(int argc, char const *argv[])
{
//定义不同类型的指针
int x = 0;
char *a = &x;
short *b = &x;
int *c = &x;
long *d = &x;
float *e = &x;
double *f = &x;
long long *g = &x;
long double *h = &x;
//不同偏移量
printf("char *a = %p , a+1 = %p \n",a,a+1);
printf("short*b = %p , b+1 = %p \n",b,b+1);
printf("char *c = %p , c+1 = %p \n",c,c+1);
printf("char *d = %p , d+1 = %p \n",d,d+1);
printf("char *e = %p , e+1 = %p \n",e,e+1);
printf("char *f = %p , f+1 = %p \n",f,f+1);
printf("char *g = %p , g+1 = %p \n",g,g+1);
printf("char *h = %p , h+1 = %p \n",h,h+1);
}
输出情况,如图所示:
- 可以发现:
- 不同类型的指针,地址的偏移量的是不同的
- 如:
- char 型指针+1,偏移了1个地址空间
- short 型指针+1,偏移了2个地址空间
- int 型指针+1,偏移了4个地址空间
- 总结:
- 指针偏移量的计算: 原地址 + (类型字节大小 * 所加的数量)
- 如:
- char *a ; a指向的地址为0xbf80f03c a + 2 ----> 0xbf80f03c + 1 * 2 -----> 0xbf80f03e (char 型为1个字节)
- int *a ; a指向的地址为0xbf80f03c a + 2 -----> 0xbf80f03c + 4 * 2 -----> 0xbf80f044 (int 型为4个字节)
- 由于不同类型的指针偏移量的是不同的
- 因此指针的类型还可以告诉编译器,取多少个地址的内容作为一个完整的数据
五、常用的指针
- 在日常开发中会使用到的指针如下:
- void *a;//万能指针,用来暂时存放地址,待用户确定类型后再强制转换成对应的类型
- char *a;//字符指针,指向一个字符
- short *a;//短整形指针,指向一个短整形
- int *a;//整形指针,指向一个整形
- char **a;//二级指针,用来保存一个 指针字符型的指针 的地址
- char *a[10];//数组,数组里面存放了10个char *的指针
- char (*a)[10];//指针,指向一个char [10]的数据
- int (*a)[10];//指针,指向一个int [10]的数据
- void *a(void *argv);//函数,该函数的返回值为void * 参数为void *
- void (*a)(void *argv);//指针, 该指针指向一个返回值为void * 参数为void *的函数
关于 指针函数 与 函数指针 可以详情可以查看:
函数指针和指针函数用法和区别
下面为粗略介绍
- 指针函数:首先是一个函数,其次这个函数返回的值是指针,即返回地址值的函数
- 声明格式:返回值类型 *函数名字(参数表)
- 函数指针:首先是一个指针,该指针指向某个函数
- 声明格式:返回值类型 (*函数名字)(参数表)
如何判断是 指针,指针数组,数组指针,指针函数,函数指针的一个小技巧:
- 使用运算符的优先级,来判定!!!
- 如:
- 首先,指针数组 与 数组指针
- int *a[10];
- 这是一个指针数组,首先[ ]数组下标的优先级最高,所以先判断这是一个数组
- 接着再判断这是一个存放了10个 int* 的指针 的数组
- int (*a)[10];
- 只是一个数组指针,首先因为( )和[ ]是同级优先级 ,所以看它的结合方向(从左往右)
- 所以判断,这个首先是一个指针,接着判断该指针指向一个 int [10]的数据
- 其次,指针函数 与 函数指针
- int *a(int *argv);
- 同理,通过运算符的优先级,可以判断这是一个函数,该函数的参数为int *argv
- 其次,该函数的返回值为int * (即返回一个地址)
- int (*a)(int argv);
- 同理,通过运算符的优先级,可以判断这首先是一个指针,该指针指向一个参数为 int argv
- 其次,该函数的返回值为int
六 & 取地址符 与 *引用符
- 对于指针的& 取地址符以及 * 引用符,可以用以下的理解方式
七、二级指针与二维数组
二级指针的作用:
用来保存指针的地址
指针与数组是一对好朋友
从某种方面上讲,指针就是数组,数组就是指针
可以看下面示例
- 例1:
- 二级指针的作用-->保存指针的地址
- 分别打印 一级指针,一级二级指针的地址,并且对一级和二级指针使用 引用符
- 测试代码如下:
#include<stdio.h>
void main(int argc, char const *argv[])
{
//定义
//一级指针存储变量的地址
//二级指针存储一级指针的地址
int a=10;
int *p = &a;
int **p1 = &p;
printf(" a = %d , &a = %p\n",a,&a);
printf(" *p = %d , p = %p , &p = %p\n",*p,p,&p);
printf("**p1 = %d , *p1 = %p , p1 = %p\n",**p1,*p1,p1);
}
输出如图所示:
可以发现 二级指针中的数据 是一级指针的地址。
- 例2:
- 从某种方面上讲,数组就是指针,指针就是数组
#include<stdio.h>
void main(int argc, char const *argv[])
{
//定义一个二维指针 与 二维数组
int a[3][3] = {1,2,3,4,5,6,7,8,9};
int (*p)[3] = a;//指向二维数组的首地址
//当p正确指向 a 的首地址的时候:
//此时p 就相当于 a 了!!!
printf("a = %p , a[1] = %p , a[1][0] = %d\n",a,a[1],a[1][0]);
printf("p = %p , p[1] = %p , p[1][0] = %d\n",p,p[1],p[1][0]);
printf("a+1 = %p\n",a+1);
printf("p+1 = %p\n",p+1);
}
输出结果,如图所示:
- 例3:
- 用一个一级指针,将二维数组中的值取出来
- 该示例,请从内存的角度来理解
#include<stdio.h>
void main(int argc, char const *argv[])
{
//定义一个二维指针 与 二维数组
int a[3][3] = {1,2,3,4,5,6,7,8,9};
int *p = a;
int i;
for(i=0;i<9;i++)
{
printf("*p = %d\n",*p);
p++;
}
}
输出结果,如图所示:
内存示意图: