C语言指针

一、指针的概念

1. 分清指针和指针变量

在计算机中,系统会给虚拟内存中的每一个存储单元编号,每一个地址编号对应着一个字节的存储单元,这个地址编号就是指针。
指针变量是存放某个地址编号的一个变量。在32位平台下,地址总线是32位的,所有地址编号都是32位的。指针变量不管存放什么类型的数据,这一类型的数据可能占1个字节或多个字节,但指针变量存放的都是这个数据对应的地址的第一个地址编号(如下图2),所以任何类型的指针变量都只占4个字节。

2. 普通变量在内存中的存储

那么普通的变量(非指针变量)在内存中占几个字节呢 ?

	// 定义一个字符型变量 ch
	char ch = 'a';

	// 定义两个整型变量 num1,num2
	int num1 = 305419896;
	int num2 = 4294967295;

定义一个字符型变量ch并赋初值值’a’,由于字符型数据在内存中储存的是它的ASCII码值,'a’的ASCII码值为97,十六进制表示为0x61,二进制表示为0110 0001,占1个字节;
再定义一个整型变量num1的值为305419896,十六进制表示为0x12345678,二进制表示为0001 0010 0011 0100 0101 0110 0111 1000,一共32位,占4个字节;
另一个整型变量num2的值为4294967295,十六进制表示为0xffffffff,二进制表示为1111 1111 1111 1111 1111 1111 1111 1111,一共32位,占4个字节。
普通变量在内存中的存储

图1. 普通变量在内存中的存储

十六进制带前缀输出变量ch、num1和num2的地址为:

0x2008
0x2004
0x2000

为什么整型变量明明占4个字节,输出的地址却只有1个字节的地址?那是因为在计算机中,占多个字节的变量在内存中占多个存储单元,存储单元地址编号最小的那个编号就是这个变量的地址。
综上,在32位平台下,内存地址、指针、指针变量都是4个字节的。

二、定义指针变量

1. 简单的指针变量的定义方式

数据类型 * 指针变量名;

	// 定义一个整型的指针变量 p
	int * p;

2. 操作指针的运算符

& 是取地址运算符,就是取值对应的地址,想象在一个盒子中存放着数据value,&value就是取地址,即获得这个盒子编号的操作。
*(地址) 是取值运算符,是解引用操作符,就是取地址对应的值,可以想象成通过盒子的编号打开盒子取出数据value的操作。

	int num = 0x12345678;
	int * p; // 定义指针变量时,* 表示修饰 p 是一个指针变量
	p = #
	int a;
	a = *p;
	printf("num的地址为:%#x\n",&num); // 输出 num 的地址
	printf("p存储的值为:%#x\n",p); // 输出 p 存储的值
	printf("a的值为:%#x",a); // 输出 a 的值

代码分析:定义整型变量num,假设num的地址为0x0062fe1c,给num赋初值0x12345678,指针变量p存放了num的地址,也可以说是p指向了num,p的值为0x0062fe1c。定义整型变量a,a=*p等同于a=*(&num),就相当于把num的值间接赋给了a。
十六进制带前缀输出结果为:

num的地址为:0x62fe1c
p的值为:0x62fe1c
a的值为:0x62fe1c

指针变量p存放a的首地址

图2. 指针变量p存放num的首地址

3. 分清*&p和&*p

根据C语言的运算优先级,*和&处在同一优先级,结合方式从右到左,所以*&p 等价于*(&p),&*p 等价于&(*p)。

(1) int * p; 那么*&p 等同于 p,&*p 等同于 p,最后都是 p,只是没定义p指向哪。

(2) int p; 那么*&p 等同于 p;而 &*p 是非法的,因为 *p 非法。例如,int p =10; 那么 *&p 等同于 *(&p) 等同于 p,最后 p 的值为10(即从p的地址取值),而 &*p 则是非法的,因为p=10,*10表示取内存地址为10的值,在c语言中是非法操作。

三、指针的分类

指针的类型按照指针指向的数据类型可以简单分为以下几类:

1. 字符型、整型、浮点型指针

(1) 字符指针

	char * p; // 定义一个字符指针变量,只能存放字符类型数据的地址
	char ch;
	p = &ch;

