今天在群里,有人问道:
char s[]="123456789";
char d[]="123";
strcpy(d,s);
printf("%s,%s\n",d,s); 输出什么?VC6上试过输出123456789,56789
这个问题其实就是考虑:上面字符串s和d的内存分配是123456789\0123\0还是123\0123456789\0,
根据结果是123\0123456789\0分配。为什么是这样分配呢,不是根据先后顺序分配,先分配s在分配d吗?
于是,我在ads(arm编译器,ANSI/ISO Standard C)定义了四个数组(全局的)
unsigned char s[]="123456789";
unsigned char d[]="123";
unsigned char sendstr[]="I am xiajiashan!\n";
unsigned char e[]="1235";
然后,编译,查看编译map图表如下:
按顺序,这四个数组在内存里面的排列顺序应该是s,d,sendstr,e但是,根据ads的编译结果却是d,e,s,sendstr。
这个时候,我怀疑是不是编译器根据我使用顺序来编译呢!但我在main里面的调用顺序是:
SendStr_Uart0(sendstr,sizeof(sendstr));
SendStr_Uart0(e,sizeof(e));
SendStr_Uart0(s,sizeof(s));
SendStr_Uart0(d,sizeof(d));
所以,从我自己的实验,及上面问题的现象推出:
编译器(不管是VC6.0,还是ads,我想其他编译器也一样)会从小的开始编译,分配内存。
我猜是这样的,因为编译器在编译一个变量前(特别是这种数组,所以在VC程序里面是不建议把数组定义成全局变量的,一般是先定义一个指针,然后用malloc申请你这个数组所需大小的内存,然后赋值给上面的指针),首先是判断要分配多大的内存。
假设你给了一个1G大的数组,当然有点夸张,不过夸张才能说明问题。编译器也要去编译,但是这个时候他是先满足小内存的申请(其实把全局数组,就是在编译的时候分配内存,malloc是在程序运行时分配内存);
网友1评论:我想这里应和编译器的分配方式没有太大的关系. 当我们把 10bytes 的 str1 copy to 4 bytes 的str2 上, 一定会有异常出现. 首先我们应编程时主动意识尽量避免. 其次通过上面的讨论, 表达了让人饶有兴致的2个现象: 1. strcpy 的 s_len > d_len 时, strcpy 这个函数会破坏 s. 这个让人好奇, 需要检查 strcpy 的代码. 2. printf 在找不到字符串数组的结尾符 \0 时, 也不会自行根据最大长度停止, 而是继续打印直到下一个不可预知的 \0? 我们应避免超过定义大小的 strcpy.
网友2评论:网友1的评论比较合理,不要过分的在意编译器会怎么分配,这种本来就是越界异常问题,不同编译器可能有不同的结果,可能先后写某些代码也会产生不同的结果。楼主的一些看法猜测是不可靠的。想用这种方式作特殊问题处理很危险,用多变量结构体化的方式确定位置,才能可靠知道越界走向覆盖。
网友3评论:
在C的函数中,变量的空间在堆栈中分配,而堆栈的地址是由高至低的,而你的指针是从低地址向高地址走的。
你可以在函数中定义两个变量,并分别打印其分配的内存地址就明白了。