编写安全代码:数组和指针的本质以及何时不能互换
2016-01-25 16:30
120 查看
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
今天,主要研究一下数组和指针的本质,即它们到底是什么?(下文都是我自己的理解,也许不够准确,欢迎大家拍砖)请看下面的代码:#include <stdlib.h>
#include <stdio.h>
int main()
{
int array[4] = {0};
int *pointer = NULL;
int value = 0;
value = array;
value = &array;
value = array[0];
value = &array[0];
value = point;
value = &point;
return 0;
}
下面是该C程序的汇编代码: 1 0x08048394 <+0>: push %ebp
2 0x08048395 <+1>: mov %esp,%ebp
3 0x08048397 <+3>: sub $0x20,%esp
/*
第4行至第7行对应C代码int array[4] = {0};
这里说明数组只是一个同类型变量的内存空间的集合。
对应这个例子,在栈上申请了4个整形变量的空间。
*/
4 0x0804839a <+6>: movl $0x0,-0x14(%ebp)
5 0x080483a1 <+13>: movl $0x0,-0x10(%ebp)
6 0x080483a8 <+20>: movl $0x0,-0xc(%ebp)
7 0x080483af <+27>: movl $0x0,-0x8(%ebp)
/*
第8行对应int *point = NULL;
这里说明指针本身自己也是一个变量,同样占用了栈的空间。
在这处要对比数组的处理,数组的占用的空间实际上是数组中的元素所占用的。
在本例中,即array[0],array[1],array[2],array[3],而array本身实际上更像一个label。
*/
8 0x080483b6 <+34>: movl $0x0,-0x18(%ebp)
// 第9行对应int value = 0;
9 0x080483bd <+41>: movl $0x0,-0x4(%ebp)
/*
10~11行,对应代码value = array
因为array只是一个数组的名称,所以value = array就是将array数组的首元素的地址赋给value。
这个应该就是C标准的一个规则。这也是最合理的行为,因为如果不是取array数组的首地址,其它的行为更加没有意 义。
*/
10 0x080483c4 <+48>: lea -0x14(%ebp),%eax
11 0x080483c7 <+51>: mov %eax,-0x4(%ebp)
/*
12~13行,对应代码value = &array
从C语言的语义上看,就是取array的地址。
从汇编上看,也是取array数组的首元素的地址。
*/
12 0x080483ca <+54>: lea -0x14(%ebp),%eax
13 0x080483cd <+57>: mov %eax,-0x4(%ebp)
/*
14~15行, 对应代码value = array[0]
这里与上文不同,取数组的内容赋给value
*/
14 0x080483d0 <+60>: mov -0x14(%ebp),%eax
15 0x080483d3 <+63>: mov %eax,-0x4(%ebp)
/*
16~17行,对应代码value = &array[0]
很明显,取array[0]的地址赋给value
*/
16 0x080483d6 <+66>: lea -0x14(%ebp),%eax
17 0x080483d9 <+69>: mov %eax,-0x4(%ebp)
/*
18~19行,对应代码value = pointer;
按照上文的说明,指针本身就是一个变量,所以这里就是将这个变量的值赋给value,即pointer的值赋给value,
也就是0.
注意这里与value=array的区别。因为指针本身就是一个变量,所以value = pointer的合理语义就是将pointer的 值赋给value,而array本身并不是一个变量,所以value=array的语义,也是将array的数组首元素的地址赋给value。 */
18 0x080483dc <+72>: mov -0x18(%ebp),%eax
19 0x080483df <+75>: mov %eax,-0x4(%ebp)
/*
20~21行,对应代码value = &pointer;
取pointer的地址赋给value
*/
20 0x080483e2 <+78>: lea -0x18(%ebp),%eax
21 0x080483e5 <+81>: mov %eax,-0x4(%ebp)
/*
22~24行,对应代码value = *pointer;
取poniter保存的值,将这个值作为一个地址,然后取该地址的值赋给value。
在这里,pointer的值是0,所以这句话的语义是从pointer取出它的值0,然后从地址0取值赋给value。
当然,如果执行到这里,程序会crash。因为地址0为非法地址。
*/
22 0x080483e8 <+84>: mov -0x18(%ebp),%eax
23 0x080483eb <+87>: mov (%eax),%eax
24 0x080483ed <+89>: %eax,-0x4(%ebp)
22 0x080483f0 <+92>: mov $0x0,%eax
23 0x080483f5 <+97>: leave
24 0x080483f6 <+98>: ret
从上面的分析中,可以看出,尽管大多数情况下,数组和指针可以通用。但是由于对于&符号的不同的语义解释,在有&的情况下的代码,数组和指针是无法通用的。
请看下面的代码#include <stdlib.h>
#include <stdio.h>
static void show_str_pointer(const char **ppstr)
{
printf("%s\n", *ppstr);
}
int main()
{
char array[4] = "abc";
char *pointer = "abc";
show_str_pointer(&pointer);
show_str_pointer(&array);
return 0;
}
执行结果为:abc7�其中show_str_pointer(&pointer)的输出结果为我们所期望的,而show_str_pointer(&array)的输出为乱码。
其根本原因就是因为上面所揭示的数组和指针的本质不同, &array与array的语义相同。在这里指针与数组是不能互换的。&pointer为指针的地址,与show_str_pointer参数char**ppstr,指向指针的指针的变量类型相同。而&array仍然为数组地址,与参数char **ppstr的类型不符。
最后希望,大家从上面的分析和例子中,可以理解数组和指针的本质,以及何时它们之间可以互换,何时不能互换。
阅读(165) | 评论(0) | 转发(0) |
0
上一篇:如何检测网络端口是否被占用
下一篇:实践C++ 代码维护的思考
相关热门文章
我的第一个python程序之续集...
HTML5 音视频媒体处理相关...
FreeBSD下zfs: failed with er...
stopPropagation, preventDefa...
softRoCE/RDMA 安装与测试...
A sample .exrc file for vi e...
游标的特征
IBM System p5 服务器 HACMP ...
busybox的httpd使用CGI脚本(Bu...
Solaris PowerTOP 1.0 发布
linux dhcp peizhi roc
关于Unix文件的软链接
求教这个命令什么意思,我是新...
sed -e "/grep/d" 是什么意思...
谁能够帮我解决LINUX 2.6 10...
给主人留下些什么吧!~~
评论热议
作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
今天,主要研究一下数组和指针的本质,即它们到底是什么?(下文都是我自己的理解,也许不够准确,欢迎大家拍砖)请看下面的代码:#include <stdlib.h>
#include <stdio.h>
int main()
{
int array[4] = {0};
int *pointer = NULL;
int value = 0;
value = array;
value = &array;
value = array[0];
value = &array[0];
value = point;
value = &point;
return 0;
}
下面是该C程序的汇编代码: 1 0x08048394 <+0>: push %ebp
2 0x08048395 <+1>: mov %esp,%ebp
3 0x08048397 <+3>: sub $0x20,%esp
/*
第4行至第7行对应C代码int array[4] = {0};
这里说明数组只是一个同类型变量的内存空间的集合。
对应这个例子,在栈上申请了4个整形变量的空间。
*/
4 0x0804839a <+6>: movl $0x0,-0x14(%ebp)
5 0x080483a1 <+13>: movl $0x0,-0x10(%ebp)
6 0x080483a8 <+20>: movl $0x0,-0xc(%ebp)
7 0x080483af <+27>: movl $0x0,-0x8(%ebp)
/*
第8行对应int *point = NULL;
这里说明指针本身自己也是一个变量,同样占用了栈的空间。
在这处要对比数组的处理,数组的占用的空间实际上是数组中的元素所占用的。
在本例中,即array[0],array[1],array[2],array[3],而array本身实际上更像一个label。
*/
8 0x080483b6 <+34>: movl $0x0,-0x18(%ebp)
// 第9行对应int value = 0;
9 0x080483bd <+41>: movl $0x0,-0x4(%ebp)
/*
10~11行,对应代码value = array
因为array只是一个数组的名称,所以value = array就是将array数组的首元素的地址赋给value。
这个应该就是C标准的一个规则。这也是最合理的行为,因为如果不是取array数组的首地址,其它的行为更加没有意 义。
*/
10 0x080483c4 <+48>: lea -0x14(%ebp),%eax
11 0x080483c7 <+51>: mov %eax,-0x4(%ebp)
/*
12~13行,对应代码value = &array
从C语言的语义上看,就是取array的地址。
从汇编上看,也是取array数组的首元素的地址。
*/
12 0x080483ca <+54>: lea -0x14(%ebp),%eax
13 0x080483cd <+57>: mov %eax,-0x4(%ebp)
/*
14~15行, 对应代码value = array[0]
这里与上文不同,取数组的内容赋给value
*/
14 0x080483d0 <+60>: mov -0x14(%ebp),%eax
15 0x080483d3 <+63>: mov %eax,-0x4(%ebp)
/*
16~17行,对应代码value = &array[0]
很明显,取array[0]的地址赋给value
*/
16 0x080483d6 <+66>: lea -0x14(%ebp),%eax
17 0x080483d9 <+69>: mov %eax,-0x4(%ebp)
/*
18~19行,对应代码value = pointer;
按照上文的说明,指针本身就是一个变量,所以这里就是将这个变量的值赋给value,即pointer的值赋给value,
也就是0.
注意这里与value=array的区别。因为指针本身就是一个变量,所以value = pointer的合理语义就是将pointer的 值赋给value,而array本身并不是一个变量,所以value=array的语义,也是将array的数组首元素的地址赋给value。 */
18 0x080483dc <+72>: mov -0x18(%ebp),%eax
19 0x080483df <+75>: mov %eax,-0x4(%ebp)
/*
20~21行,对应代码value = &pointer;
取pointer的地址赋给value
*/
20 0x080483e2 <+78>: lea -0x18(%ebp),%eax
21 0x080483e5 <+81>: mov %eax,-0x4(%ebp)
/*
22~24行,对应代码value = *pointer;
取poniter保存的值,将这个值作为一个地址,然后取该地址的值赋给value。
在这里,pointer的值是0,所以这句话的语义是从pointer取出它的值0,然后从地址0取值赋给value。
当然,如果执行到这里,程序会crash。因为地址0为非法地址。
*/
22 0x080483e8 <+84>: mov -0x18(%ebp),%eax
23 0x080483eb <+87>: mov (%eax),%eax
24 0x080483ed <+89>: %eax,-0x4(%ebp)
22 0x080483f0 <+92>: mov $0x0,%eax
23 0x080483f5 <+97>: leave
24 0x080483f6 <+98>: ret
从上面的分析中,可以看出,尽管大多数情况下,数组和指针可以通用。但是由于对于&符号的不同的语义解释,在有&的情况下的代码,数组和指针是无法通用的。
请看下面的代码#include <stdlib.h>
#include <stdio.h>
static void show_str_pointer(const char **ppstr)
{
printf("%s\n", *ppstr);
}
int main()
{
char array[4] = "abc";
char *pointer = "abc";
show_str_pointer(&pointer);
show_str_pointer(&array);
return 0;
}
执行结果为:abc7�其中show_str_pointer(&pointer)的输出结果为我们所期望的,而show_str_pointer(&array)的输出为乱码。
其根本原因就是因为上面所揭示的数组和指针的本质不同, &array与array的语义相同。在这里指针与数组是不能互换的。&pointer为指针的地址,与show_str_pointer参数char**ppstr,指向指针的指针的变量类型相同。而&array仍然为数组地址,与参数char **ppstr的类型不符。
最后希望,大家从上面的分析和例子中,可以理解数组和指针的本质,以及何时它们之间可以互换,何时不能互换。
阅读(165) | 评论(0) | 转发(0) |
0
上一篇:如何检测网络端口是否被占用
下一篇:实践C++ 代码维护的思考
相关热门文章
我的第一个python程序之续集...
HTML5 音视频媒体处理相关...
FreeBSD下zfs: failed with er...
stopPropagation, preventDefa...
softRoCE/RDMA 安装与测试...
A sample .exrc file for vi e...
游标的特征
IBM System p5 服务器 HACMP ...
busybox的httpd使用CGI脚本(Bu...
Solaris PowerTOP 1.0 发布
linux dhcp peizhi roc
关于Unix文件的软链接
求教这个命令什么意思,我是新...
sed -e "/grep/d" 是什么意思...
谁能够帮我解决LINUX 2.6 10...
给主人留下些什么吧!~~
评论热议
相关文章推荐
- 实践C++ 代码维护的思考
- 我的C++技巧总结
- Thunderbird使用163邮箱smtp设置方法
- jdk环境变量配置:java.lang.NoClassDefFoundError
- 【转】Java命令行运行参数说明大全(偷来的)
- jdk环境变量配置:java.lang.NoClassDefFoundError
- 【转】Java命令行运行参数说明大全(偷来的)
- Thunderbird使用163邮箱smtp设置方法
- 【转】[Java]Stack栈和Heap堆的区别(终结篇)
- java.lang.UnsatisfiedLinkError:Unable to load
- [转]java的System.getProperty()方法可以获取的值
- java内存垃圾回收机制
- 【转】Java缓存框架 EhCache
- 【转】JAVA下的多线程程序造成系统时钟变快
- 【转】c语言static和extern的用法
- 【转】Java内存优化
- 【转】JAVA中的内联函数
- Java并发编程实践之ThreadLocal变量
- 深入研究java.lang.ThreadLocal类
- java性能测试工具