• C语言学习(八)之指针



    C语言的灵魂,重中之重的环节。

    一、指针


    1.1 什么是指针

    从根本上看,指针(pointer)是一个值为内存地址的变量(或数据对象)。正如char类型的变量是字符,int类型的变量是整数,指针变量的值是地址。

    简单来说,指针是一个变量,不过存放的是变量的地址。

    指针的大小在32为平台是4个字节,在64位平台为8个字节。

    # include 
    
    int main(void){
    
    	// 声明一个变量
    	int a = 10;	
    	// 声明一个int类型的指针p,该指针存放的值为a变量在内存中的地址。
    	int* p = &a;
    	// 打印a变量的地址,也就是p的值。
    	printf("%p", p);	// 结果为:000000000061FE14
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在上面的例子中 “%p”为输出指针类型变量的控制符,详细可看格式化输入/输出

    " * "为解引用操作符。&为取地址符。可以看操作符这篇文章,详细讲解了操作符。


    1.2 指针的定义

    指针的定义在上面的例子中已经看到了。结构如下:

    数据类型 *指针变量名
    
    • 1
    // 定义一个char类型指针变量,char_p为指针变量名。等号后面为char_p的值。
    char* char_p = &a;
    // 定义一个int类型指针变量,int_p为指针变量名。等号后面为int_p的值。
    int* int_p = &b;
    // 定义一个short类型指针变量,short_p为指针变量名。等号后面为short_p的值。
    short* short_p = &c;
    // 定义一个float类型指针变量,float_p为指针变量名。等号后面为float_p的值。
    float* float_p = &d;
    // 定义一个double类型指针变量,double_p为指针变量名。等号后面为double_p的值。
    double* double_p = &e;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    1.3 指针的类型

    在1.2中,我们声明了int, char等等类型的指针变量,通过上面例子中,我们看到了指针也是有类型的,指针的类型就是:

    type *	// type指的是数据类型。例如 int,char等等
    
    // 示例
    int* p = &a;	// int*表示指针类型为int*,p为指针变量的名称。&a为指针变量的值。
    
    • 1
    • 2
    • 3
    • 4

    指针的类型决定了指针在解引用操作时,能够访问空间的大小(能操作几个字节)。比如:char * 的指针解引用就只能访问一个字节,而int *的指针解引用就能访问4个字节。

    # include 
    
    int main(void){
    
    int a = 10;
    char* ch = &a;
    int* p = &a;
    
    // 打印a的地址
    printf("%p\n", &a);	// 000000000061FE0C
    // char类型的指针只能访问一个字节,当char类型的指针+1时,
    // 只能后移一个字节,则ch+1的地址就是a的地址+1。0C+1 = 0D
    printf("%p\n", ch+1);	// 000000000061FE0D
    // int类型的指针只能访问4个字节,当int类型的指针+1时,
    // 后移4个字节,则p+1的地址就是a的地址+4。0C+4 = 10 
    printf("%p\n", p+1);	// 000000000061FE10
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    指针变量的大小在32位机器上为4个字节,64位机器上为8个字节


    二、野指针

    2.1 什么是野指针

    野指针就是指针指向的地址不可知(不确定的,随机的)


    2.2 为什么会产生野指针

    1、指针未初始化。
    2、指针数组越界。
    3、指针指向的空间释放
    
    • 1
    • 2
    • 3
    # include 
    
    int main(void){
    
    	int *p;	// 指针未初始化,随机值
    	*p = 10;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    # include 
    
    int main(void){
    
    	int arr[5] = {0, 1, 2, 3, 4};
    
    	int *p = arr;
    	for (int i = 0; i < 6; i++)
    	{
    		// 当超出数组时,指针就是野指针
    		*(p++) = i;
    	}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    2.3 如何规避野指针

    1、指针记得初始化。
    2、不要使指针数组越界。
    3、指针指向空间释放后置指针为NULL。
    4、避免返回局部变量的地址。
    5、指针使用前检查有效性。
    
    • 1
    • 2
    • 3
    • 4
    • 5

    三、指针运算

    3.1 赋值

    赋值就是可以把地址赋值给指针。但注意,地址的类型要与指针的类型相同,例如:不能把char类型的地址赋值给int类型的指针。

    # include 
    
    int main(void){
    
    	// 定义一个char类型的变量
    	char a = 10;
    	// 定义一个char类型的指针并将char类型变量的地址赋值给char类型的指针
    	char *p = &a;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    // 将char类型的变量不能赋值给int类型的指针
    int *tp = &a;
    
    • 1
    • 2

    3.2 解引用

    *运算符给出指针指向地址上存储的值。*p的值为10,该值存储在地址上。


    3.3 指针整数相加

    可以使用+运算符与指针相加,或整数与指针相加。无论哪种情况,整数都会和指针所指向类型的大小(以字节为单位)相乘,然后把结果与初始值相加。在指针类型1.3中有示例。


    3.4 指针减去一个整数

    可以使用 - 运算符从指针中减去一个整数。指针必须是第1个运算对象,整数是第2个运算对象。该整数将乘以指针指向类型大小(以字节为单位),然后用初始地址减去乘积。

    如果相减的结果超出了初始指针所指向数组的范围,则超出的部分为野指针。除非正好超过数组末尾第一个位置,C保证该指针有效。


    3.5 递增递减指针

    递增递减同样适用于指针,递增指针指向数组元素的指针可以让该指针移动至数组的下一个元素。

    # include 
    
    int main(void){
    
    	int arr[5] = {0, 1, 2, 3, 4};
    
    	int *p = arr;
    	for (int i = 0; i < 5; i++)
    	{
    		// 通过指针的递增来遍历数组
    		*(p++) = i;
    	}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    3.6 指针求差

    可以计算两个指针的差值。通常,求差的两个指针分别指向同一个数组的不同元素,通过计算求除两个元素之间的距离。差值的单位与数据类型的单位相同。

    # include 
    
    int main(void){
    
    	int arr[]={0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    	
    	// 取数组首元素的地址
    	int *first = &arr[0];
    	// 取数组尾元素的地址
    	int *last = &arr[9];
    	// 两个指针相减,为数组的长度
    	int ret = *last - *first;
    	printf("arr[0]为:%d\n", arr[0]);	// 结果为:0
    	printf("arr[9]为:%d\n", arr[9]);	// 结果为:9
    	printf("数组长度为:%d\n", ret);	// 结果为:9
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3.7 指针比较

    使用关系运算符可以比较两个指针的值,前提是两个指针都指向相同类型的对象。

    # include 
    
    int main(void){
    
    	int arr[]={0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    
    	int *first = &arr[0];
    	int *last = &arr[9];
    	// 两个相同类型的指针比较,因为last大于first,所以输出的结果为0
    	int ret = *last < *first;
    
    	printf("ret结果为:%d\n", ret);	// 结果为:0
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    四、二级指针

    前面讲过,指针变量也是变量,是变量就有地址,存储指针变量的地址的指针就是二级指针

    # include 
    
    int main(void){
    
    	// 定义int类型的变量
    	int a = 10;
    	// 定义int类型的指针变量并赋值为a的地址
    	int *pta = &a; 
    	// 定义一个二级指针变量指向*pta的地址
    	int **ppta = &pta;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    a的地址存在pta中,pta的地址存在ppta中,pta为一级指针,ppta为二级指针。

    # include 
    
    int main(void){
    
    	// 定义int类型的变量
    	int a = 10;
    	// 定义int类型的指针变量并赋值为a的地址
    	int *pta = &a; 
    	// 定义一个二级指针变量指向*pta的地址
    	int **ppta = &pta;
    
    	printf("a的地址为:%p\n", a);	// 000000000000000A
    	printf("*pta的地址为:%p\n", *pta);	// *pta的地址为:000000000061FE14
    	printf("**ppta的地址为:%p\n", **ppta); // **ppta的地址为:000000000061FE08
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    五、指针数组

    指针数组的本质还是数组。不过数组中存储的是指针

    # include 
    
    int main(void){
    
    	// arr是一个数组,里面有6个元素,每个元素都是一个整型指针。其他类型的指针数组同理。
    	int *arr[6];
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • 相关阅读:
    2022年全国大学生数学建模竞赛总结
    【第二章 数据的表示和运算】d1
    Qt扫盲- QTextStream 理论总结
    JVM原理学习笔记总结
    图解系统(二)——进程管理
    【Nacos案例】
    Redis
    Vue 消息的订阅与发布
    代码随想录算法训练营19期第53天
    编译原理:编译原理简明教程知识点梳理(应对考试版)
  • 原文地址:https://blog.csdn.net/qq_46292926/article/details/127568865