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 搞成同一个地址。
手机打字不容易,不好排版,见谅。
4823 次点击
所在节点    程序员
32 条回复
eote
2022-05-27 09:40:33 +08:00
c 里面数组初始化都不分配默认值的吗?那里面有啥都不奇怪
MoYi123
2022-05-27 09:46:11 +08:00
有没有可能你需要 memset 一下 res.
microxiaoxiao
2022-05-27 09:50:04 +08:00
@eote 不是说他默认值的问题,是 res 地址和 result 地址一致,导致在调用的时候被覆盖了。在 test 中写入的就是 res 的地址。
microxiaoxiao
2022-05-27 09:52:54 +08:00
@eote memset 和直接初始化当然应该,我的主要问题是它把地址优化为一个地址是不是不太对,因为如果,printf 之前对他进行付初值已经晚了
wuruorocks
2022-05-27 09:58:42 +08:00
第 3 行的 out 在哪里定义的
villivateur
2022-05-27 09:59:21 +08:00
在你的 test 函数里面,snprintf 的 len 参数 是 1024 ,而 "hello world" 又那么短,你确定不会发生内存访问错误?
shyrock
2022-05-27 09:59:32 +08:00
snprintf(out,len,"%s","hello world");

这句里面的 out 是啥?
microxiaoxiao
2022-05-27 10:01:27 +08:00
@wuruorocks 写错了,就是 result 手机打字麻烦,函数那个 p 写成 out
microxiaoxiao
2022-05-27 10:02:07 +08:00
@shyrock 手机打字,out 写成 p
microxiaoxiao
2022-05-27 10:03:08 +08:00
void test(char *p, int len)
{
snprintf(p,len,"%s","hello world");
}修改一下
eote
2022-05-27 10:06:20 +08:00
@microxiaoxiao

gcc 根本没申请 res[512]


--前略

main:
.LFB1:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $1040, %rsp
movq %fs:40, %rax
movq %rax, -8(%rbp)
xorl %eax, %eax
leaq -1040(%rbp), %rax
movl $1024, %esi
movq %rax, %rdi
call test
leaq -1040(%rbp), %rax
movq %rax, %rsi
leaq .LC2(%rip), %rdi
movl $0, %eax
call printf@PLT
movl $0, %eax
movq -8(%rbp), %rdx
xorq %fs:40, %rdx
je .L4
call __stack_chk_fail@PLT
-- 后略
chenxytw
2022-05-27 10:06:58 +08:00
和操作系统无关,和编译器有关系。
Ubuntu 20.04 默认是 gcc9, suse12 默认是 gcc7 。
未初始化的变量是 UB 行为,编译器理论上怎么做都没关系。
变量声明是没有先后顺序的。
ksco
2022-05-27 10:08:46 +08:00
初始化一下 res:char res[512] = {0};
senninha
2022-05-27 10:09:23 +08:00
编译器版本不一样导致的,看一下汇编代码吧
另外,在不会输出 hello world 的版本上 -O3 优化一下,估计也是 hello world.
lonestar
2022-05-27 10:16:09 +08:00
有意思。ubuntu 18/gcc 7.5 也是这样。即使 -O0 也还是这样。

考虑到在进入下级 { } 前这些局部变量并未使用,那么这样的行为也没有危害。只要在进入内部 { } 前随便引用一下 res ,编译器就会为 res 真正分配空间了。
DianQK
2022-05-27 10:27:08 +08:00
试了一下 clang 在 ubuntu 、macos 、arch 上都是正常的行为,而 gcc 在 -O0 -O1 下都会出错,-O2 -O3 是没问题的。
gcc 为这两个变量分配同一个地址没什么问题,但是看起来 -O0 下因为什么原因没有释放这部分内存(难不成是 bug )
DianQK
2022-05-27 10:37:46 +08:00
说错了,栈上的内存哪有什么释放的一说,如上所说,**当使用一个 char[] 时候应当进行初始化。如果没初始化就会进入未定义行为。**
事实上由于未定义行为,clang 一样会坏掉。
encro
2022-05-27 10:42:28 +08:00
你适合 rust ,哈哈
luassuns
2022-05-27 10:43:45 +08:00
suse gcc 12.1/clang14 没有这个问题
DianQK
2022-05-27 10:44:35 +08:00
有一个方式理解起来可能容易一些,把 res 和 result 当成一个普通的 int 变量。
由于在 { result } 的逻辑前面没有 res 的 define 和 use ,同时 { result } 完成后,result 不是 live (后面没有逻辑在使用 result )的,为了优化性能 result 自然可以复用 res 的地址 /寄存器。
但是 result 的内容在栈 /寄存器上没有释放(我是指把 result 用到的内存 /寄存器设置为默认值)。

(感觉自己表述的很烂,不知道我的思考是不是对的,应该差不多了
关键还是使用一个变量但没有初始化,那这个变量就可能指向一个脏的空间(别人用过的)。

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

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

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

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

© 2021 V2EX