如何在没有参考的情况下更改局部变量

时间:2019-07-15 10:15:40

标签: c

面试问题:更改局部变量值而不发送参考或获取返回值

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的值需要更改

6 个答案:

答案 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()中的代码,即可创建类似于@chqrliedefine

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;
}