talk is cheap,来观摩一下简单代码

2022-05-27 09:35:02 +08:00
 microxiaoxiao
void test(char *p, int len)
{
snprintf(out,len,"%s","hello world");
}
int main()
{
char res[512];
{
char result[1024];
test(result,1024);
}
printf("result %s\n",res);
}

问题:在 ubuntu20.04 上面会输出 hello world 。
其他环境;公司自研系统正常,suse12 正常
问题,为啥 ubuntu 有这种骚操作->把 res 和 result 搞成同一个地址。
手机打字不容易,不好排版,见谅。
5024 次点击
所在节点    程序员
32 条回复
ksco
2022-05-27 10:45:24 +08:00
@DianQK #17 不写 UB 的代码就好了,纠结 UB 属于费力不讨好。
mingl0280
2022-05-27 10:58:40 +08:00
使用未初始化的变量是未定义行为,无讨论价值。
建议锁帖。
codehz
2022-05-27 10:59:08 +08:00
@DianQK 局部变量哪有什么“释放”的概念。。。
DianQK
2022-05-27 11:17:38 +08:00
@codehz 我表述错了
sudoy
2022-05-27 11:20:27 +08:00
没排版的代码看着好难受
microxiaoxiao
2022-05-27 11:24:06 +08:00
大家都很有见地,我就不一一回复了,打字不方便。感谢大家。这个问题综合大家说的,我的初步结论是这样的:char res[],过程其实并不分配内存,只有在真正引用的过程才会分配内存,咋们平时说的初始化和 memset print 都是针对它的引用。行为其实是定义了的,有些编译器可能给它分配到的是为使用过的地址,有些分配到的是未使用过的地址,表现为未定义,这样就会有所谓的数据随机化。感谢给汇编的思路。
smdbh
2022-05-27 11:42:15 +08:00
作用域问题把
weiwenhao
2022-05-27 12:02:06 +08:00
测试了一下,确实是 26 楼主这样的结论。

发现这里大佬这么多,刚好也有一个疑惑点,在所有变量都初始化的情况下
```
int8_t a = 1
{
int8_t b = 2
}
int8_t c = 3
```

类似这种情况,int8_t b 的作用域离开后不久没用了吗, int8_t b 和 int8_t c 完全可以使用同一段栈空间, 这样不是更节省空间吗, 但是实际上 gcc 编译器生成的代码是这样的, 为每个变量都分配了栈空间,完全没有考虑作用域? 这样做有啥好处吗
movb $1, -3(%rbp)
movb $2, -2(%rbp)
movb $3, -1(%rbp)
iamzuoxinyu
2022-05-27 13:01:23 +08:00
@weiwenhao C 没有 RAII 。只有作用域访问限制。
Caturra
2022-05-27 13:51:49 +08:00
虽然说不是一定要全部初始化,但你不初始化那么`res`的内容完全可以任由编译器去解释
我丢到 godbolt 上看确实不同的 gcc 版本结果不一样,生成的指令各不相同就不细看了

其中一种可以解释为:
1. 因为你用`result`前(调用`test`)的代码并没有用到`res`
2. gcc 打算把它放到`result`的前 512 字节中,既在`printf`前其实只有栈指针 rsp - 1024 而不是 1024+512
3. 而`res`从起始地址开始就因为`test`被塞入了 hello world ,所以也会莫名其妙输出 hello world

其实意思就是编译器把原来第 2 行的`res`定义优化了,放到原来第 4 行后面,栈指针可以省点偏移量

要避免这种 UB ,又舍不得`memset`整个`res`的话,可以用`res[0] = 0`,直接在首部塞入结束符
不过写这种代码我觉得还是别像资本家发工资那么抠门吧,直接 memset 就好了
DianQK
2022-05-27 14:06:12 +08:00
@weiwenhao O0 编译?改成 O1 这三个变量就会用同一个寄存器了
(不过我不知道怎么避免执行 DCE ,我加了个辅助函数

extern void use_i(int8_t);

int main() {
int8_t a = 1;
use_i(a);
{
int8_t b = 2;
use_i(b);
}
int8_t c = 3;
use_i(c);
}
secondwtq
2022-05-28 16:12:39 +08:00
@weiwenhao 这问题没法回答,因为编译器优化是个 best effort 的过程,你不能保证他一定会给你优化
不优化不一定是“有什么好处”,有可能只是触发了一个 bug 或者 edge case ,这是和具体实现相关的
现在的大多数 C/C++ 程序员被灌输的认知一般是“编译器很聪明”(甚至 JavaScript 等语言的程序员也出现了这种现象),但实际上编译器有时候也很笨。客观的来说,编译器优化只是一个暴力循环自动算法的过程而已。有时候灵,有时候不灵。

但是只要程序的可见行为符合标准,原则上就没问题

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/855579

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX