ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

C语言基础(八)指针相关内容

C语言基础(八)指针相关内容 C语言指针引言指针是C语言的核心机制之一也是其区别于多数高级语言的关键特性。指针本质上是对内存地址的抽象它提供了直接操作内存的能力使得C语言能够实现高效的系统编程、硬件控制和动态内存管理。本文基于C语言标准系统阐述指针的语法、语义及典型应用场景涵盖从基础定义到函数指针的完整知识体系。一、指针基础1.1 内存、地址与指针的概念计算机内存以字节为单位进行编址每个字节对应一个唯一的数值标识即地址。程序运行时变量被分配在内存的特定位置其起始地址称为该变量的首地址。指针即地址本身而指针变量是用于存储地址的变量其自身也占用内存空间在64位系统中通常为8字节32位系统中为4字节。1.2 指针变量的定义指针变量的定义语法如下基类型*变量名;基类型指明该指针所指向内存单元中存储的数据类型可以是基本数据类型int、char、float、double、构造数据类型或void。*类型说明符表示定义的是指针变量。变量名遵循标识符命名规则通常以p或ptr为前缀以增强可读性。示例int*pint;// 指向int型数据的指针char*pch;// 指向char型数据的指针double*pd;// 指向double型数据的指针指针变量中存储的是其所指向数据的首地址。1.3 指针变量的初始化与赋值指针变量必须在使用前被明确赋值否则其值为随机野指针可能导致不可预料的错误。推荐初始化为NULL即0或已有变量的地址。intnum10;int*pNULL;// 初始化为空指针pnum;// 赋值获取num的地址野指针是指未初始化、已释放或指向无效内存的指针。避免野指针的核心原则定义时初始化为NULL仅将指针指向合法且生命周期可控的变量释放动态内存后将指针置为NULL。1.4 指针运算符取地址运算符获取变量的首地址。只能作用于变量不能用于常量或表达式。间接访问运算符*解引用访问指针所指向的内存单元中的数据。解引用所访问的字节数由指针的基类型决定例如char *解引用访问1字节int *访问4字节。inta5;int*paa;*pa10;// 等价于 a 10;1.5 指针的算术运算指针支持有限的算术运算主要包括自增、自减--、加/减整数pn、p-n以及指针相减仅当指向同一数组时有效。运算的步长由基类型大小决定即p1的地址偏移量为sizeof(基类型)字节。intarr[5]{1,2,3,4,5};int*parr;// 指向arr[0]p;// 现在指向arr[1]地址增加4字节int为4字节指针相减的结果为元素个数差类型为ptrdiff_t有符号整数。二、指针与一维数组2.1 数组名的指针语义在C语言中数组名在绝大多数表达式中被解释为数组首元素的地址即arr等价于arr[0]其类型为基类型 *。例如inta[10];int*pa;// 合法a为int*类型2.2 通过指针访问数组元素利用指针算术和间接访问可以遍历数组for(inti0;i10;i){*(pi)i;// 等价于 a[i] i;}a[i]本质上被编译器转换为*(ai)故指针和数组下标在访问层面等效。2.3 数组作为函数参数当数组作为函数参数传递时实际传递的是首元素的地址形参退化为指针。因此以下两种函数原型等价voidprint_array(intarr[],intlen);voidprint_array(int*arr,intlen);在被调函数中sizeof(arr)获得的是指针本身的大小8字节而非数组总字节数故必须显式传递数组长度。三、指针与一维字符型数组字符型数组C风格字符串以\0作为结束标志。数组名同样退化为字符指针指向字符串首字符。charstr[32]hello;char*pstr;// 等价于 char *p str[0];遍历字符串可通过指针递增直至遇到\0intmystrlen(constchar*s){intlen0;while(*s)len;returnlen;}由于字符串自带终止符函数传参时无需额外传递长度仅需传入指针即可。四、数组指针指向数组的指针数组指针是指向整个数组的指针其定义语法为基类型(*指针名)[数组长度];例如int (*p)[5]定义了一个指针可以指向具有5个int元素的数组。它与普通指针的关键区别在于步长p1将偏移整个数组的大小5 * sizeof(int)。inta[5]{1,2,3,4,5};int(*pa)[5]a;// pa指向数组整体注意以下区别a数组名类型为int *指向首元素a类型为int (*)[5]指向整个数组。数组指针通常用于操作二维数组的行。五、指针与二维数组5.1 二维数组名的指针类型二维数组int a[2][3]的数组名a在表达式中被解释为首元素即第一行一维数组的地址其类型为int (*)[3]即一个数组指针步长为3 * sizeof(int)。5.2 指针访问二维数组元素借助数组指针和偏移可以访问任意元素inta[2][3]{{1,2,3},{4,5,6}};int(*p)[3]a;// p指向第一行intval*(*(p1)2);// 访问a[1][2]值为6通用公式a[i][j]等价于*(*(ai) j)。5.3 二维数组作为函数参数形参可声明为二维数组形式行数可省略列数不可省略或等价的数组指针形式voidprint_2D(intarr[][3],introws);voidprint_2D(int(*arr)[3],introws);// 完全等价调用时传递数组名及行数即可。六、指针数组保存指针的数组指针数组本质是数组其元素均为指针类型。定义语法基类型*数组名[元素个数];例如char *str_arr[5]定义了一个包含5个字符指针的数组每个指针可指向一个字符串常量或字符数组。初始化char*p[3]{hello,world,how};此时p[0]存储字符串常量hello的首地址p[1]存储world的首地址依此类推。指针数组常用于处理多个字符串每个字符串的长度可不同且各元素可独立重新指向。指针数组与二维字符数组的区别特性二维字符数组指针数组存储内容字符串数据本身占全部空间字符串的地址内存分配固定行列浪费或不足灵活各指针独立指向修改内容可修改字符串内容可改变指针指向但字符串常量不可修改典型用途存储固定数量、固定长度的字符串操作多个动态字符串或常量串七、二级指针指向指针的指针二级指针是存储指针变量的地址的指针。定义语法基类型**变量名;例如int **pp表示指向int *类型变量的指针。7.1 主要应用场景在函数中修改主调函数的指针变量若需在被调函数中改变主调函数中指针的指向需传递该指针的地址形参用二级指针接收。voidset_pointer(int**pp,int*new_val){*ppnew_val;// 修改主调中的指针}调用set_pointer(p, some_var);指针数组的数组名char *p_arr[3]在表达式中退化为char **类型可传递给期望二级指针的函数用于遍历或操作指针数组。voidprint_strs(char**arr,intn){for(inti0;in;i){puts(arr[i]);// arr[i]为char*}}八、指针函数返回指针的函数指针函数是指返回值为指针类型的函数。定义时需将*与函数名结合基类型*函数名(形参表);例如char *strcpy(char *dest, const char *src);返回char *。8.1 合法返回的指针类型静态变量或全局变量的地址具有静态生命周期程序结束时才释放。字符串常量地址位于只读数据段生命周期贯穿程序。通过参数传入的地址可直接返回该地址如strcpy返回目标地址。堆区动态分配的内存通过malloc等前提是未释放。8.2 禁止返回局部变量的地址局部变量包括局部数组在函数返回后其栈帧被销毁返回其地址将导致悬垂指针访问未定义行为。int*bad_func(){intx10;returnx;// 错误}九、函数指针指向函数的指针9.1 定义与赋值函数名代表函数的入口地址。函数指针的定义需指定返回类型和参数类型列表返回值类型(*指针名)(参数类型列表);例如int (*pfun)(int, int);定义了一个可指向返回int、接受两个int参数的函数的指针。赋值intadd(inta,intb){returnab;}pfunadd;// 或 pfun add;9.2 调用通过函数指针调用函数可直接使用指针名或解引用加实参intresultpfun(3,4);// 等价于 add(3,4)9.3 回调函数回调函数是将函数指针作为参数传递给另一函数使得被调函数能够在适当时候调用该函数。回调机制广泛应用于事件驱动、排序算法如qsort的比较函数和框架设计中其核心优势在于解耦调用者与具体实现提高代码复用性和灵活性。示例简化版排序比较intcompare_int(constvoid*a,constvoid*b){return(*(int*)a-*(int*)b);}voidsort(void*base,intn,intsize,int(*cmp)(constvoid*,constvoid*));调用时传递compare_int作为回调。十、总结指针是C语言的精髓其核心思想可归纳为指针即地址指针变量存储地址通过基类型决定访问宽度。数组名在表达式中退化为指针函数参数传递数组时实际传递指针。数组指针、指针数组、二级指针各具用途分别用于指向数组整体、存储指针集合、修改指针变量。指针函数与函数指针需严格区分前者返回指针后者指向函数。指针与数组、字符串、函数、动态内存管理紧密关联掌握指针是深入理解C语言内存模型和系统编程的基础。正确使用指针能够显著提升程序效率但不当使用如野指针、越界访问将导致严重错误。开发者应始终遵循“初始化、检查边界、避免悬垂”的原则并善用const修饰符增强安全性与可读性。
返回列表