18

I found this puzzle in a C aptitude paper.

void change()
{
    //write something in this function so that output of printf in main function
    //should always give 5.you can't change the main function
}

int main()
{
    int i = 5;
    change();
    i = 10;
    printf("%d", i);
    return 0;
}

Any solutions.?

Kev
  • 118,037
  • 53
  • 300
  • 385
SyncMaster
  • 9,754
  • 34
  • 94
  • 137
  • yawn... stuff like that was topic in the Obfuscated C Contest 20 years ago already. – Thorsten79 Feb 02 '10 at 12:39
  • 1
    Puzzles like this make more sense to test whether people spot a missing `\n` at the end of the output or a missing declaration for `printf` function. – AnT stands with Russia Feb 02 '10 at 21:18
  • 4
    +1 there are some really excellent answers here, despite the initial skepticism. Doubters should check out the POSIX-compliant, no-macros version. – Chris Lutz Feb 04 '10 at 06:20

16 Answers16

27

define?

#include <stdio.h>

void change()
{
//write something in this function so that output of printf in main function
//should always give 5.you can't change the main function
#define printf_ printf
#define printf(a, b) printf_("5");
}

int main()
{
       int i = 5;
       change();
       i = 10;
       printf("%d", i);
       return 0;
}
  • Seen this problem before, and this was the solution. – Gregory Feb 02 '10 at 05:48
  • @Gregory, I was thinking this couldn't be the solution, but good to know, thanks :) –  Feb 02 '10 at 05:49
  • I don't think this will work, it is recursive and the pre-processor has to end up with something. Just use `#define printf(a,b) puts("5");`? – Hogan Feb 02 '10 at 05:49
  • @Hogan tested with gcc version 4.4.1 (Ubuntu 4.4.1-4ubuntu9) and works –  Feb 02 '10 at 05:50
  • drat, I like puts better... but I guess that is not "the output of the printf" function. of course, yours is not really also. silly question. – Hogan Feb 02 '10 at 05:52
  • 6
    what the hell is the moral of this story? what is this meant to teach?? I didn't know they did classes on how to write horribly unmanageable and hacked code... (though by the amount I've seen, I wouldn't be surprised if they did...) – matt Feb 02 '10 at 07:27
  • @Hogan the preprocessor doesn't expand again the symbols that it has already expanded – fortran Feb 02 '10 at 12:03
  • 1
    @RC: I can't really believe this is the real solution. What would be the point of the `change()` function? If this is the expected answer then wouldn't the question simply say "Write something here" above `main()` without bothering with the function? – GrahamS Feb 02 '10 at 12:20
  • @GrahamS: It's a puzzle. The point of `change()` is to put you on the wrong track :) – Thomas Feb 02 '10 at 12:31
  • @Thomas: maybe, but I think modifying the stack within `change()` is a more likely "correct" solution. Though possibly it is just one of those stupid interview questions where they "just want to see how you think". – GrahamS Feb 02 '10 at 14:00
  • @GrahamS that was the first thing that came to my mind (at 7am) –  Feb 02 '10 at 20:09
  • 3
    Another solution with only one macro is `#define printf(a, b) (printf)("5")` which neatly avoids any recursive macro expansion issues (my compiler likes to complain when people have recursive macros, which is probably good). Though I prefer the `puts()` version because it adds a newline for readability. – Chris Lutz Feb 04 '10 at 05:49
  • 1
    Quoth the preprocessor: "Abuse me! Abuse me! MAKE ME FEEL WANTED!" – Tim Post Mar 28 '10 at 18:11
26

This is a POSIX answer that really does what the problem asks :)

It won't work on some architectures/compilers but it does here.

#include <stdio.h>

void
change () {

    void _change();
    _change();
}
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/mman.h>
void
_change()
{
    int main();
    uintptr_t m=(uintptr_t)main;
    uintptr_t ps=sysconf(_SC_PAGESIZE);
    m/=ps;
    m*=ps;
    mprotect((void*)m,ps,PROT_READ|PROT_WRITE|PROT_EXEC);
    char *s=(char*)(intptr_t)main;
    s=memchr(s,10,ps);
    *s=5;
    mprotect((void*)m,ps,PROT_READ|PROT_EXEC);

}

int
main() {
    int i=5;
    change();
    i=10;
    printf ("%d\n",i);
    return 0;
}

