>点击蓝字,关注我们
往
期
回
顾
C语言中的断言:如何使用assert.h头文件进行调试
C语言的数组:跨越一个阶梯,如何用一种数据结构存储无限多的数据?
01
本节重点
C语言指针
什么是指针?
指针是C语言中一种特殊的变量,它可以存储另一个变量的内存地址。通过指针,我们可以间接地访问或修改内存中的数据,而不需要知道它们的具体位置。指针是C语言的灵魂,它使得C语言具有强大的功能和灵活性,但也带来了一些复杂性和风险。
为什么要使用指针?
指针的用途非常广泛,它可以帮助我们实现一些C语言中的核心功能,例如:
动态内存分配:通过指针,我们可以在运行时根据需要申请或释放内存空间,而不必事先确定大小或数量。
数组和字符串:数组和字符串本质上都是指针,它们指向一段连续的内存空间,其中存储了多个相同类型的数据或字符。通过指针,我们可以方便地操作数组和字符串中的元素,或者传递它们作为函数的参数。
函数指针:函数指针是一种指针,它指向一个函数的入口地址。通过函数指针,我们可以实现函数的回调或者多态,即根据不同的情况调用不同的函数。
链表和树:链表和树是两种常用的数据结构,它们由多个节点组成,每个节点都包含一个或多个指针,指向其他节点。通过指针,我们可以构建和遍历这些复杂的数据结构,实现各种算法和应用。
如何定义和使用指针?
指针的定义和使用需要遵循一些基本的规则和格式,否则可能会导致错误或者未定义的行为。下面是一些常见的指针操作的示例和说明:
-定义指针:指针的定义需要指定指针的类型和名称,类型表示指针所指向的变量的类型,名称表示指针变量的标识符。指针的类型和名称之间需要加一个星号(*),表示这是一个指针。例如:
int *p; // 定义一个指向整型的指针pchar *s; // 定义一个指向字符型的指针svoid (*f)(int); // 定义一个指向函数的指针f,该函数接受一个整型参数,没有返回值
-初始化指针:指针的初始化需要给指针赋值一个合法的内存地址,这个地址通常是另一个变量的地址,或者是动态分配的内存空间的地址。为了获取一个变量的地址,我们需要使用取地址符(&),它可以返回一个变量在内存中的位置。例如:
int a = 10; // 定义一个整型变量a,赋值为10int *p = &a; // 定义一个指向整型的指针p,初始化为a的地址char *s = "Hello"; // 定义一个指向字符型的指针s,初始化为字符串常量"Hello"的地址void *q = malloc(sizeof(int)); // 定义一个指向任意类型的指针q,初始化为动态分配的一个整型大小的内存空间的地址
-访问指针:访问指针的目的是获取或修改指针所指向的变量的值,这需要使用解引用符(*),它可以返回一个指针所指向的内存地址中存储的数据。例如:
int a = 10; // 定义一个整型变量a,赋值为10int *p = &a; // 定义一个指向整型的指针p,初始化为a的地址printf("%d\n", *p); // 打印指针p所指向的变量的值,即10*p = 20; // 修改指针p所指向的变量的值,即将a的值改为20printf("%d\n", a); // 打印变量a的值,即20
-指针的算术运算:指针的算术运算是指对指针进行加减法,或者比较两个指针的大小。指针的算术运算的结果取决于指针的类型,因为不同类型的指针所指向的变量占用的内存空间大小不同。例如:
int a[5] = {1, 2, 3, 4, 5}; // 定义一个整型数组a,包含5个元素int *p = a; // 定义一个指向整型的指针p,初始化为数组a的首地址,即a[0]的地址int *q = p + 2; // 定义一个指向整型的指针q,初始化为p加上2,即a[2]的地址printf("%d\n", *q); // 打印指针q所指向的变量的值,即3printf("%d\n", q - p); // 打印指针q和p的差值,即2,表示q和p之间相隔了2个整型的距离printf("%d\n", q > p); // 打印指针q和p的比较结果,即1,表示q的地址大于p的地址
指针的注意事项
指针虽然强大,但也需要注意一些常见的问题和错误,例如:
-空指针:空指针是一个特殊的指针,它不指向任何有效的内存地址,它的值为NULL。空指针通常用来表示一个指针没有被初始化,或者表示一个指针已经失效。空指针不能被解引用,否则会导致程序崩溃。例如:
int *p = NULL; //定义一个指向整型的指针p,初始化为NULL
printf("%d\n",*p); // 错误,试图访问一个空指针所指向的变量的值,会导致程序崩溃
-野指针:野指针是一个指向无效或未知的内存地址的指针,它的值不是NULL,但也不是一个合法的地址。野指针通常是由于指针没有被正确初始化,或者指针指向的内存空间已经被释放或修改而造成的。野指针也不能被解引用,否则会导致程序崩溃或数据错误。例如:
int *p; //定义一个指向整型的指针p,没有初始化,它的值是随机的
printf("%d\n",*p); // 错误,试图访问一个野指针所指向的变量的值,会导致程序崩溃或数据错误
-指针的越界:指针的越界是指指针指向了一个超出了合法范围的内存地址,这通常是由于指针的算术运算或者数组的下标操作造成的。指针的越界可能会覆盖或破坏其他的内存空间,导致程序崩溃或数据错误。例如:
int a[5] = {1, 2, 3, 4,5}; // 定义一个整型数组a,包含5个元素
int *p = a + 5; //定义一个指向整型的指针p,初始化为a加上5,即a[5]的地址,这是一个越界的地址,因为数组的下标从0开始,最大为4
printf("%d\n",*p); // 错误,试图访问一个越界的指针所指向的变量的值,会导致程序崩溃或数据错误
-指针的类型转换:指针的类型转换是指将一个指针强制转换为另一个类型的指针,这通常是为了实现一些特殊的功能或者绕过一些限制。指针的类型转换需要谨慎使用,因为不同类型的指针所指向的变量的大小和表示方式可能不同,如果不正确地转换或者访问,可能会导致程序崩溃或数据错误。例如:
int a = 0x12345678; //定义一个整型变量a,赋值为16进制的12345678
char *p = (char*)&a; // 定义一个指向字符型的指针p,将a的地址强制转换为字符型的地址,这是一个类型转换的操作
printf("%x\n",*p); //打印指针p所指向的变量的值,按照16进制的格式,结果是78,这是因为字符型的大小为1个字节,而整型的大小为4个字节,指针p只能访问到a的最低位的字节
实例:
为了更好地理解指针的概念和用法,我们来看一个实际的例子:
#include <stdio.h>
// 定义一个交换两个整数的函数,使用指针作为参数void swap(int *a, int *b) { // 定义一个临时变量,用来存储*a的值 int temp = *a; // 将*b的值赋给*a,即交换了两个变量的值 *a = *b; // 将temp的值赋给*b,即完成了交换 *b = temp;}
int main() { // 定义两个整数变量x和y,分别赋值为10和20 int x = 10, y = 20; // 打印x和y的值,此时为10和20 printf("x = %d, y = %d\n", x, y); // 调用swap函数,传入x和y的地址,即使用&运算符获取变量的地址 swap(&x, &y); // 再次打印x和y的值,此时为20和10,说明交换成功 printf("x = %d, y = %d\n", x, y); // 返回0,表示程序正常结束 return 0;}
学习C语言,可以购买下列链接的C语言基础书籍进行系统的学习:
点赞加关注,学习不迷路
微信公众号|工控小新
EPLAN电气绘图、TIA博图基础 、CAD、C语言教学、单片机基础、三菱PLC ... 每日持续更新中
发现“分享”和“赞”了吗,戳我看看吧
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |