C 语言深入理解指针

一、指针基础与内存地址

1. 内存与地址

内存被划分为一个个字节单元,每个单元都有唯一编号(地址),也称指针。计算机通过地址总线实现对内存的编址,32 位机器有 32 根地址总线,可表示 2³² 个地址(4GB 空间),64 位机器则支持更大内存空间。

  • 单位换算:1 字节(byte)= 8 比特(bit),1KB=1024B,1MB=1024KB,以此类推。
  • 地址表示:通常以十六进制形式呈现(如0x006FFD70)。

2. 指针变量

  • 定义与初始化:指针变量用于存储地址,格式为类型* 变量名。例如:

    int a = 10;
    int* pa = &a; // pa存储a的地址
    
  • 解引用操作:通过*操作符访问指针指向的变量:

    *pa = 20; // 等价于a = 20
    
  • 大小:指针变量大小与类型无关,32 位平台占 4 字节,64 位平台占 8 字节。

二、指针类型与 const 修饰

1. 指针类型的意义

  • 解引用权限char*指针解引用访问 1 字节,int*访问 4 字节。

    int n = 0x11223344;
    char* pc = (char*)&n;
    *pc = 0; // 仅修改n的第一个字节
    
  • 指针 ± 整数:步长由类型决定。char*+1 跳过 1 字节,int*+1 跳过 4 字节。

2. const 修饰指针

  • const 在 * 左:限制指针指向的内容不可修改,但指针本身可改。

    const int* p; // *p不可改,p可改
    
  • const 在 * 右:限制指针本身不可修改,但指向的内容可改。

    int* const p; // p不可改,*p可改
    
  • const 在 * 两侧:指针及其指向的内容均不可改。

    const int* const p; // 均不可改
    

三、数组与指针

1. 数组名的特殊性

  • 数组名通常表示首元素地址,但有两个例外:
    • sizeof(数组名):计算整个数组大小。
    • &数组名:取出整个数组的地址。
    int arr[10];
    printf("%p\n", arr);       // 首元素地址
    printf("%p\n", &arr);      // 数组地址(与首元素地址值相同但意义不同)
    printf("%zd\n", sizeof(arr)); // 40(10*4字节)
    

2. 指针访问数组

  • 数组元素arr[i]等价于*(arr+i),指针p[i]等价于*(p+i)

    int arr[5] = {1,2,3,4,5};
    int* p = arr;
    for(int i=0; i<5; i++) {
        printf("%d ", *(p+i)); // 打印1 2 3 4 5
    }
    

3. 数组传参本质

  • 数组传参实际传递首元素地址,形参可写成数组或指针形式。

    void test(int arr[]) {} // 等价于int* arr
    void test(int* arr) {}
    

四、多级指针与指针数组

1. 二级指针

  • 指向指针的指针,用于存储指针变量的地址。

    int a = 10;
    int* pa = &a;
    int** ppa = &pa; // 二级指针
    **ppa = 20; // 等价于a = 20
    

2. 指针数组

  • 存放指针的数组,可模拟二维数组。

    int arr1[] = {1,2,3};
    int arr2[] = {4,5,6};
    int* parr[] = {arr1, arr2}; // 指针数组
    printf("%d\n", parr[0][1]); // 2
    

五、函数指针与回调函数

1. 函数指针

  • 存储函数地址的指针,格式为返回类型(*指针名)(参数类型)

    int Add(int x, int y) { return x+y; }
    int(*pf)(int, int) = Add;
    printf("%d\n", pf(2,3)); // 5
    

2. 函数指针数组

  • 存放函数指针的数组,用于实现转移表(如计算器)。

    int(*p[])(int,int) = {0, add, sub, mul, div}; // 转移表
    

3. 回调函数

  • 通过函数指针调用的函数,由其他函数间接调用。

    void calc(int(*pf)(int,int)) {
        int x,y;
        scanf("%d%d", &x, &y);
        printf("%d\n", pf(x,y));
    }
    calc(add); // 调用add函数
    

六、qsort 函数与指针应用

1. qsort 使用

  • 标准库中的快速排序函数,支持任意类型排序,需自定义比较函数。

    #include 
    int int_cmp(const void* p1, const void* p2) {
        return *(int*)p1 - *(int*)p2;
    }
    int main() {
        int arr[] = {3,1,5,2};
        qsort(arr, 4, sizeof(int), int_cmp);
        return 0;
    }
    

2. 模拟实现 qsort(冒泡排序版)

  • 利用void*指针和回调函数实现通用排序。

    void swap(void* p1, void* p2, int size) {
        for(int i=0; i

七、sizeof 与 strlen 对比及笔试题

1. 关键区别

sizeof strlen
操作符,计算内存大小(字节) 库函数,计算字符串长度(\0 前字符数)
不关注内容 需找到 \0,可能越界

2. 典型笔试题解析

  • 一维数组

    int a[] = {1,2,3,4};
    printf("%zd\n", sizeof(a)); // 16(整个数组大小)
    printf("%zd\n", sizeof(a+0)); // 4/8(首元素地址大小)
    
  • 字符数组

    c

    char arr[] = "abc";
    printf("%zd\n", sizeof(arr)); // 4(含\0)
    printf("%d\n", strlen(arr));  // 3(不含\0)
    
  • 二维数组

    int a[3][4];
    printf("%zd\n", sizeof(a[0])); // 16(第一行大小)
    printf("%zd\n", sizeof(a+1)); // 4/8(第二行地址大小)
    

八、面试考察要点

  1. 指针本质:地址的表示与存储,指针变量大小与平台的关系。
  2. 数组与指针:数组名的特殊性,指针访问数组的原理,数组传参本质。
  3. 指针类型:类型对解引用和步长的影响,const 修饰的不同效果。
  4. 函数指针:定义与使用,回调函数的应用(如 qsort)。
  5. 笔试题常见陷阱:sizeof 与 strlen 的区别,指针运算的边界情况,二维数组的指针访问。

指针是 C 语言的核心难点,掌握指针需结合内存模型和实际代码调试,多练习数组、函数与指针的综合应用场景(如排序、字符串处理)。

C 语言深入理解指针_第1张图片

你可能感兴趣的:(C 语言深入理解指针)