EDIT: This should make it more robust for people with boycotting headers.

  • +1 astounding. I had to move the `#include`s to the top to get it to compile, but it works like a charm, and this technique is quite brutal. You win at least 3 internets. – Chris Lutz Feb 04 '10 at 06:02
  • 1
    I'd personally like to factor `sysconf(_SC_PAGESIZE)` into a variable, rather than calling `sysconf` 5 times. :-P – C. K. Young Feb 04 '10 at 06:04
  • @Chris[0], what compiler and options are you using? @Chris[1], fixed, I blame `vi` :p –  Feb 04 '10 at 06:13
  • 2
    @jbcreix - I like your indexing. I'm using `gcc -Wall -Wextra -Werror` by default. I bet it would compile fine if I cranked down the warnings. I just forget about them because they're so nice most of the time. Update: Nope, even without any warnings I have to turn on `-fnested-functions`, and then it still spits out a warning. I blame OS X though. – Chris Lutz Feb 04 '10 at 06:17
  • Now I get it. Your include files must contain functions. You can probably do away with some of the includes. If the includes were code free, the code would compile without warning. Actually -pedantic tells me that ISO C doesn't allow casting function* to other pointer types. Still, I am not aware of any architecture where they can't at least be converted eg. CS:???? to DS:???? so I guess inserting a cast to intptr_t first would satisfy the standard. It does placate -pedantic at the very least. –  Feb 04 '10 at 11:17
  • I wish I could +1 again for your catering to my whimsical headers. – Chris Lutz Feb 04 '10 at 11:55
  • @Chris could you explain what's going on here for the unwashed like myself? – SiegeX Jan 12 '11 at 00:58
  • 2
    @SiegeX - The function `_change` looks into the machine code of the `main` function, makes it writable, searches for a 10, and changes the 10 to a 5. Thus, when `_change` returns to `change` and `change` returns to `main`, the next line, `i = 10;` will have been changed to `i = 5;` in the machine code that gets executed. It's self-modifying code. – Chris Lutz Jan 12 '11 at 01:00
  • @Chris Thanks, much clearer now. When you say 'machine code', that would be in the code segment of memory, correct? I'm fairly sure you're not allowed to have a function call `main()` but I never really gave it much thought that you could get a pointer into the memory segment where `main()` lies. – SiegeX Jan 12 '11 at 01:58
  • 1
    @SiegeX - C++ prohibits calling `main`, but in C calling `main` from another function is perfectly legal. And yes, the pointer points to the code segment. It's a function pointer, cast to a regular pointer (which is UB but works in practice on POSIX systems which we're already assuming anyway). – Chris Lutz Jan 12 '11 at 02:03
25
void change()
{
  //write something in this function so that output of printf in main function
  //should always give 5.you can't change the main function
  #define i a=5,b
}
Laurence Gonsalves
  • 137,896
  • 35
  • 246
  • 299
  • -1, That would not work. `printf("%d", i)` would be replaced by the preprocessor to be `printf("%d", a=5,)` which would not compile. – GrahamS Feb 02 '10 at 11:59
  • 1
    Well, it'll be replaced with `printf("%d", a=5,b)`. Does that compile? I'm not sure how printf works – Chris Burt-Brown Feb 02 '10 at 12:24
  • 1
    @Graham, @Chris: Yes, that works. Extra arguments to `printf` are ignored. – C. K. Young Feb 02 '10 at 14:54
  • 5
    @GrahamS it does work. `printf("%d", i)` is replaced by `printf("%d", a=5,b)` (not sure why you thought the `b` would be omitted). `a=5` is an expression that evaluates to 5. The `b` is passed to `printf` as well (`printf` uses varargs), but `printf` ignores it because there's only one formatting code (the `%d`). Some compilers might warn that you're passing an extra argument to `printf`, but it's still valid standard C to do so. – Laurence Gonsalves Feb 03 '10 at 01:21
  • @Chris Burt-Brown: printf is just a varargs function. See my comment to GrahamS for more details. – Laurence Gonsalves Feb 03 '10 at 01:23
  • 2
    @Laurence Yeah, I stand corrected. Not sure why I read that without the `b` (it was late), but `printf` does indeed seem to handle the extra arguments. – GrahamS Feb 03 '10 at 11:21
21

Here's a really cheap answer:

void
change()
{
    printf("%d", 5);
    exit(0);
}

