C语言指针详解:从字符指针到函数指针的核心概念
C语言中指针是核心难点,理解指针的关键在于明确其类型所决定的步长与访问权限。本文从字符指针、指针数组、数组指针及函数指针四个维度,结合内存地址分配原理与代码运行结果,系统梳理各类指针的定义方式、存储特性及使用规范。
字符指针:常量区与栈区的差异
指针的本质是存放地址的变量。对于字符类型的指针,常见两种初始化的逻辑,它们在内存管理和数据安全性上有显著区别。
第1步:初始化普通字符指针
目的:展示如何获取变量地址并建立指针关联。
操作:定义一个字符变量,取其地址赋给 char* 类型的指针。
int main()
{
char ch = 'w';
char* pc = &ch;
*pc = 'w'; // 通过指针修改值
return 0;
}
在此场景中,pc 指向栈区中的变量 ch,内容可自由读写。
第2步:初始化指向字符串常量的指针
目的:理解字符串字面量在内存中的存储位置及只读特性。
操作:将字符串直接赋值给指针变量。
const char* pstr = "hello bit.";
printf("%s\n", pstr);
这里并非把整个字符串放入指针变量,而是将字符串首字符的地址存入 pstr。该字符串位于常量区,无论是否添加 const 修饰,其内容均不可修改。若尝试修改:*pstr = 'w';,程序将在运行时崩溃或报错。
第3步:对比数组与指针的内存分布
目的:验证字符串常量与字符数组在内存地址上的差异。
代码逻辑:比较两个相同内容的字符数组与两个相同内容的字符指针的地址关系。
int main()
{
char str1[] = "hello world."; // 栈区数组,两份独立拷贝
char str2[] = "hello world.";
const char *str3 = "hello world."; // 指向常量区同一地址
const char *str4 = "hello world.";
if(str1 == str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n"); // 输出:not same
if(str3 == str4)
printf("str3 and str4 are same\n"); // 输出:same
else
printf("str3 and str4 are not same\n");
return 0;
}
运行结果表明:str1 和 str2 各自占用独立的栈空间,地址不同;而 str3 和 str4 指向常量区的同一字符串实例,地址相同。
这一机制说明了编译器对字符串字面量的优化策略以及数组复制的本质。
指针数组:存储指针的容器
指针数组的本质依然是数组,只是其元素类型为指针。它与普通数组的区别仅在于存储的数据类型不同,语法结构完全类比。
第1步:定义不同类型的指针数组
入口:声明语句,根据括号优先级判断。
动作:根据 [] 优先级高于 * 的特点,先确定是数组,再确定元素类型。
int* arr1[10]; // 存放10个 int* 类型指针的数组
char *arr2[4]; // 存放4个 char* 类型指针的数组
char **arr3[5]; // 存放5个 char** 类型指针的数组
由于 [] 的优先级高于 *,arr1 首先是一个拥有10个元素的数组,每个元素都是 int* 类型。这就像普通的整型数组一样,只不过里面装的是地址。
数组指针:指向数组的指针
数组指针的本质是指针,它指向一个完整的数组对象。区分“指针数组”与“数组指针”的关键在于运算符优先级:加括号后 (*p) 优先,则 p 为指针,指向数组。
第1步:理解数组名与取地址的差异
目的:辨析数组首元素地址与整个数组地址在算术运算中的步长区别。
参数:int arr[10] 整型数组。
int main()
{
int arr[10] = { 0 };
printf("arr = %p\n", arr); // 数组首元素地址
printf("&arr= %p\n", &arr); // 整个数组的地址
printf("arr+1 = %p\n", arr + 1); // 跳过1个int (4字节)
printf("&arr+1= %p\n", &arr + 1); // 跳过整个数组 (40字节)
return 0;
}
结果显示,arr+1 和 &arr+1 的数值跨度不同。前者以元素大小为单位,后者以数组总大小为单位的偏移。
第2步:定义数组指针
定义: int (*p)[10]; 表示 p 是一个指针,指向包含10个整数的数组。
类比关系:普通指针指向单个变量,数组指针指向整个数组对象。
函数指针:调用函数的另一种方式
函数在内存中也有自己的地址。函数指针即为存放函数入口地址的变量,可用于间接调用函数,常用于回调机制或函数表实现。
第1步:获取函数地址
目的:验证函数名与取址函数名的等价性。
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", test); // 函数地址
printf("%p\n", &test); // 函数地址
return 0;
}
测试表明,test 和 &test 输出相同的地址值,说明函数名即代表其入口地址。
第2步:通过函数指针调用函数
目的:演示函数指针的定义语法和解引用调用方式。
动作:定义匹配签名的指针变量,赋值并解引用调用。
void test()
{
printf("hehe\n");
}
int main()
{
// 定义函数指针:指向无参无返回值的函数
void (*p)() = test;
// 调用:(*p)() 或直接用 p() 均可,推荐显式解引用以强调指针性质
(*p)();
return 0;
}
代码执行后成功打印 “hehe”。关键在于理解 void (*p)() 中,括号强制 * 先结合,说明 p 是指针,指向一个函数,该函数无参数且无返回值。
以上就是C语言清楚了解指针的使用的详细内容,更多关于C语言指针的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









