-9

Interview question : Change the local variable value without using a reference as a function argument or returning a value from the function

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 value need to changed

George Stocker
  • 57,289
  • 29
  • 176
  • 237
user2811114
  • 76
  • 1
  • 8

6 Answers6

8

The answer is that you can’t.

The C programming language offers no way of doing this, and attempting to do so invariably causes undefined behaviour. This means that there are no guarantees about what the result will be.

Now, you might be tempted to exploit undefined behaviour to subvert C’s runtime system and change the value. However, whether and how this works entirely depends on the specific executing environment. For example, when compiling the code with a recent version of GCC and clang, and enabling optimisation, the variable x simply ceases to exist in the output code: There is no memory location corresponding to its name, so you can’t even directly modify a raw memory address.

In fact, the above code yields roughly the following assembly output:

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

As you can see, the value 100 is a literal directly stored in the ESI register before the printf call. Even if your func attempted to modify that register, the modification would then be overwritten by the compiled printf call:

    …
    movl    $200, %esi /* This is the inlined `func` call! */
    movl    $100, %esi
    movl    $.LC0, %edi
    xorl    %eax, %eax
    call    printf
    …

However you dice it, the answer is: There is no x variable in the compiled output, so you cannot modify it, even accepting undefined behaviour. You could modify the output by overriding the printf function call, but that wasn’t the question.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • *"you cannot modify it"* Are you sure about that? Try compiling it for 32-bit i386 ;) `mov DWORD PTR [ebp-0xc],0x64` – Marco Bonelli Aug 05 '19 at 23:28
  • @MarcoBonelli And where would you put that instruction? – Konrad Rudolph Aug 06 '19 at 08:35
  • I don't, gcc does: it's how the variable `x` is loaded: i386 32bit calling convention puts variables on the stack. It would therefore be possible to modify the value of the variable from `func()`. Not saying it should be done or it would be correct or it's not UB. Of course it would be a terrible idea and you'd have to write inline asm to do it AND it would depend on the compiler etc, but it could be done. – Marco Bonelli Aug 06 '19 at 08:39
  • @MarcoBonelli Ah I misunderstood. Yes, on i386 without optimisations you could intercept the loading of the value (something similar happens on x64, for that matter). But once you enable optimisations the load instruction is optimised out. – Konrad Rudolph Aug 06 '19 at 09:03
6

By the design of the C language, and by the definition of a local variable, you cannot access it from outside without making it available in some way.

Some ways to make a local variable accessible to the outside world:

  • send a copy of it (the value);
  • send a pointer to it (don't save and use the pointer for too long, since the variable may be removed when its scope ends);
  • export it with extern if the variable is declared at file level (outside of all functions).
virolino
  • 2,073
  • 5
  • 21
6

Hack
Only changing code in void func(), create a define.
Akin to @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
}

Output

100
200
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
2

The answer is that you can’t, but...

I perfectly agree with what @virolino and @Konrad Rudolph and I don't like my "solution" to this problem be recognised as a best practise, but since this is some sort of challenge one can come up with this approach.

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

The define will set int to nothing. Thus x will be the global static x and not the local one. This compiles with a warning, since the line int main() { is now only main(){. It only compiles due to the special handling of a function with return type int.

schorsch312
  • 5,553
  • 5
  • 28
  • 57
  • Or you could move the `#define` inside `main`. – Weather Vane Jul 15 '19 at 11:25
  • Upvoted just for fun... You should move the `#define` before the definition of `func` so as to hide it more and change the body of `func` to `int x = 200;`. As a matter of fact, you could move it *before* the `static* definition, but that might trigger another warning. – chqrlie Jul 15 '19 at 11:40
  • 1
    @chqrlie you could hide the two lines in an extra header. – schorsch312 Jul 15 '19 at 11:44
0

Boring answer: I would use a straightforward, global pointer variable:

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

I'm not sure what "sending reference" means. If setting a global pointer counts as sending a reference, then this answer obviously violates the stated problem's curious stipulations and isn't valid.

(On the subject of "curious stipulations", I've sometimes wished SO had another tag, something like driving-screws-with-a-hammer, because that's what these "brain teasers" always make me think of. Perfectly obvious question, perfectly obvious answer, but no, gotcha, you can't use that answer, you're stuck on a desert island and your C compiler's for statement got broken in the shipwreck, so you're supposed to be McGyver and use a coconut shell and a booger instead. Occasionally these questions can demonstrate good lateral thinking skills and are interesting, but most of the time, they're just dumb.)

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
0

This approach is hacky and fragile, but that interviewer is asking for it. So here's an example for why C and C++ are such fun languages:

// 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;
}
Frank S.
  • 692
  • 6
  • 4
  • 3
    Did you try this? Most stack implementations grow toward lower addresses, so `&x` would be greater than `&tmp`, not less than it. And `tmp` and `x` would not be adjacent on the stack, there would be additional data between them, such as a return address. – Eric Postpischil Jul 15 '19 at 19:31
  • You have a point, yes, but it works for me anyway. The debugger says when main() starts, the stack pointer is decreased (stack growing), reserving space for all the locals at once. They're not pushed one by one. Since func() is inlined, the "tmp" variable is actually to be found at esp + 0x0C, meanwhile variable "x" is at esp + 0x08. I'm using VS 2017 by the way. By no means am I saying it would work anywhere or with any compiler! – Frank S. Jul 16 '19 at 07:15