C语言 如何在没有引用的情况下更改局部变量

wz8daaqr  于 2023-02-07  发布在  其他
关注(0)|答案(6)|浏览(204)

**面试问题:**在不使用引用作为函数参数或不从函数返回值的情况下更改局部变量值

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

q0qdq0h2

q0qdq0h21#

答案是你不能。
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是一个直接存储在ESI寄存器中的常量,在printf调用之前,即使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,但这不是问题所在。

aiazj4mn

aiazj4mn2#

根据C语言的设计,以及局部变量的定义,如果不以某种方式使其可用,就无法从外部访问它。
使外部世界可以访问局部变量的一些方法:

  • 发送其副本(价值);
  • 发送一个指向该变量的指针(不要保存和使用该指针太长时间,因为该变量可能会在其作用域结束时被删除);
  • 如果变量在文件级声明(所有函数之外),则使用extern导出。
klh5stk1

klh5stk13#

哈克

仅更改void func()中的代码,创建define
类似于“chqrlie”。

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
4bbkushb

4bbkushb4#

答案是你不能,但是...
我完全同意@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
}

define会将int设置为空。因此x将是全局静态x而不是局部静态x。由于int main() {行现在仅为main(){,因此编译时会出现警告。它仅在返回类型为int的函数为special handling时才编译。

umuewwlo

umuewwlo5#

这种方法是笨拙和脆弱的,但面试官要求它。所以这里有一个例子,为什么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;
}
wydwbb8l

wydwbb8l6#

无聊的回答:我将使用一个简单的全局指针变量:

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

我不确定“发送引用”是什么意思,如果设置一个全局指针也算发送引用,那么这个答案显然违反了所述问题的奇怪规定,是无效的。
(On关于“奇怪的规定”这个主题,我有时候希望SO有另外一个标签,像driving-screws-with-a-hammer这样的东西,因为这是这些“脑筋急转弯”总是让我想到的。非常明显的问题,非常明显的答案,但是不,明白了,你不能使用那个答案,你被困在一个荒岛上,你的C编译器的for语句在沉船中被破坏了,所以你应该像麦盖弗一样,用椰子壳和鼻屎来代替。偶尔,这些问题可以展示出良好的横向思维能力,而且很有趣,但大多数时候,它们只是愚蠢的。)

相关问题