C语言指针进阶应用:遍历数组、行地址、数组指针、指针数组、字符串指针

131 阅读6分钟

一、一级指针遍历二维数组

二维数组的传统遍历

#include <stdio.h>

int main() {
    // 定义并初始化一个3行4列的二维数组
    int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
    int i,j;
    
    // 计算行数:整个数组大小除以第一行的大小
    int n = sizeof(a) / sizeof(a[0]);
    // 计算列数:第一行大小除以每个元素的大小
    int m = sizeof(a[0]) / sizeof(int);
    
    // 遍历二维数组
    for (i = 0; i < n; i++) {        // 遍历每一行
        for (j = 0; j < m; j++) {    // 遍历当前行的每一列
            printf("%d\t", a[i][j]); // 打印元素,用制表符分隔
        }
        printf("\n"); // 每行结束后换行
    }

    return 0;
}

使用一级指针遍历二维数组

#include <stdio.h>

int main() {
    // 创建一个3行4列的二维数组
    int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
    int i;
    
    // 计算行数和列数
    int n = sizeof(a) / sizeof(a[0]);
    int m = sizeof(a[0]) / sizeof(int);
    
    // 声明一个指针变量
    int *p;
    // 当数组赋值给指针时,传递的是数组第一个元素的地址
    p = &a[0][0];
    
    // 使用指针遍历所有元素
    for (i = 0; i < n * m; i++) {
        printf("%d ", *(p + i));
    }
    printf("\n");

    return 0;
}

二、行地址与数组指针

二维数组名的地址特性

二维数组名代表数组的起始地址

#include <stdio.h>

int main() {
    int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
    
    printf("a + 0: %p\n", a + 0);    // 第0行地址
    printf("a + 1: %p\n", a + 1);    // 第1行地址(移动16字节)
    printf("a + 2: %p\n", a + 2);    // 第2行地址(移动32字节)
    
    // 计算地址差值
    printf("地址差值: %ld 字节\n", (char*)(a+1) - (char*)a);
    
    return 0;
}

行地址与数组指针

二维数组名代表数组的起始地址,数组名+1,是移动一行元素,二维数组名常被称为行地址。

#include <stdio.h>

int main() {
    int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
    
    // 定义数组指针:指向包含4个int的数组的指针
    int (*p)[4] = a;  // 行地址
    
    printf("a的地址:    %p\n", a);
    printf("p的地址:    %p\n", p);
    printf("a + 1地址:  %p\n", a + 1);
    printf("p + 1地址:  %p\n", p + 1);
    printf("a + 2地址:  %p\n", a + 2);
    printf("p + 2地址:  %p\n", p + 2);
    
    return 0;
}

三、数组指针的三种访问方式

p[i][j]*(p[i] + j)*(*(p+i) + j) 这三种写法都用于访问二维数组元素,它们是完全等价的。

示例演示

#include <stdio.h>

int main() {
    int arr[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8}, 
        {9, 10, 11, 12}
    };
    int (*p)[4] = arr;  // p是指向包含4个int的数组的指针
    
    // 访问arr[1][2](值为7)的三种等价方式:
    printf("p[1][2] = %d\n", p[1][2]);           // 方式一
    printf("*(p[1] + 2) = %d\n", *(p[1] + 2));   // 方式二
    printf("*(*(p+1) + 2) = %d\n", *(*(p+1) + 2)); // 方式三
    
    // 详细解释每种方式的步骤:
    printf("\n=== 详细解释 ===\n");
    
    // 方式一:p[1][2]
    printf("p[1][2]: 直接下标访问\n");
    
    // 方式二:*(p[1] + 2)
    printf("*(p[1] + 2):\n");
    printf("  p[1] = *(p+1) → 指向第1行的指针\n");
    printf("  p[1] + 2 → 指向第1行第2列元素的指针\n");
    printf("  *(p[1] + 2) → 解引用得到值%d\n", *(p[1] + 2));
    
    // 方式三:*(*(p+1) + 2)
    printf("*(*(p+1) + 2):\n");
    printf("  p+1 → 指向第1行的指针\n");
    printf("  *(p+1) → 第1行的数组名\n");
    printf("  *(p+1) + 2 → 指向第1行第2列元素的指针\n");
    printf("  *(*(p+1) + 2) → 解引用得到值%d\n", *(*(p+1) + 2));
    
    return 0;
}

