为什么返回指向局部声明变量的指针为空而不是指向堆栈中位置的指针?
Why returning pointer to locally declated variable is null in place of pointer to location in stack?
在下面的代码片段中,str_s 不应指向堆栈中的某个位置。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char* fun_s(){
char str[8]="vikash";
printf("s :%p\n", str);
return str;
}
char* fun_h(){
char* str = (char*)malloc(8);
printf("h :%p\n", str);
strcpy(str, "vikash");
return str;
}
int main(){
char* str_s = fun_s();
char* str_h = fun_h();
printf("s :%p\nh :%p\n", str_s, str_h);
return 0;
}
我知道 fun_s 的 return 有问题,这个指针的内容不可信,但根据我的理解,它应该指向堆栈中的某个位置而不是零?
我在控制台中得到以下输出。你能解释一下为什么第三行打印 (nil) 而不是 0x7ffce7561220
s :0x7ffce7561220
h :0x55c49538d670
s :(nil)
h :0x55c49538d670
GCC 版本
gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
OS :Ubuntu 18.04.3 LTS
大多数现代编译器检测 return 指向局部变量的指针,实际上 return NULL(一般来说,对于 UB 更积极,方法是 - 让程序尽快失败或 "make the UB possible to detect runtime" 在这种情况下)https://godbolt.org/z/pDUXmm
您的编译器故意从该函数中注入空 return 值。我没有可用的 gcc 7.4,但我有 7.3,我认为结果类似:
将 fun_s
编译为程序集可提供以下内容:
.LC0:
.string "s :%p\n"
fun_s:
push rbp
mov rbp, rsp
sub rsp, 16
movabs rax, 114844764957046
mov QWORD PTR [rbp-8], rax
lea rax, [rbp-8]
mov rsi, rax
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov eax, 0 ; ======= HERE =========
leave
ret
注意 eax 的硬设置为零,当 return 返回给调用者时,它将保存结果指针。
将 str
设为静态可提供以下内容:
.LC0:
.string "s :%p\n"
fun_s:
push rbp
mov rbp, rsp
mov esi, OFFSET FLAT:str.2943
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov eax, OFFSET FLAT:str.2943
pop rbp
ret
简而言之,您的编译器正在检测本地地址 return 并将其重写为 NULL。这样做可以防止以后对上述地址的任何恶意使用(例如:内容注入攻击)。
我看不出有什么理由允许编译器不这样做。我相信语言纯粹主义者会证实或拒绝这种怀疑。
在下面的代码片段中,str_s 不应指向堆栈中的某个位置。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char* fun_s(){
char str[8]="vikash";
printf("s :%p\n", str);
return str;
}
char* fun_h(){
char* str = (char*)malloc(8);
printf("h :%p\n", str);
strcpy(str, "vikash");
return str;
}
int main(){
char* str_s = fun_s();
char* str_h = fun_h();
printf("s :%p\nh :%p\n", str_s, str_h);
return 0;
}
我知道 fun_s 的 return 有问题,这个指针的内容不可信,但根据我的理解,它应该指向堆栈中的某个位置而不是零? 我在控制台中得到以下输出。你能解释一下为什么第三行打印 (nil) 而不是 0x7ffce7561220
s :0x7ffce7561220
h :0x55c49538d670
s :(nil)
h :0x55c49538d670
GCC 版本
gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
OS :Ubuntu 18.04.3 LTS
大多数现代编译器检测 return 指向局部变量的指针,实际上 return NULL(一般来说,对于 UB 更积极,方法是 - 让程序尽快失败或 "make the UB possible to detect runtime" 在这种情况下)https://godbolt.org/z/pDUXmm
您的编译器故意从该函数中注入空 return 值。我没有可用的 gcc 7.4,但我有 7.3,我认为结果类似:
将 fun_s
编译为程序集可提供以下内容:
.LC0:
.string "s :%p\n"
fun_s:
push rbp
mov rbp, rsp
sub rsp, 16
movabs rax, 114844764957046
mov QWORD PTR [rbp-8], rax
lea rax, [rbp-8]
mov rsi, rax
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov eax, 0 ; ======= HERE =========
leave
ret
注意 eax 的硬设置为零,当 return 返回给调用者时,它将保存结果指针。
将 str
设为静态可提供以下内容:
.LC0:
.string "s :%p\n"
fun_s:
push rbp
mov rbp, rsp
mov esi, OFFSET FLAT:str.2943
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov eax, OFFSET FLAT:str.2943
pop rbp
ret
简而言之,您的编译器正在检测本地地址 return 并将其重写为 NULL。这样做可以防止以后对上述地址的任何恶意使用(例如:内容注入攻击)。
我看不出有什么理由允许编译器不这样做。我相信语言纯粹主义者会证实或拒绝这种怀疑。