strcpy 的 overlap 之迷

在进入正文之前先说几句题外话:最近有个新闻很火,是21岁才女被聘为跨国公司副总裁的事情。且不说新闻内容的真实程度有多少,其实我看到新闻时候就对里面的东西产生怀疑了,但是有几点是无法否认的:人家确实被聘为副总裁了,这点在Topcoder的网站上可以得到证实,至于这个副总裁手下有几个人,和事实没关系;人家确实有过三项发明专利,至于专利内容,和事实也没关系;人家确实是ACM主席成员,至于这个ACM和ACM/ICPC的缩略语的区别,和事实也没有关系;还有人家获得过29届ACM 国际大学生程序设计竞赛亚洲总决赛银牌,至于这个奖牌含金量有多高,和事实也没关系。

这几点已经让我崇拜有加了,Topcoder不是什么太牛的公司,但是起码也不烂;发明专利可能不是很高科技,但是我没有一个;ACM/ICPC主席团没有ACM主席高级,那不是废话吗?谁不知道啊,ACM/ICPC和ACM奖差远了,但是交通大学获奖后不也经常缩写吗?我连校ACM队都进不了,就别歧视人家奖牌了。对于新闻的炒做大家心里都有底,没有必要非得整出来《神话通常是由于低素质产生的》这种无耻文章来做所谓“揭露”,你可以写事实,但不要用这种口吻。还有一些别有用心的人拿所谓高考中考成绩说事,有意思吗?人家不容易。

读心理学专业能把计算机学那么好,还兼职干过那么多事情,能力啊,相比而言真让我受打击!更打击我的是今天在小百合上看到有认识的人说吴莹莹的男朋友是Stanford的王颖(2006 google 全球编程挑战赛亚军)............我只能 keep 无语了。

好了,下面进入正题,今天在小百合上和别人争了一番,争论是争论,总得留下点东西不,不然白费口舌了。

Program 1:
#include <stdlib.h>
int main(void)
{
char a[] = "abcd";
char b[] = "efg";
strcpy(b, "efghi");
printf("a=%s
", a);
printf("b=%s
", b);
return 0;
}
gcc build and run:
a=i
b=efghi

Program 2:
#include <stdlib.h>
int main(void)
{
char a[] = "abcd";
char b[] = "efgh";
strcpy(b, "efghij");
printf("a=%s
", a);
printf("b=%s
", b);
return 0;
}
gcc build and run:
a=abcd
b=efghij

ANSI C/ISO-IEC-9899-1999 在 7.21.2.3 The strcpy function 章节中指出

7.21.2.3 The strcpy function
Synopsis
1 #include <string.h>
char *strcpy(char * restrict s1,
const char * restrict s2);
Description
2 The strcpy function copies the string pointed to by s2 (including the terminating null
character) into the array pointed to by s1. If copying takes place between objects that
overlap, the behavior is undefined.
Returns
3 The strcpy function returns the value of s1.

所以上面的两个程序都有错误,但是我们这里只分析为什么会导致奇怪的输出。

第一个例子,运行时内存的内容是这样的(请注意方括号中的内存单元内容,冒号前是内存地址):
a,b赋完值:
0xbffd7778: 0xbffd7798 [0x00676665] [0x64636261] 0x006dff00

执行完strcpy:
0xbffd7778: 0xbffd7798 [0x68676665] [0x64630069] 0x006dff00

第二个例子:
a,b赋完值:
0xbfef6ca8: 0xbfef6cb8 0x080482b5 [0x68676665] 0x00000000
0xbfef6cb8: 0xbfef6cd8 0x0804843a [0x64636261] 0x006dff00

执行完stycpy:
0xbfef6ca8: 0xbfef6cb8 0x080482b5 [0x68676665 0x00006a69]
0xbfef6cb8: 0xbfef6cd8 0x0804843a [0x64636261] 0x006dff00

奇怪输出的原因找到了,是因为第一个程序把a,b放到连续的空间里导致了覆盖。那么为什么第二个程序中a,b不放到连续空间中呢?

再看看汇编代码,只关注相关部分:

第一个例子:
.LC1:
.string "efg"
...
movl .LC1, %eax
movl %eax, -28(%ebp)
...

第二个例子:
.LC1:
.string "efgh"
...
movl .LC1, %eax
movl %eax, -40(%ebp)
movb .LC1+4, %al
movb %al, -36(%ebp)
...
哦,这时候我们可以看到,由于efg只有三个byte,没有超过计算机的字长,所以eax容纳了它结尾的�,而且可以把b放到一个内存单元(word)中去,所以编译器就把a,b挨着放了。

而efgh有四个byte,一个内存单元(word)不能容纳全部字符串(放不下结尾的�),所以编译器就多为它预留了一段空间,既然留了,就留大点,就是4个word了。程序输出就变成正确的了。

所以就可以看到为什么没有放到连续空间中了。至于这个预留空间的策略,就不得而知了,那得看编译器的逻辑了。
Copyright © 2005-2006 Solrex Yang. All rights reserved.

《strcpy 的 overlap 之迷》上有1条评论

  1. 这个算是缓冲区溢出的问题,和strcpy的overlap不是一回事吧?

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注