views:

1122

answers:

16

Hi,

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.?

+12  A: 

Here's a really cheap answer:

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

:-P

Chris Jester-Young
"so that output of `printf` **in `main` function** should always give 5"
Jason
You can downvote all you like, but really, none of the answers really call "`printf` in `main`" either, so. :-P
Chris Jester-Young
This is really only slightly cheaper than mine.
Matthew Flaschen
@Chris Jester-Young: Which is why the stack corruption answer is probably the correct one which makes the question completely obnoxious.
Jason
@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.
Chris Jester-Young
@Chris Jester-Young: my answer really calls printf in main.
Laurence Gonsalves
@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
Chris Jester-Young
+1 - Gordian knot.
Bob Jarvis
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
@Chris, @Laurence: Mine really calls printf in main too, without any macro hackery. My, this post keeps calling me back with upvotes...
Charles Stewart
+19  A: 

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;
}
RC
Seen this problem before, and this was the solution.
Gregory
@Gregory, I was thinking this couldn't be the solution, but good to know, thanks :)
RC
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
@Hogan tested with gcc version 4.4.1 (Ubuntu 4.4.1-4ubuntu9) and works
RC
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
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
@Hogan the preprocessor doesn't expand again the symbols that it has already expanded
fortran
@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
@GrahamS: It's a puzzle. The point of `change()` is to put you on the wrong track :)
Thomas
@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
@GrahamS that was the first thing that came to my mind (at 7am)
RC
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
Quoth the preprocessor: "Abuse me! Abuse me! MAKE ME FEEL WANTED!"
Tim Post
+6  A: 

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.

Floetic
+1 I believe this is the right answer.
Nick D
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.
Chris Jester-Young
Very true Chris. The nature of the question seemed academic, and theoretically, this is one option.
Floetic
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
+4  A: 
void change()
{
#define printf(x,y) fprintf(stdout,x,y-5)
}
mobrule
oh... I like this one.
Hogan
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
@Chris Lutz: Good one. And in that case why not just #define printf(x,y) (printf)(x,5)?
ArunSaha
+1  A: 

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
No you don't. See Laurence Gonsalves' answer.
finnw
+16  A: 
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
@Laurence: Clever :)
AndreyT
-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
Well, it'll be replaced with `printf("%d", a=5,b)`. Does that compile? I'm not sure how printf works
Chris Burt-Brown
@Graham, @Chris: Yes, that works. Extra arguments to `printf` are ignored.
Chris Jester-Young
nice solution :)
RC
@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
@Chris Burt-Brown: printf is just a varargs function. See my comment to GrahamS for more details.
Laurence Gonsalves
@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
+4  A: 

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
You'd have been better to have opened `/dev/null` for the new stdout.
Donal Fellows
+6  A: 

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
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.
Chris Jester-Young
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
@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
@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
@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
@Chris: thanks. I like jbcreix's answer the most.
Alok
+7  A: 

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
`printf` returns `int`, you have it defined as having no return value.
dreamlax
`printf` returns an `int` but your implementation returns no value. `return fprintf` is in order, methinks. But +1 for extreme cleverness.
Chris Lutz
+3  A: 

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
+15  A: 

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
Very clever! I like it :D
Thomas
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
@jbcreix's mprotect() trick should work with this too.
moonshadow
Dangit! I was just about to post this!
MSN
You'd need to build with `-fwritable-strings` for that to work. I like it!
Donal Fellows
+20  A: 

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.

jbcreix
+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
I'd personally like to factor `sysconf(_SC_PAGESIZE)` into a variable, rather than calling `sysconf` 5 times. :-P
Chris Jester-Young
@Chris[0], what compiler and options are you using? @Chris[1], fixed, I blame `vi` :p
jbcreix
@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
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.
jbcreix
I wish I could +1 again for your catering to my whimsical headers.
Chris Lutz
My eyes ... MY EYES!!!!!
Tim Post
+1  A: 

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
+1  A: 

I know this should be a comment, but I don't see a comment box anywhere :(. I don't get jbcreix's answer. How did he know that there won't be any 10's earlier, in the form binary of some instruction. Also, can somebody tell me why can't I see a comment box :|

Do you see the "add comment" link?
Alok
You can't see the comment box because you need 50 reputation to comment. I'd upvote you so you could comment, but I'm out of upvotes for today. And I believe he didn't know that there would be any 10's earlier, but he banked on the chance that, out of (an estimated) 15 earlier values, any one of them would be 10 is about 60 (assuming each word is 4 bytes) / 256, or 23.4%. Not perfect, but enough to bet on. And it turns out to work.
Chris Lutz
The actual chances of a 10 occurring earlier are lower, because not all 2 ** 32 x86 opcodes are used for instructions so not all values are used (so not all combinations of 10 can appear), and many instructions (bit operations) are almost guaranteed not to occur when you're just assigning to variables (with no math) and calling a function. In practice you can sort of predict what operations might be called, but I think it was more likely blind luck. (I know I didn't consider that situation until you brought it up, so good job.)
Chris Lutz
This is the main reason why I said it won't work on some architectures. But good point.
jbcreix
You can modify the const char[] in a more portable way using the same method but I thought it would be more fun modifying the actual value. In the end, some OSes and architectures are going to thwart your evil plans anyway.
jbcreix
+3  A: 

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
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
changed it to `\r5 \b\n`, it looks cleaner.
Joe D
A: 

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