天天看点

C语言学习笔记——指针(一)

一、指针的作用

指针的作用:用来存放内存地址。

二、计算机寻址范围 以及 存储单元的一些知识

关于存储单元 详细可以查看百度百科:

存储单元

关于寻址范围 可以查看:

不要再被误导了,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));	
}
           

输出结果,如图所示:

C语言学习笔记——指针(一)
  • 例2:
  • 指针存放地址的过程,示意图,如下图所示:
C语言学习笔记——指针(一)

四、不同类型的指针的区别

  • 不同类型的指针的区别:
  • 虽然不同类型指针在内存中所占的大小都一样,但是不同类型的指针,偏移量的计算不同
  • 例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);
}
           

输出情况,如图所示:

C语言学习笔记——指针(一)
  • 可以发现:
  • 不同类型的指针,地址的偏移量的是不同的
  • 如:
  • 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

六 & 取地址符 与 *引用符

  • 对于指针的& 取地址符以及 * 引用符,可以用以下的理解方式
C语言学习笔记——指针(一)

七、二级指针与二维数组

二级指针的作用:

用来保存指针的地址

指针与数组是一对好朋友

从某种方面上讲,指针就是数组,数组就是指针

可以看下面示例

  • 例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);
}
           

输出如图所示:

C语言学习笔记——指针(一)

可以发现 二级指针中的数据 是一级指针的地址。

  • 例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);
}
           

输出结果,如图所示:

C语言学习笔记——指针(一)
  • 例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++;
	}
}
           

输出结果,如图所示:

C语言学习笔记——指针(一)

内存示意图:

C语言学习笔记——指针(一)