面试问题:更改局部变量值而不发送参考或获取返回值
void func()
{
/*do some code to change the value of x*/
}
int main()
{
int x = 100;
printf("%d\n", x); // it will print 100
func(); // not return any value and reference of x also not sent
printf("%d\n", x); // it need to print 200
}
x
的值需要更改
答案 0 :(得分:6)
答案是您不能。
C编程语言无法做到这一点,而尝试这样做总是会导致不确定的行为。这意味着不能保证结果会是什么。
现在,您可能很想利用未定义的行为来破坏C的运行时系统并更改其值。但是,这是否以及如何工作完全取决于特定的执行环境。例如,当使用最新版本的GCC和clang编译代码并启用优化功能时,变量x
根本就不存在于输出代码中:没有与名称对应的内存位置,因此您可以'甚至直接修改原始内存地址。
实际上,以上代码大致产生以下汇编输出:
main:
subq $8, %rsp
movl $100, %esi
movl $.LC0, %edi
xorl %eax, %eax
call printf
xorl %eax, %eax
call func
movl $100, %esi
movl $.LC0, %edi
xorl %eax, %eax
call printf
xorl %eax, %eax
addq $8, %rsp
ret
如您所见,值100
是在printf
调用之前直接存储在ESI寄存器中的文字。即使您的func
试图修改该寄存器,该修改也会被编译后的printf
调用覆盖:
…
movl $200, %esi /* This is the inlined `func` call! */
movl $100, %esi
movl $.LC0, %edi
xorl %eax, %eax
call printf
…
无论如何,将其切成小方块,答案是:编译后的输出中没有x
变量,因此即使接受未定义的行为,也无法对其进行修改。您可以通过覆盖printf
函数调用来修改 output ,但这不是问题。
答案 1 :(得分:4)
通过C语言的设计,并通过局部变量的定义,您不能从外部访问它,而不能以某种方式使用它。
使局部变量可以被外界访问的一些方法:
extern
导出它。答案 2 :(得分:2)
答案是你不能,但是...
我完全同意@virolino和@Konrad Rudolph的观点,我不希望我对这个问题的“解决方案”被视为最佳实践,但是由于这是一种挑战,因此可以提出这种方法。
#include <stdio.h>
static int x;
#define int
void func() {
x = 200;
}
int main() {
int x = 100;
printf("%d\n", x); // it prints 100
func(); // not return any value and reference of x also not sent
printf("%d\n", x); // it prints 200
}
定义将int
设置为空。因此,x
将是全局静态x
,而不是局部的。由于第int main() {
行现在仅是main(){
,因此编译时会带有警告。它仅由于返回类型为int的函数的special handling进行编译。
答案 3 :(得分:2)
黑客:只需更改void func()
中的代码,即可创建类似于@chqrlie的define
void func()
{
/*do some code to change the value of x*/
#define func() { x = 200; }
}
int main()
{
int x = 100;
printf("%d\n", x); // it will print 100
func(); // not return any value and reference of x also not sent
printf("%d\n", x); // it need to print 200
}
输出
100
200
答案 4 :(得分:0)
无聊的答案:我将使用一个简单的全局指针变量:
int *global_x_pointer;
void func()
{
*global_x_pointer = 200;
}
int main()
{
int x = 100;
global_x_pointer = &x;
printf("%d\n", x);
func();
printf("%d\n", x);
}
我不确定“发送参考”是什么意思。如果将全局指针设置为发送参考,则此答案显然违反了所述问题的好奇规定,并且无效。
(关于“好奇的规定”,我有时希望SO有另一个标签,例如driving-screws-with-a-hammer
,因为这就是这些“脑筋急转弯”总是让我想到的。显而易见的答案,但是不,您不能使用该答案,您被困在荒岛上并且C编译器的for
语句在沉船中被打碎,因此您应该是McGyver并使用有时候,这些问题可以展示出良好的横向思维能力,并且很有趣,但大多数情况下,它们只是愚蠢的。)
答案 5 :(得分:0)
这种方法很容易破解,但面试官却要求这样做。因此,以下示例说明了为什么C和C ++如此有趣的语言:
// Compiler would likely inline it anyway and that's necessary, because otherwise
// the return address would get pushed onto the stack as well.
inline
void func()
{
// volatile not required here as the compiler is told to work with the
// address (see lines below).
int tmp;
// With the line above we have pushed a new variable onto the stack.
// "volatile int x" from main() was pushed onto it beforehand,
// hence we can take the address of our tmp variable and
// decrement that pointer in order to point to the variable x from main().
*(&tmp - 1) = 200;
}
int main()
{
// Make sure that the variable doesn't get stored in a register by using volatile.
volatile int x = 100;
// It prints 100.
printf("%d\n", x);
func();
// It prints 200.
printf("%d\n", x);
return 0;
}