ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

指针的算术运算详解

指针的算术运算详解 1. 引言指针是 C/C 语言中最重要也最难以掌握的概念之一。很多初学者在理解指针时往往只关注指针的声明、赋值和解引用却忽略了指针的算术运算。实际上指针的算术运算Pointer Arithmetic是理解数组、字符串和动态内存管理的核心基础。本文将系统性地讲解指针算术运算的规则、原理和典型应用帮助你彻底掌握这一关键技能。2. 指针算术运算的基础规则2.1 指针的本质指针本质上是一个变量它存储的是另一个变量的内存地址。在 32 位系统上指针占 4 个字节在 64 位系统上指针占 8 个字节。但指针算术运算的关键在于指针加减运算的结果取决于指针所指向的数据类型的大小而不是简单地加减 1 个字节。2.2 指针加减整数的规则指针p加上整数n结果是指向地址p n * sizeof(类型)的新指针。也就是说指针每加 1实际地址增加的是sizeof(所指向类型)个字节。intarr[5]{10,20,30,40,50};int*parr;// p 指向 arr[0]int*qp2;// q 指向 arr[2]地址偏移 2 * sizeof(int) 8 字节2.3 指针减法指针减去整数n结果是指向地址p - n * sizeof(类型)的新指针。int*rq-1;// r 指向 arr[1]2.4 指针相减两个同类型的指针相减结果是它们之间相差的元素个数不是字节数。int*parr;// 指向 arr[0]int*qarr4;// 指向 arr[4]longdiffq-p;// diff 4表示相差 4 个元素3. 指针算术运算的详细原理3.1 为什么指针加 1 不是加 1 个字节这是指针算术运算最容易让人困惑的地方。假设int类型占 4 个字节int *p指向地址0x1000那么p 1指向的是0x1004而不是0x1001。这是因为编译器在编译时已经知道p指向的是int类型所以会自动乘以sizeof(int)。#includestdio.hintmain(){intarr[3]{1,2,3};int*parr;printf(p 的地址: %p\n,(void*)p);printf(p1 的地址: %p\n,(void*)(p1));printf(p2 的地址: %p\n,(void*)(p2));return0;}运行结果示例地址可能因系统而异p 的地址: 0x7ffc12345670 p1 的地址: 0x7ffc12345674 p2 的地址: 0x7ffc12345678可以看到每次加 1地址增加 4 个字节sizeof(int)。3.2 不同类型指针的算术运算差异指针算术运算的结果与指针指向的类型密切相关。下面通过对比来加深理解#includestdio.hintmain(){charc_arr[4]{a,b,c,d};inti_arr[4]{1,2,3,4};doubled_arr[4]{1.0,2.0,3.0,4.0};char*cpc_arr;int*ipi_arr;double*dpd_arr;printf(char 指针 1 偏移: %ld 字节\n,(long)((char*)(cp1)-(char*)cp));printf(int 指针 1 偏移: %ld 字节\n,(long)((char*)(ip1)-(char*)ip));printf(double 指针 1 偏移: %ld 字节\n,(long)((char*)(dp1)-(char*)dp));return0;}输出结果char 指针 1 偏移: 1 字节 int 指针 1 偏移: 4 字节 double 指针 1 偏移: 8 字节3.3 指针算术运算的合法性指针算术运算并不是任意指针都可以进行的它有以下限制指针加减整数只能对指向数组元素的指针进行或指向单个对象时加 1 指向下一个对象。指针相减两个指针必须指向同一个数组或同一个对象之后的一个位置。指针不能相加两个指针相加没有意义C 语言也不允许这样做。指针不能乘除指针不支持乘法和除法运算。inta10,b20;int*paa,*pbb;// int *pc pa pb; // 错误指针不能相加// int *pd pa * 2; // 错误指针不能乘4. 指针算术运算的典型应用4.1 遍历数组指针算术运算最常见的应用就是遍历数组比使用下标更灵活、更高效。#includestdio.hintmain(){intarr[5]{10,20,30,40,50};int*p;// 使用指针遍历数组for(parr;parr5;p){printf(%d ,*p);}printf(\n);return0;}4.2 指针与数组下标的关系在 C 语言中arr[i]本质上等价于*(arr i)。理解这一点就能明白为什么数组下标可以从 0 开始——因为arr[0]就是*(arr 0)即数组首元素。#includestdio.hintmain(){intarr[3]{100,200,300};printf(arr[1] %d\n,arr[1]);// 下标访问printf(*(arr1) %d\n,*(arr1));// 指针访问等价return0;}4.3 计算数组元素个数利用指针相减可以方便地计算数组中元素的个数。#includestdio.hintmain(){intarr[8]{0};int*startarr;int*endarr8;longcountend-start;printf(数组元素个数: %ld\n,count);// 输出 8return0;}4.4 字符串处理指针算术运算在字符串处理中应用非常广泛例如实现字符串长度计算#includestdio.hsize_tmy_strlen(constchar*s){constchar*ps;while(*p!\0){p;}returnp-s;// 指针相减得到字符个数}intmain(){charstr[]Hello, Pointer!;printf(字符串长度: %zu\n,my_strlen(str));return0;}4.5 动态内存管理指针算术运算在动态内存分配中同样重要常用于在堆上分配的内存中定位元素。#includestdio.h#includestdlib.hintmain(){int*arr(int*)malloc(5*sizeof(int));if(arrNULL){return1;}// 使用指针算术运算给数组赋值for(inti0;i5;i){*(arri)i*i;}// 打印结果for(inti0;i5;i){printf(arr[%d] %d\n,i,*(arri));}free(arr);return0;}5. 指针算术运算的常见误区5.1 误区一指针加 1 就是地址加 1这是最常见的误区。如前所述指针加 1 实际地址增加的是sizeof(类型)字节而不是 1 字节。5.2 误区二对 void 指针进行算术运算void *指针没有类型信息编译器不知道它指向的对象大小因此不能对void *指针直接进行算术运算。void*pmalloc(100);// void *q p 1; // 错误void 指针不能进行算术运算如果需要操作必须先转换为具体类型的指针char*cp(char*)p;char*qcp1;// 正确5.3 误区三指针相减得到的是字节数指针相减得到的是元素个数不是字节数。如果需要字节数需要乘以sizeof(类型)。intarr[5]{0};int*parr;int*qarr3;longelementsq-p;// 3元素个数longbytes(char*)q-(char*)p;// 12字节数5.4 误区四越界访问指针算术运算很容易导致越界访问这是 C 语言中常见的 bug 来源。指针可以指向数组最后一个元素之后的一个位置称为尾后指针但不能解引用它。intarr[3]{1,2,3};int*endarr3;// 合法指向尾后位置// int x *end; // 非法解引用尾后指针是未定义行为6. 指针算术运算与多维数组6.1 二维数组的指针算术对于二维数组指针算术运算的规则同样适用但需要理解数组的数组这一概念。#includestdio.hintmain(){intmatrix[2][3]{{1,2,3},{4,5,6}};// matrix 是 int[2][3] 类型matrix 1 指向第二行int(*row)[3]matrix1;// 指向第 2 行printf(第二行第一个元素: %d\n,(*row)[0]);// 输出 4// 访问 matrix[1][2]printf(matrix[1][2] %d\n,*(*(matrix1)2));// 输出 6return0;}6.2 指针数组与数组指针理解指针数组和数组指针的区别有助于正确使用指针算术运算// 指针数组数组的每个元素是指针int*ptr_arr[3];// 数组指针指向数组的指针int(*arr_ptr)[3];7. 总结指针的算术运算虽然看似简单但背后蕴含着 C 语言内存模型的深刻设计。掌握以下几点就能灵活运用指针算术运算指针加/减整数n实际地址偏移n * sizeof(类型)字节。两个同类型指针相减结果是元素个数差。指针算术运算必须基于同一数组或同一对象进行避免越界。void *指针不能直接进行算术运算需先转换为具体类型。指针算术运算在数组遍历、字符串处理、动态内存管理等场景中应用广泛。理解指针算术运算是通往 C/C 高级编程的必经之路。建议读者在理解规则后多动手编写和调试代码在实践中巩固这些概念。
返回列表