马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
指针与数组
指针操作数组元素
在C语言中,数组名现实上就是一个指向数组首元素的指针。换句话说,可以把数组名视为指向了数组的第一个元素的内存所在。
例如,对于一个整型数组 int arry[5] = {1, 2, 3, 4, 5},我们可以通过数组名 arry 大概通过取指针操作符 &arry[0] 来获取指向数组第一个元素的指针。数组名字是数组的首元素所在,但它是一个常量。
我们可以通过指针访问数组元素大概遍历整个数组。- #include <stdio.h>
- int main()
- {
- int num1 = 10;
- int num2 = 20;
- int num3 = 30;
- // 声明指针数组,存储指向 int 类型数据的指针
- int* arr[3];
- int **p = arr;
- arr[0] = &num1; // 将指针指向变量 num1
- arr[1] = &num2; // 将指针指向变量 num2
- arr[2] = &num3; // 将指针指向变量 num3
- // 指针数组:存储3个char类型指针
- char *str_array[3] = {"Hello", "World", "C"};
- // 通过指针数组访问存储的指针并输出对应的值
- for (int i = 0; i < 3; i++)
- {
- printf("*arr[%d]:%d, **p:%d\n", i, *arr[i], **p);
- p++;
- printf("*str_array[%d]:%s\n", i, str_array[i]);
- printf("*str_array[%d]:%d\n", i, *str_array[i]);//字符串首字符的ASCII数值
- }
- return 0;
- }
- /*输出结果:
- *prt=1, *arr=1
- *(ptr + 2))=3
- arr[1])=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[5] = {1,2,3,4,5};
- int *start = &arr[0], *end = &arr[4];
- 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[3][4];
- int (*p1)[4] = Data; // 行指针,指向第一行
- int (*p2)[4] = 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 是基类型(如 int, char 等)。
- size 是数组长度。
- 特点:
- 每个元素存储的是所在(指针)。
- 内存大小 = size * sizeof(指针)(如64位系统中,每个指针占8字节)。
- 应用场景:
- 存储多个字符串(字符串数组)。
- 动态分配多维数组的行指针。
- #include <stdio.h>
- int main()
- {
- int num1 = 10;
- int num2 = 20;
- int num3 = 30;
- // 声明指针数组,存储指向 int 类型数据的指针
- int* arr[3];
- int **p = arr;
- arr[0] = &num1; // 将指针指向变量 num1
- arr[1] = &num2; // 将指针指向变量 num2
- arr[2] = &num3; // 将指针指向变量 num3
- //指针数组:存储3个char类型指针
- char *str_array[3] = {"Hello", "World", "C"};
- // 通过指针数组访问存储的指针并输出对应的值
- for (int i = 0; i < 3; i++)
- {
- printf("*arr[%d]:%d, **p:%d\n", i, *arr[i], **p);
- p++;
- printf("*str_array[%d]:%s\n", i, str_array[i]);
- printf("*str_array[%d]:%d\n", i, *str_array[i]);//字符串首字符的ASCII数值
- }
- return 0;
- }
- /*运行结果:
- *arr[0]:10, **p:10
- *str_array[0]:Hello
- *str_array[0]:72
- *arr[1]:20, **p:20
- *str_array[1]:World
- *str_array[1]:87
- *arr[2]:30, **p:30
- *str_array[2]:C
- *str_array[2]:67
- */
复制代码 利用指针数组存储差别类型的数据
理论上,指针数组的确可以存储差别类型的数据,比如利用void*全能指针大概干脆存储布局体。但直接利用 void* 会绕过类型查抄,需程序员自行包管类型正确性,而且不加注释的话有点比较难维护。- #include <stdio.h>
- int main()
- {
- int a = 1;
- float b = 2.5;
- char c = 'T';
- // 声明指针数组,类型为void*
- void* arr[3];
- arr[0] = &a;
- arr[1] = &b;
- arr[2] = &c;
- printf("arr[0]:%d\n", *(int*)arr[0]);
- printf("arr[1]:%f\n", *(float*)arr[1]);
- printf("arr[2]:%c\n", *(char*)arr[2]);
- //测试程序,这里不考虑字节对齐了
- 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[2] = {&data1, &data2};
- printf("arr_data[0]:%f\n", arr_data[0]->b);
- printf("arr_data[1]:%c\n", ar
- r_data[1]->c);
- return 0;
- }
- /*
- 测试结果
- arr[0]:1
- arr[1]:2.500000
- arr[2]:T
- arr_data[0]:1.100000
- arr_data[1]:b
- */
复制代码 数组指针
- 定义:
一个指针,指向一个完整数组(而非单个元素)。
- 声明方式:
- type (*pointer_name)[size];
复制代码
- type 是数组元素的类型。
- size 是所指数组的长度(必须明确指定)。
- 特点:
- 指针本身占一个指针的大小(如8字节)。
- 对指针进行 +1 操作时,会跳过整个数组的长度(size * sizeof(type) 字节)。
- 应用场景:
- 处理多维数组(如二维数组的行指针)。
- 函数参数中传递多维数组。
- #include <stdio.h>
- int main()
- {
- int arr[3][4] =
- {
- {1, 2, 3, 4},
- {5, 6, 7, 8},
- {9, 10, 11, 12}
- };
- // 定义数组指针,指向含有4个int的数组(即一行)
- int (*ptr)[4] = arr; // 指向第一行
- for(int i = 0; i < 3; i++)
- {
- printf("ptr address:%p\n", ptr);
- for(int j = 0; j < 4;j++)
- {
- printf("arr[0][%d]:%d, address:%p\n", j, (*ptr)[j], &(*ptr)[j]);
- }
- ptr++;
- }
- }
复制代码 总结
特性指针数组数组指针本质数组,元素是指针指针,指向整个数组声明int *arr[5];int (*arr)[5];内存占用5 * sizeof(int*)(如40字节)sizeof(int*)(如8字节)加减操作按指针大小移动(如8字节)按整个数组大小移动(如5*sizeof(int))典型用途字符串数组、动态多维数组的行管理二维数组的行操作、函数传参二维数组和指针
对于arr[j]表示的二维数组而言,可以用指针去表示:
- arr 是指向第一行的指针(类型是 int (*)[4])
- arr 是指向第i行第一个元素的指针(类型是 int *)
- &arr[j] 是第i行第j列元素的所在
其元素访问的方式如下:
- 数组下标法:arr[j]
- 指针表示法:*(*(arr + i) + j)
看下面的代码示例。本质上,二维数组是一个指向一维数组的指针,只是由于二维数组中内存是连续的,所以可以通过指针的加法来表示后面的几个一维数组。- #include <stdio.h>
- int main() {
- int arr[3][4] = {
- {1, 2, 3, 4},
- {5, 6, 7, 8},
- {9, 10, 11, 12}
- };
-
- //用数组指针表示一个一维数组
- int (*ptr)[4] = 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
- */
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |