指针的减法运算和指针在函数间的传递
来源:互联网 发布:python adodb下载 编辑:程序博客网 时间:2024/05/22 11:40
昨晚在微信上和老友讨论c语言指针的相关问题,得到点收获。他和我一样,都是2016年6月份毕业的,从事的也都是嵌入式软件开发工作。下来,我尝试着将讨论的内容讲清楚。
说明,下面测试程序会用到两个宏: NUM和ERRP,它们的原型为:
#define ERRP(con, ret, ...) do \ if (con){ \ printf(__VA_ARGS__); \ ret; \}while(0)#define NUM 5
1. 指针的减法运算
int* intPoint = NULL; void* voidPoint = NULL; intPoint = (int* )malloc(sizeof(int) * NUM); ERRP(NULL == intPoint, return -1, "intPoint: malloc memory failed!!\n"); voidPoint = malloc(sizeof(int) * NUM); ERRP(NULL == voidPoint, return -1, "voidPoint: malloc memory failed!!\n"); printf("voidPoint = %p, voidPoint + sizeof(int) * 5 = %p\n", voidPoint, voidPoint + sizeof(int) * 5); printf("(voidPoint + 5 * sizeof(int)) - voidPoint = %d\n", (voidPoint + 5 * sizeof(int)) - voidPoint); printf("intPoint = %p, intPoint + sizeof(int) * 5 = %p\n", intPoint, intPoint + sizeof(int) * 5); printf("(intPoint + 5 * sizeof(int)) - intPoint = %d\n", (intPoint + 5 * sizeof(int)) - intPoint);
运行结果:
首地址为voidPoint(0x8350020)的堆空间,其第5个元素的首地址为0x8350034,它们之间相差的地址偏移是20;
首地址为intPoint(0x8350008)的堆空间,其第5个元素的首地址为0x8350058,它们之间相差的地址偏移是80但是打印出来的地址偏移值却都是20;
也许你会认为上面的减法运算可以进行数据的结合律得到的确实是20,但是在括号内的值保存在一个指针变量,然后再减法运算,得到的结果也是如此。所以可以推论,指针的减法运算并非单纯数值上的相减,而是:
两个指针相减,其结果是两个指针之间元素的数目。前提是两个指针指向同一个数组,也就是指向空间连续的一段内存空间。
也就是说:
(1) voidPoint类型的指针,其最后一个元素的首地址减首元素的首地址得到的20表示有20个void型数据元素,因为void型是空类型,编译器默认它占据1字节;
(2) intPoint类型的指针,其最后一个元素的首地址减首元素的首地址得到的20表示有20个int型数据元素,因为int型占据4字节,所以地址实际上相差80字节
由此可见,若通过指针对动态分配的内存空间进行赋值,那么就需要考虑指针的类型了。
我们知道,对指针加减1实质是加减指针的步长:int型指针加减4字节,char/void型指针加减1字节,所以
for (i = 0; i < NUM; i++) *(intPoint + i) = i * 6; //intPoint是int*型,对该指针加1等于加sizeof(i) //voidPoint是void*型,对该指针加1等于加sizeof(void),即1 //void*型指针可以接受任意类型的指针,这里是接受int型指针,所以对这块内存赋值的时候需要乘以sizeof(int) //这样指针才能sizeof(int)为单位进行偏移 for (i = 0; i < NUM; i++) *(int* )(voidPoint + i * sizeof(int) ) = i * 5;
2. 指针在函数间的传递
指针在函数间的传递,在之前的文章变量在函数间的传递有详细讲解过,但都是文字的解析,现想通过画图的形式,理解起来会更简单。
还是这个程序:
int main(void){ int *main_point = NULL; main_point = (int *)malloc(sizeof(int) * NUM); ERRP(NULL == main_point, return -1, "Malloc Memory failed!!\n"); //... //调用FreeMem()函数来释放堆空间,并将指向该堆空间的指针置为NULL return 0; }
FreeMem要实现的功能是释放堆空间和将指向该堆空间的指针置为NULL,那么FreeMem函数要什么原型?其实现体又为什么?
2.1 假设FreeMem函数的原型为FreeMem(int free_point)
FreeMem(int free_point)的实现体为:
int FreeMem(int* free_point){ free(free_point); free_point = NULL; return 0;}
main函数对FreeMem函数的调用是
FreeMem(main_point);
假设动态分配的堆的首地址为0x60003,那么
(1)指针main_point为:
(2)当将指针main_point传给free_point之后,free_point为:
free_point和main_point都为动态空间的地址0x60003,所以执行free(free_point)或者free(main_point)都能起到一样的效果,
即断开与动态内存的链接。
(3)但是free_point = NULL 和main_point = NULL的效果则是不同的:
一个是0x10012地址为NULL,一个是0x10036地址为NULL,所以free_point = NULL不能使得原本指向动态分配的内存的首地址的指针指向NULL。
因此FreeMem函数的原型不能是接收指针,而是要接收指针的地址,即原型为FreeMem(int** free_point)。
2.2 FreeMem(int** free_point)的实现方式1
int FreeMem(int** free_point) //free_point是一个二级指针,它存放main_point的地址{ free(*free_point); *free_point = NULL; return 0;}
main函数对FreeMem的调用是
FreeMem(&main_point);
(1) 当main函数将main_point的地址传给free_point时候,free_point的内容为:
free_point等于0x10012,即main_point的地址,那么*free_point得到的0x60003,,它是即动态内存的首地址,
因此free(*ree_point)是正确的;
(2) *free_point = NULL等价于main_point指向了NULL,所以这是正确的。
2.3 FreeMem(int** free_point)的实现方式2
int FreeMem(int** free_point){ int* my_point = *free_point; free(my_point); my_point = NULL; return 0;}
同理,main函数对FreeMem的调用是
FreeMem(&main_point);
(1) 定义一个局部指针my_point,my_point的内容为:
即my_point为main_point,它是0x60003,为动态空间的首地址,free(my_point)的使用没错;
(2) my_point = NULL等价于
也就是并不等价于main_point指向了NULL,所以不能这么写。
综上:FreeMem函数的原型为
int FreeMem(int** free_point){ free(*free_point); *free_point = NULL; return 0;}//或者int FreeMem(int** free_point){ int* my_point = *free_point; free(my_point); *free_point = NULL; return 0;}
- 指针的减法运算和指针在函数间的传递
- 指针与指针之间的减法运算以及比较运算
- 传递指针和传递指针的指针
- 函数指针的传递
- 指针减法运算
- 函数指针传递和全局指针的测试
- c语言--二级指针在函数间的传递和使用
- 两个指针变量的减法
- 成员函数指针作为参数传递给其他函数和普通函数指针的传递
- C 函数值传递和指针传递的效率问题
- 函数指针的传递问题
- 关于函数的指针传递
- 数组指针和指针数组以及二维数组的函数间传递与返回
- 指针型形参和指针引用的传递
- 指针转换 和 指针在函数迭代中的传递
- 指针的数据类型和指针运算小结
- 函数指针和指针函数 指针的指针
- 对函数指针,指针函数和指针的指针理解。
- codeforce 760 B Frodo and pillows 二分搜索
- 高通Trustzone and QSEE
- onItemClick和onItemSelected方法的4个参数详细解析
- Python之"本固枝荣"篇
- AVL二叉搜索树的Rotation
- 指针的减法运算和指针在函数间的传递
- spring整合dubbo+zookeeper搭分布式服务,简单案例
- TCP 和 UDP 的定义、区别及模拟演示
- ButterKnife使用之Activity与Fragment
- CVE-2017-0012:Microsoft Edge / IE 浏览器欺骗漏洞(昨日补丁)
- PHP之PDO连接数据库实例
- 13 Slot PCI Expansion – RAS
- 胜者树(线段树RMQ)——Luogu1816 忠诚
- Zero configuration networking in OpenWrt