四、字符指针与字符串

字符指针的基本使用

初始化字符指针是把内存中字符串的首地址赋予指针,并不是把该字符串复制到指针当中。

#include <stdio.h>

int main() {
    char str[] = "Hello World";
    char *p = str;  // p指向字符串的首地址
    
    printf("字符串: %s\n", str);
    printf("指针指向: %s\n", p);
    printf("str地址: %p\n", str);
    printf("p的值: %p\n", p);
    
    // 通过指针访问字符
    printf("第一个字符: %c\n", *p);
    printf("第二个字符: %c\n", *(p+1));
    
    return 0;
}

大小写转换示例

在C语言中,字符是用ASCII码表示的:

  • 'A' = 65, 'B' = 66, ..., 'Z' = 90
  • 'a' = 97, 'b' = 98, ..., 'z' = 122
#include <stdio.h>

int main() {
    char s[] = "Hello World";
    char *p = s;
    
    printf("原始字符串: %s\n", s);
    
    while (*p != '\0') {
        if (*p >= 'A' && *p <= 'Z') {
            *p += 32;  // 大写转小写
        } else if (*p >= 'a' && *p <= 'z') {
            *p -= 32;  // 小写转大写
        }
        p++;
    }
    
    printf("转换后: %s\n", s);
    
    return 0;
}

五、指针数组

指针数组的基本用法

指针数组的一般说明形式:数据类型 *指针数组名[大小]

#include <stdio.h>

int main() {
    int a = 10, b = 20, c = 30;
    int *p[3];  // 声明一个包含3个int指针的数组
    
    p[0] = &a;
    p[1] = &b;
    p[2] = &c;
    
    // 通过指针数组访问值
    for(int i = 0; i < 3; i++) {
        printf("p[%d] = %p, *p[%d] = %d\n", i, p[i], i, *p[i]);
    }
    
    return 0;
}

字符串指针数组

因64位系统下指针数组的大小都是八个字节(包括char ,int),所以在计算数组大小时sizeof(char*)要这么写。

#include <stdio.h>

int main() {
    char *s[] = {"BeiJing", "ShangHai", "GuangZhou"};
    int i;
    
    // 计算数组元素个数
    int count = sizeof(s) / sizeof(char*);
    
    printf("城市列表:\n");
    for (i = 0; i < count; i++) {
        printf("%d: %s\n", i, s[i]);
    }
    
    // 显示每个指针的值
    printf("\n指针地址:\n");
    for (i = 0; i < count; i++) {
        printf("s[%d] = %p, 指向: %s\n", i, s[i], s[i]);
    }
    
    return 0;
}

六、数组指针与指针数组的区别

指针数组 int *p[5]

  • 指针数组,是一维数组,每个元素都是int*
  • 指针数组经常结合二维数组使用,储存每行首元素的地址
  • 本质是数组,储存内容是指针
#include <stdio.h>

int main() {
    int a = 1, b = 2, c = 3, d = 4, e = 5;
    int *p[5] = {&a, &b, &c, &d, &e};  // 指针数组
    
    printf("指针数组示例:\n");
    for(int i = 0; i < 5; i++) {
        printf("p[%d] = %p, *p[%d] = %d\n", i, p[i], i, *p[i]);
    }
    
    return 0;
}

数组指针 int (*p)[5]

  • 指向数组的指针
  • 本质是指针,指向的内容是含有5个元素的一维数组
  • 一维数组名取地址,或者二维数组取名,都可以赋予它
#include <stdio.h>

int main() {
    int arr[3][5] = {
        {1, 2, 3, 4, 5},
        {6, 7, 8, 9, 10},
        {11, 12, 13, 14, 15}
    };
    
    int (*p)[5] = arr;  // 数组指针,指向包含5个int的数组
    
    printf("数组指针示例:\n");
    for(int i = 0; i < 3; i++) {
        for(int j = 0; j < 5; j++) {
            printf("%2d ", p[i][j]);
        }
        printf("\n");
    }
    
    return 0;
}

关键点总结

特性指针数组数组指针
声明int *p[5]int (*p)[5]
本质数组指针
存储内容多个指针一个数组地址
常用场景字符串数组、多级指针二维数组操作