C语言指针进阶解析:字符指针、指针数组与数组指针的区别

认真发呆
认真发呆
发布时间:2026-10-16| 6人浏览过
原创

C语言指针是核心难点之一,尤其是涉及字符处理与多维数组时容易混淆。本文从内存分配角度详解字符指针指向常量区的特性,利用“剥去变量名看类型”的技巧辨析指针数组与数组指针,并重点剖析数组指针在二维数组处理中的步长逻辑与函数应用,助你攻克指针进阶关卡。

字符指针与字符串内存分布

在深入复杂结构前,我们先巩固对 char * 的理解。指针本质是存储地址的变量,其大小由平台决定(32位为4字节,64位为8字节),且指针的类型决定了偏移步长和解引用的权限。对于字符指针,主要有两种常见的初始化方式:

第1步:单字符引用

目的:理解普通字符变量的地址存取。
操作:定义一个字符变量并取地址赋给指针。
代码示例:

int main()
{
    char ch = 'w';
    char *pc = &ch;
    *pc = 'w'; // 修改所指向的值
    return 0;
}

第2步:字符串字面量引用

目的:掌握常量字符串的存储位置及指针的真实含义。
操作:将双引号括起来的字符串直接赋给指针。

const char* pstr = "hello bit.";
这里并不是将整串字符存入指针(指针仅能存地址),而是将字符串首字符 'h' 的地址存入 pstr。由于双引号内的字符串属于字符串常量,存储在静态数据区(常量区),因此它不可被修改。

为了更直观地看清这一差异,我们可以对比数组定义与指针赋值的结果:

#include <stdio.h>
int main()
{
    // 两个独立的栈内存数组,内容相同但地址不同
    char str1[] = "hello bit.";
    char str2[] = "hello bit.";
    
    // 指向同一常量区地址
    const char *str3 = "hello bit.";
    const char *str4 = "hello bit.";
    
    if(str1 == str2)
        printf("str1 and str2 are same\n");
    else
        printf("str1 and str2 are not same\n");
        
    if(str3 == str4)
        printf("str3 and str4 are same\n");
    else
        printf("str3 and str4 are not same\n");
    return 0;
}

运行结果显示,str1 和 str2 地址不相等,因为它们在栈上各自开辟了独立的存储空间,类似数组压栈行为,需注意避免栈溢出风险;而 str3 和 str4 指向同一个常量字符串的首地址,故相等。这验证了数组名通常表示首元素地址(非整个数组),而字符串常量在全局/静态区仅保存一份副本的特点。

指针数组 vs 数组指针

这两个名称极易混淆,区分它们的核心法则很简单:去掉变量名,剩下的即为该变量真正代表的类型。

第1步:识别指针数组

操作:观察变量声明。若 [] 先于 * 结合,则是数组。

int *arr1[10]; // 优先级 [] 高于 *,arr1是包含10个 int* 的数组
char *arr2[4]; // 包含4个 char* 的数组

第2步:识别数组指针

操作:观察变量声明。若括号 () 将 * 强制结合到变量名,则是指针。
结果:它是指向数组的指针。

int (*p)[10]; // p先和*结合,说明p是一个指针,指向大小为10的整型数组

判断口诀:先找变量名,看它和谁优先结合。与 * 优先结合就是指针,与 [] 优先结合就是数组。

数组指针的定义与地址步长陷阱

既然明确了 int (*p)[10] 是数组指针,那它在实际地址运算中与普通数组名有何不同?这是指针进阶的关键。

第1步:数组名与取地址的差异

目的:探究 arr 与 &arr 的物理意义。
操作:分别打印一维数组的基地址及加1后的地址。

#include <stdio.h>
int main()
{
    int arr[10] = {0};
    printf("%p\n", arr);      // 数组首元素地址
    printf("%p\n", &arr);     // 整个数组的地址
    
    printf("arr+1 = %p\n", arr+1);       // 跳过sizeof(int),即4字节
    printf("&arr+1= %p\n", &arr+1);      // 跳过整个数组,即40字节
    return 0; 
}

虽然两者输出的初始地址数值相同,但在进行 +1 运算时截然不同:
- arr + 1:跳过 1 个 int 的大小(4字节)。 - &arr + 1:跳过整个数组的大小(10 × 4 = 40字节)。
&arr 的类型实际上是 int (*)[10],这是一种典型的数组指针类型。

数组指针的实际应用场景

理解了数组指针的含义后,最常见的实战场景便是处理二维数组。

第1步:使用下标遍历二维数组

这种方式参数传递时会自动退化为首元素地址,编译器通过行列索引计算位移。

void print_arr1(int arr[3][5], int row, int col) {
    for (int i = 0; i < row; i++)
        for (int j = 0; j < col; j++)
            printf("%d ", arr[i][j]);
}

第2步:使用数组指针高效遍历

目的:在函数参数列表中显式表达“这是一个行指针”,增强代码可读性并明确内存结构。
操作:形参接收类型为 int(*arr)[5]。

#include<stdio.h>
// 用指针来打印二维数组
void print_arr2(int(*arr)[5], int row, int col) {
    int i = 0;
    int j = 0;
    for (i = 0; i < row; i++) {
        for (j = 0; j < col; j++) {
            // 此时 arr+i 代表第 i 行的地址,解引用 *(arr+i) 得到该行首元素,再偏移 j
            printf("%d ", (*(arr + i))[j]); // 或简写为 arr[i][j]
        }
        printf("\n");
    }
}

int main() {
    int arr[3][5] = { 1,2,3,4,5,6,7,8,9,10 }; // 注意补全所有元素避免未初始化警告
    print_arr1(arr, 3, 5);
    print_arr2(arr, 3, 5); // 传递的是第一行的地址,符合数组指针接收要求
    return 0;
}

在这里,实参 arr 退化为第一行的地址,正好可以用 int(*)[5] 类型的数组指针接住。数组指针在处理多维数据映射时优势明显。

以上就是C语言指针进阶详细内容的解析,更多关于指针与内存管理的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。