(2) 短整型指针

	short int * p; // 定义一个短整型指针变量,只能存放短整型变量的地址
	short int val;
	p = &val;

(3) 整型指针

	int * p; // 定义一个整型指针变量,只能存放整型变量的地址
	int val;
	p = &val;

(4) 长整型指针

	long * p; // 定义一个长整型指针变量,只能存放长整型变量的地址
	long val;
	p = &val;

(5) 单精度浮点型指针

	float * p; // 定义一个float型指针变量,只能存放float型变量的地址
	float val;
	p = &val;

(6) 双精度浮点型指针

	double * p; // 定义一个double 型指针变量,只能存放double 型变量的地址
	double val;
	p = &val;

2. 函数、结构体、数组指针

(1) 函数指针

	int func(int x); // 定义一个函数
	int (*p)(int x); // 定义一个函数指针变量
	p = func; // 将函数的首地址赋值给 p

(2) 结构体指针

	// 定义学生基本信息结构体
	struct student{
		char * name; // 姓名
		int sno; // 学号
		int age; // 年龄
	}
	struct student stu1; // 定义一个学生基本信息结构体变量stu1
	struct student *p = NULL; // 定义一个指向struct student类型的指针变量 p
	p = &stu1; // p 指向结构体变量stu1的首地址
	strcpy((*p).name,"张三");
	(*p).sno = 2020001;
	(*p).age= 20;

(3) 数组指针

	int a[10]; // 定义一个长度为10的整型数组
	int * p; // 定义一个整型指针变量
	p = a; // 将数组首地址赋值给 p,数组名就是数组的首地址,a是地址值,不是一个变量,p和a含义不同
	p[1] = 10; // 用指针变量 p来访问数组元素,相当于a[1] = 10
	*(p+1) = 10; // 相当于a[1] = 10

3. 多重指针和通用指针

(1) 指针的指针(多重指针)
指针的指针就是指针的地址,定义一个指针变量,指针变量也有其地址编号。

	int num = 0x12345678;
	int * p;
	p = #
	int **q; // 定义二重指针
	q = &p; //p存放了q的地址
	int ***r; // 定义三重指针
	r = &q; // r存放q的地址

(2) 通用指针
任何类型的指针都可以赋值给void类型的指针,而void类型的指针赋值给其它类型的指针需要进行转换。

	void * p; // 定义void类型指针变量
	int * q;
	p = q;
	int * r = (int *)p;

在32位系统下,任何类型的指针变量都只占4个字节,指针只能存放对应类型的变量的地址编号。

四、指针的运算

1. 指针加上一个整数

指针变量加上一个整数,结果仍是一个地址。

	int a[10];
	int * p;
	p = a;
	p = p + 1;
	printf("%#x\n",a);
	printf("%#x",p);

假设a的首地址为0x0062fe10,最终程序编译运行后输出a的值为0x62fe10,p的值为0x62fe14,p加上的1是整型数组a的一个元素的字节(4字节)。

	char b[10];
	char * q;
	q = b;
	q = q + 1;
	printf("%#x\n",b);
	printf("%#x",q);

假设b的首地址为0x0062fef0,最终程序编译运行后输出b的值为0x62fef0,q的值为0x62fef1,q加上的1是字符型数组b的一个元素的字节(1字节)。

2. 相同类型指针比较大小

相同类型的指针指向同一个数组中的元素时,比较大小才有意义。

	int a[10];
	int * p, * q;
	p = &a[1];
	q = &a[5];
	if(p > q){
		printf("p>q");
	}
	else{
		printf("p<q");
	}

输出结果为p<q,这里比较的其实是两个数组元素a[1]和a[5]的地址大小。

3. 相同类型指针相减

相同类型的指针指向同一个数组中的元素时,相减才有意义。

	int a[10];
	int * p, * q;
	p = &a[1];
	q = &a[5];
	printf("%d",q-p);

输出结果为4,q和p相减得到的是a[1]到a[5]之间的元素个数。

4. 相同类型指针相互赋值

相同类型的指针(除void类型指针)之间才可以相互赋值。

	int * p;
	int * q;
	int num;
	p = &num;
	q = p;

不同类型的指针要想相互赋值,必须先进行强制类型转换成相同类型的指针。


版权声明:本文为qq_62283591原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。