:-P

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
  • 4
    "so that output of `printf` **in `main` function** should always give 5" – jason Feb 02 '10 at 05:47
  • 12
    You can downvote all you like, but really, none of the answers really call "`printf` in `main`" either, so. :-P – C. K. Young Feb 02 '10 at 05:57
  • This is really only slightly cheaper than mine. – Matthew Flaschen Feb 02 '10 at 06:00
  • 1
    @Chris Jester-Young: Which is why the stack corruption answer is probably the correct one which makes the question completely obnoxious. – jason Feb 02 '10 at 06:02
  • @Jason: I'm not sure how you can pull off the stack corruption in a way that will make it work on modern operating systems. I'd love to see someone write some demonstration code. – C. K. Young Feb 02 '10 at 06:05
  • 2
    @Chris Jester-Young: my answer really calls printf in main. – Laurence Gonsalves Feb 03 '10 at 04:48
  • @Laurence: I know, I love your answer and was probably the first person to upvote it. :-) My comment came about before your answer was posted, and two people decided to downvote me. :-P – C. K. Young Feb 03 '10 at 13:15
  • 4
    I _don't_ feel guilty for up-voting this. The answer addresses the requirement and exits with its nose properly in the air. – Tim Post Mar 28 '10 at 18:07
  • @Chris, @Laurence: Mine really calls printf in main too, without any macro hackery. My, this post keeps calling me back with upvotes... – Charles Stewart May 05 '10 at 09:52
20

Here's another possibility:

void change()
{
  char const *literal = "%d";
  char * ptr = (char*)literal;
  ptr[0] = '5';
  ptr[1] = 0;
}

This is much more portable than changing the return address, but requires you to (a) have a compiler that pools string literals (most do), and (b) have a compiler that doesn't place constants in a read-only section, or be running on an architecture with no MMU (unlikely these days).

moonshadow
  • 86,889
  • 7
  • 82
  • 122
  • I sadly get a Bus error on OS X before anything prints, so I have no idea if this worked. But it is an excellent solution. – Chris Lutz Feb 04 '10 at 06:13
  • Dangit! I was just about to post this! – MSN Feb 05 '10 at 01:01
  • You'd need to build with `-fwritable-strings` for that to work. I like it! – Donal Fellows Mar 28 '10 at 11:01
  • Is this really *legal* C or just what compilers happen to accept? I thought there was some standard rule about not modifying constants through pointers. – Ira Baxter Jan 20 '11 at 21:49
  • The C is perfectly legal and will compile everywhere. Whether it actually works at runtime will vary - the compiler might not pool literals (in which case the change won't affect the calling code), or the literals may be placed in an area of memory to which the MMU blocks writes (in which case there'll be an exception when the code runs). – moonshadow Jan 21 '11 at 10:49
9

Anybody thought of using atexit?


void change (void)
{
    static int i = 0;
    if (i == 0) atexit (change);

    if (i == 1) printf ("\r5 \b\n");
    ++i;
}

Note that there is no terminating newline in the main function, if we send 2 backspace characters to stdout, the 10 will be erased, and only the 5 will be printed.

Joe D
  • 2,855
  • 2
  • 31
  • 25
  • Brilliant! I thought about deleting the 5 with terminal backspaces, but didn't think of a way to get change() called after the printf below. You can also do printf("\r5 \b\n");, as \r (carriage return) rewinds the cursor. – Joey Adams Mar 28 '10 at 18:16
  • changed it to `\r5 \b\n`, it looks cleaner. – Joe D Mar 28 '10 at 18:40
8

Invoke the requisite #include, and replace the comment with the parenthesis-unbalanced text:

}
int printf(const char *s, ...) {
  return fprintf(stdout,"%d",5);

Tested successfully. Thanks to dreamlax and Chris Lutz for bugfixes.

Charles Stewart
  • 11,661
  • 4
  • 46
  • 85
  • `printf` returns `int`, you have it defined as having no return value. – dreamlax Feb 02 '10 at 10:35
  • `printf` returns an `int` but your implementation returns no value. `return fprintf` is in order, methinks. But +1 for extreme cleverness. – Chris Lutz Feb 04 '10 at 05:59
6

You have a local variable i in the stack that has a value of 5 to begin with.

With change(), you need to modify the next instruction to be 5 so you would need to buffer override to that location where 10 is set, and have it set to 5.

Brian Liang
  • 7,734
  • 10
  • 57
  • 72
  • There's only one little flaw with this idea. On modern operating systems, executable pages are not writable. So, self-modifying code is not very likely to work. – C. K. Young Feb 02 '10 at 06:02
  • Very true Chris. The nature of the question seemed academic, and theoretically, this is one option. – Brian Liang Feb 02 '10 at 06:06
  • 2
    This takes me back... To clarify, if I understand correctly: when you reach `change` the stack should have a return address. You want to use that address to find the next instruction (assuming you know its size), and change it to no-op? – Kobi Feb 02 '10 at 06:17
6

The printf("%d", i); call in main() doesn't end its output in a newline, the behavior of the program is implementation-defined.

I assert that on my implementation, a program that fails to write a terminating newline for the final line always prints 5 followed by a newline as its last line.

Thus, the output will always be 5, whatever the definition of change(). :-)

(In other words, what's the point of such questions, unless they're meant to run on particular hardware, compiler, etc.?)

