- 一、指针的概念
- 1. 分清指针和指针变量
- 2. 普通变量在内存中的存储
- 二、定义指针变量
- 1. 简单的指针变量的定义方式
- 2. 操作指针的运算符
- 3. 分清*&p和&*p
- 三、指针的分类
- 1. 字符型、整型、浮点型指针
- 2. 函数、结构体、数组指针
- 3. 多重指针和通用指针
- 四、指针的运算
- 1. 指针加上一个整数
- 2. 相同类型指针比较大小
- 3. 相同类型指针相减
- 4. 相同类型指针相互赋值
在计算机中,系统会给虚拟内存中的每一个存储单元编号,每一个地址编号对应着一个字节的存储单元,这个地址编号就是指针。
指针变量是存放某个地址编号的一个变量。在32位平台下,地址总线是32位的,所有地址编号都是32位的。指针变量不管存放什么类型的数据,这一类型的数据可能占1个字节或多个字节,但指针变量存放的都是这个数据对应的地址的第一个地址编号(如下图2),所以任何类型的指针变量都只占4个字节。
那么普通的变量(非指针变量)在内存中占几个字节呢 ?
// 定义一个字符型变量 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个字节。
十六进制带前缀输出变量ch、num1和num2的地址为:
0x2008 0x2004 0x2000
为什么整型变量明明占4个字节,输出的地址却只有1个字节的地址?那是因为在计算机中,占多个字节的变量在内存中占多个存储单元,存储单元地址编号最小的那个编号就是这个变量的地址。
综上,在32位平台下,内存地址、指针、指针变量都是4个字节的。
数据类型 * 指针变量名;
// 定义一个整型的指针变量 p int * p;2. 操作指针的运算符
& 是取地址运算符,就是取值对应的地址,想象在一个盒子中存放着数据value,&value就是取地址,即获得这个盒子编号的操作。
*(地址) 是取值运算符,是解引用操作符,就是取地址对应的值,可以想象成通过盒子的编号打开盒子取出数据value的操作。
int num = 0x12345678;
int * p; // 定义指针变量时,* 表示修饰 p 是一个指针变量
p = #
int a;
a = *p;
printf("num的地址为:%#xn",&num); // 输出 num 的地址
printf("p存储的值为:%#xn",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
根据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) 字符指针
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] = 103. 多重指针和通用指针
(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("%#xn",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("%#xn",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
输出结果为p
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 = #
q = p;
不同类型的指针要想相互赋值,必须先进行强制类型转换成相同类型的指针。



