申倩语 发表于 2025-6-1 00:07:34

指针与数组

指针与数组

指针操作数组元素

在C语言中,数组名实际上就是一个指向数组首元素的指针。换句话说,可以把数组名视为指向了数组的第一个元素的内存地址。
例如,对于一个整型数组 int arry = {1, 2, 3, 4, 5},我们可以通过数组名 arry 或者通过取指针操作符 &arry 来获取指向数组第一个元素的指针。数组名字是数组的首元素地址,但它是一个常量。
我们可以通过指针访问数组元素或者遍历整个数组。
#include <stdio.h>

int main()
{
    int num1 = 10;
    int num2 = 20;
    int num3 = 30;

    // 声明指针数组,存储指向 int 类型数据的指针
    int* arr;
    int **p = arr;
    arr = &num1;// 将指针指向变量 num1
    arr = &num2;// 将指针指向变量 num2
    arr = &num3;// 将指针指向变量 num3

    // 指针数组:存储3个char类型指针
    char *str_array = {"Hello", "World", "C"};

    // 通过指针数组访问存储的指针并输出对应的值
    for (int i = 0; i < 3; i++)
    {
      printf("*arr[%d]:%d, **p:%d\n", i, *arr, **p);
      p++;
      printf("*str_array[%d]:%s\n", i, str_array);
      printf("*str_array[%d]:%d\n", i, *str_array);//字符串首字符的ASCII数值
    }

    return 0;
}

/*输出结果:
*prt=1, *arr=1
*(ptr + 2))=3
arr)=10
*prt=1
ptr:0x7fff864c8004, arr:0x7fff864c8000
*prt=10
ptr:0x7fff864c8008, arr:0x7fff864c8000
*prt=3
ptr:0x7fff864c800c, arr:0x7fff864c8000
*prt=4
ptr:0x7fff864c8010, arr:0x7fff864c8000
*prt=5
ptr:0x7fff864c8014, arr:0x7fff864c8000
address between prt and arr is 5
*/指针加减法

指针加减法允许我们将指针与一个整数相加/相减。加法/减法运算将根据指针所指向的数据类型来调整指针的位置。
如果是一个int,+1/-1的结果是增加/减少一个int的大小。
如果是一个char,+1/-1的结果是增加/减少一个char大小。
对于数组而言,可以通过指针加减法对其进行遍历。
指针与指针相减


[*]结果类型:两个指针相减的结果为 ptrdiff_t(定义于 ),表示有符号整数。
[*]计算方式:结果为两个指针之间的元素个数(以指针指向的类型大小为单位),而非字节数。

[*]例如:int *p 和 int *q 相差 16 字节,若 sizeof(int) = 4,则 q - p = 4(元素个数)。

[*]合法性条件:两个指针必须指向同一数组/同一字符串,否则行为未定义。
#include <stdio.h>
#include <stddef.h>

int main()
{
    //数组元素距离
    int arr = {1,2,3,4,5};
    int *start = &arr, *end = &arr;
    ptrdiff_t element_diff = end - start; //4,字符差
    ptrdiff_t byte_diff = (char*)end - (char*)start;//16,字节差
    printf("element_diff:%ld, byte_diff:%ld\n", element_diff, byte_diff);

    //字符串字符差
    char str[] = "Hello";
    char *p = str;
    while (*p) p++;
    ptrdiff_t char_diff = p - str;//5,字符差,这里本质是一个char arr[]
    printf("char_diff:%ld\n", char_diff);

    int Data;
    int (*p1) = Data;   // 行指针,指向第一行
    int (*p2) = Data + 2; // 行指针,指向第三行
    ptrdiff_t row_diff = p2 - p1; // 2(行数差)
    printf("row_diff:%ld\n", row_diff);
}
/*
测试结果:
element_diff:4, byte_diff:16
char_diff:5
row_diff:2
*/
[*]常见误区:直接相减结构体成员指针是未定义行为。如果想要计算结构体成员的偏移,应使用 offsetof 宏:
struct S { int a; double b; };
size_t offset = offsetof(struct S, b); // 正确获取偏移量
[*]非法情况:在C语言中,两个毫无关系的指针相减(即指向不同数组或非连续内存的指针相减)会导致未定义行为。C11标准规定:

[*]指针相减仅当两个指针指向同一个数组(或数组末尾之后的位置)时才合法。
[*]如果两个指针指向不同对象或无关内存,行为未定义,编译器可能:

[*]返回一个无意义的数值。
[*]导致程序崩溃。
[*]触发编译器优化后的意外行为。


指针数组和数组指针

指针数组


[*]定义:
一个数组,其每个元素都是指针,可以指向相同或不同类型的数据。
[*]声明方式:
type *array_name;

[*]type 是基类型(如 int, char 等)。
[*]size 是数组长度。

[*]特点:

[*]每个元素存储的是地址(指针)。
[*]内存大小 = size * sizeof(指针)(如64位系统中,每个指针占8字节)。

[*]应用场景:

[*]存储多个字符串(字符串数组)。
[*]动态分配多维数组的行指针。

#include <stdio.h>

int main()
{
    int num1 = 10;
    int num2 = 20;
    int num3 = 30;

    // 声明指针数组,存储指向 int 类型数据的指针
    int* arr;
    int **p = arr;
    arr = &num1;// 将指针指向变量 num1
    arr = &num2;// 将指针指向变量 num2
    arr = &num3;// 将指针指向变量 num3

    //指针数组:存储3个char类型指针
    char *str_array = {"Hello", "World", "C"};

    // 通过指针数组访问存储的指针并输出对应的值
    for (int i = 0; i < 3; i++)
    {
      printf("*arr[%d]:%d, **p:%d\n", i, *arr, **p);
      p++;
      printf("*str_array[%d]:%s\n", i, str_array);
      printf("*str_array[%d]:%d\n", i, *str_array);//字符串首字符的ASCII数值
    }

    return 0;
}
/*运行结果:
*arr:10, **p:10
*str_array:Hello
*str_array:72
*arr:20, **p:20
*str_array:World
*str_array:87
*arr:30, **p:30
*str_array:C
*str_array:67
*/使用指针数组存储不同类型的数据

理论上,指针数组的确可以存储不同类型的数据,比如使用void*万能指针或者干脆存储结构体。但直接使用 void* 会绕过类型检查,需程序员自行保证类型正确性,而且不加注释的话有点比较难维护。
#include <stdio.h>

int main()
{
    int a = 1;
    float b = 2.5;
    char c = 'T';

    // 声明指针数组,类型为void*
    void* arr;
    arr = &a;
    arr = &b;
    arr = &c;

    printf("arr:%d\n", *(int*)arr);
    printf("arr:%f\n", *(float*)arr);
    printf("arr:%c\n", *(char*)arr);

    //测试程序,这里不考虑字节对齐了
    typedef struct
    {
      int a;
      float b;
      char c;
    }Data;

    Data data1 = {.a = 1,.b=1.1,.c = 'a'};
    Data data2 = {.a = 2,.b=2.2,.c = 'b'};

    Data* arr_data = {&data1, &data2};

    printf("arr_data:%f\n", arr_data->b);
    printf("arr_data:%c\n", ar
r_data->c);
    return 0;
}
/*
测试结果
arr:1
arr:2.500000
arr:T
arr_data:1.100000
arr_data:b
*/数组指针


[*]定义:
一个指针,指向一个完整数组(而非单个元素)。
[*]声明方式:
type (*pointer_name);

[*]type 是数组元素的类型。
[*]size 是所指数组的长度(必须明确指定)。

[*]特点:

[*]指针本身占一个指针的大小(如8字节)。
[*]对指针进行 +1 操作时,会跳过整个数组的长度(size * sizeof(type) 字节)。

[*]应用场景:

[*]处理多维数组(如二维数组的行指针)。
[*]函数参数中传递多维数组。

#include <stdio.h>
int main()
{
    int arr =
    {
      {1, 2, 3, 4},
      {5, 6, 7, 8},
      {9, 10, 11, 12}
    };
    // 定义数组指针,指向含有4个int的数组(即一行)
    int (*ptr) = arr; // 指向第一行
    for(int i = 0; i < 3; i++)
    {
      printf("ptr address:%p\n", ptr);
      for(int j = 0; j < 4;j++)
      {
            printf("arr[%d]:%d, address:%p\n", j, (*ptr), &(*ptr));
      }   
      ptr++;
    }
}总结

特性指针数组数组指针本质数组,元素是指针指针,指向整个数组声明int *arr;int (*arr);内存占用5 * sizeof(int*)(如40字节)sizeof(int*)(如8字节)加减操作按指针大小移动(如8字节)按整个数组大小移动(如5*sizeof(int))典型用途字符串数组、动态多维数组的行管理二维数组的行操作、函数传参二维数组和指针

对于arr表示的二维数组而言,可以用指针去表示:

[*]arr 是指向第一行的指针(类型是 int (*))
[*]arr 是指向第i行第一个元素的指针(类型是 int *)
[*]&arr 是第i行第j列元素的地址
其元素访问的方式如下:

[*]数组下标法:arr
[*]指针表示法:*(*(arr + i) + j)
看下面的代码示例。本质上,二维数组是一个指向一维数组的指针,只是因为二维数组中内存是连续的,所以可以通过指针的加法来表示后面的几个一维数组。
#include <stdio.h>

int main() {
    int arr = {
      {1, 2, 3, 4},
      {5, 6, 7, 8},
      {9, 10, 11, 12}
    };
   
    //用数组指针表示一个一维数组
    int (*ptr) = arr;
    for(int i = 0; i < 3; i++) {
      for(int j = 0; j < 4; j++) {
            printf("%d %d ", *(*(ptr + i) + j), *(*(arr + i) + j));
            //以上两种结果一样,说明二维数组名不是二级指针,它是指向一维数组的指针
      }
      printf("\n");
    }
   
    return 0;
}
/*
结果:
1 1 2 2 3 3 4 4
5 5 6 6 7 7 8 8
9 9 10 10 11 11 12 12
*/


来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 指针与数组