Alok Singhal
  • 93,253
  • 21
  • 125
  • 158
  • 1
    What?! I thought the undefined behaviour comes about when your _source file_ does not end in a newline, not when the program's output doesn't end in a newline. – C. K. Young Feb 02 '10 at 06:35
  • 3
    C99 7.19.2.2: *A text stream is an ordered sequence of characters composed into lines, each line consisting of zero or more characters plus a terminating new-line character. Whether the last line requires a terminating new-line character is implementation-defined.* I was wrong about "undefined", it should be "implementation-defined", but my answer is still correct, in spirit :-) – Alok Singhal Feb 02 '10 at 06:40
  • 6
    @Alok - I disagree with your reading. I don't see it saying "implementation defined behavior _if_ you don't end with a newline," but rather "implementation defined behavior _whether_ you have to end with a newline." Good try, though. Would have been beautiful if the standard was looser. – Chris Lutz Feb 04 '10 at 05:57
  • @Chris: but I don't see the difference. An implementation can define "it's okay to not end your final line with a newline", but another can say, "no, you MUST end the final line with a newline, otherwise I'm going to print 5". – Alok Singhal Feb 04 '10 at 06:00
  • @Alok - Okay, yeah. I think this is probably just a miswording on the part of the standard, but you are correct. +1 for sheer pedantry. – Chris Lutz Feb 04 '10 at 06:09
4
void change()
{
#define printf(x,y) fprintf(stdout,x,y-5)
}
mob
  • 117,087
  • 18
  • 149
  • 283
  • If you want to golf this down you can go `#define printf(x,y) (printf)(x,y-5)` and avoid an unsightly recursive-looking macro. But if you really wanted to golf `puts` is the way to go. +1 for using the original input. – Chris Lutz Feb 04 '10 at 06:15
  • @Chris Lutz: Good one. And in that case why not just #define printf(x,y) (printf)(x,5)? – Arun Mar 02 '10 at 05:16
4

Simple:

void change()
{
    printf("%d\n", 5);
    int foo;
    close(0);
    close(1);
    dup2(foo, 1);
    dup2(foo, 0);
}

Slightly more sophisticated:

void change()
{
    int *outfd = malloc(2 * sizeof(int));
    char buf[3];
    pipe(outfd);
    if(!fork())
    {
    read(outfd[0], buf, 2);
    if(buf[0] == '1' && buf[1] == '0')
    {   
        printf("5\n");
    }
    else
    {
        write(1, buf, 2);
    }
    while(1);
    }
    else
    {
    close(1);
    dup2(outfd[1], 1);
    }
}
Matthew Flaschen
  • 278,309
  • 50
  • 514
  • 539
4

I suspect that the "correct" answer to this is to modify the return address on the stack within the change() function, so that when it returns the control flow skips the i=10 command and goes straight to the printf.

If so then that is a horrible, ugly question and the (non-portable) answer requires knowledge of the architecture and instruction set used.

GrahamS
  • 9,980
  • 9
  • 49
  • 63
1

How about something like this: (x86 only)

change()  
{
    __asm__( "mov eax, [ebp+4]\n\t"
             "add eax, 4\n\t"
             "mov [ebp+4], eax\n\t" );
}
Sanjaya R
  • 6,246
  • 2
  • 17
  • 19
1

Loving the answers in here. I got it to work in two lines.

void change()
{
    //write something in this function so that output of printf in main function
    //should always give 5.you can't change the main function

    /* print a 5 */
    printf("5\n");

    /* Close standard output file descriptor */
    close(1);
}

int main()
{
    int i = 5;
    change();
    i = 10;
    printf("%d", i);
    return 0;
}

The 10 will never reach the output because after the change() function prints a 5, the stdout file descriptor is closed.

People can verify that using the following online C compiler.

http://www.tutorialspoint.com/compile_c_online.php

MrPickles
  • 1,255
  • 1
  • 16
  • 31
0

here is a different one:

void change()
{
#define printf(x,y) printf("5",x,y);
}

do I get the "smallest #define to solve a silly problem award"?

Hogan
  • 69,564
  • 10
  • 76
  • 117
-1

I am not sure this would always work, but what about locating the i variable on the stack like this:

 void change()
 {  
     int j, *p;
     for (j=-100, p=&j; j<0; j++, p++)
        if (*p == 10) { *p = 5; break; }
 }
J S
  • 1,105
  • 7
  • 11
  • 1
    You could do that, but `i` is changed after `change` is called. So when `printf` is called the value of `i` will still be 10. – Joe D Mar 28 '10 at 11:20
  • @J S Can you explain how this works? How can we be sure that the 5 we seek will be within the `100*sizeof(int)` part of memory as `&j`? – J...S Jul 16 '17 at 03:33