C拼图:printf的输出应始终为"5"

Syn*_*ter 18 c puzzle

我在C aptitude论文中发现了这个难题.

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;
}
Run Code Online (Sandbox Code Playgroud)

任何解决方案.?

小智 27

限定?

#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;
}
Run Code Online (Sandbox Code Playgroud)

  • 这个故事的道德到底是什么?什么意思教?我不知道他们上过关于如何编写可怕的无法管理和被黑客攻击的代码的课程......(尽管我已经看过,如果他们这样做我也不会感到惊讶......) (6认同)
  • 只有一个宏的另一个解决方案是`#define printf(a,b)(printf)("5")`它巧妙地避免了任何递归的宏扩展问题(我的编译器喜欢在人们有递归宏时抱怨,这可能是好的) .虽然我更喜欢`puts()`版本,因为它为可读性添加了换行符. (3认同)

小智 27

这是一个POSIX答案,真正做到了问题的问题:)

它不适用于某些架构/编译器,但它确实在这里.

#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;
}
Run Code Online (Sandbox Code Playgroud)

编辑:这应该使抵制标题的人更健壮.

  • @jbcreix - 我喜欢你的索引.我默认使用`gcc -Wall -Wextra -Werror`.我打赌如果我放下警告,它会编译好.我只是忘了他们,因为他们大部分时间都很好.更新:没有,即使没有任何警告,我必须打开`-fnested-functions`,然后它仍然会发出警告.我责怪OS X. (2认同)
  • @SiegeX - 函数`_change`查看`main`函数的机器代码,使其可写,搜索10,并将10更改为5.因此,当`_change`返回`change`和` change`返回到`main`,下一行,`i = 10;`将在执行的机器代码中更改为`i = 5;`.它是自修改代码. (2认同)

Lau*_*ves 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
}
Run Code Online (Sandbox Code Playgroud)

  • @GrahamS确实有效.`printf("%d",i)`被替换为`printf("%d",a = 5,b)`(不确定为什么你认为`b`会被省略).`a = 5`是一个计算结果为5的表达式.`b`也传递给`printf`(`printf`使用varargs),但`printf`忽略它,因为只有一个格式化代码(`%d `).有些编译器可能会警告您将额外的参数传递给`printf`,但它仍然是有效的标准C. (5认同)
  • @Laurence是的,我的立场得到了纠正.不知道为什么我在没有`b`的情况下阅读它(已经很晚了),但是`printf`确实似乎处理了额外的参数. (2认同)

Chr*_*ung 21

这是一个非常便宜的答案:

void
change()
{
    printf("%d", 5);
    exit(0);
}
Run Code Online (Sandbox Code Playgroud)

:-P

  • 你可以向所有人喜欢,但实际上,没有一个答案在"main"中真正称为"`printf`",所以.:-P (12认同)
  • "所以'main`函数**中'printf`**的输出应该总是给出5" (4认同)
  • 我对此投票感到内疚.答案解决了这个要求,并在空中正确地退出. (4认同)
  • @Chris Jester-Young:我的答案真的称为printf in main. (2认同)

moo*_*dow 20

这是另一种可能性:

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

这比更改返回地址要便携得多,但要求你(a)有一个汇编字符串文字的编译器(大多数都这样做),(b)有一个不将常量放在只读部分的编译器,或者在没有MMU的架构上运行(这些天不太可能).


Joe*_*e D 9

有人想过使用atexit吗?


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

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

请注意,main函数中没有终止换行符,如果我们向stdout发送2个退格符,则10将被删除,并且只打印5个.


Cha*_*art 8

调用必需的#include,并用括号 - 不平衡文本替换注释:

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

测试成功.感谢dreamlax和Chris Lutz的错误修正.


Bri*_*ang 6

堆栈中有一个局部变量i,其值为5.

使用change(),您需要将下一条指令修改为5,因此您需要将覆盖缓冲区设置为设置为10的位置,并将其设置为5.

  • 这让我回过头来......澄清一下,如果我理解正确:当你到达`change`时,堆栈应该有一个返回地址.您想使用该地址来查找下一条指令(假设您知道它的大小),并将其更改为no-op? (2认同)

Alo*_*hal 6

该printf("%d", i);呼叫main()没有结束它在一个换行符输出,程序的行为是实现定义的.

我断言,在我的实现中,一个无法为最后一行写一个终止换行的程序总是打印5后跟换行作为最后一行.

因此,无论定义如何,输出总是为5 change().:-)

(换句话说,这些问题的重点是什么,除非它们意味着在特定的硬件,编译器等上运行?)

  • @Alok - 我不同意你的阅读.我没有看到它说"实现定义的行为_如果你没有以换行符结束",而是"实现定义的行为_Woother_你必须以换行结束".不错,尝试一下.如果标准更宽松,本来会很漂亮. (6认同)
  • C99 7.19.2.2:*文本流是由行组成的有序字符序列,每行由零个或多个字符加上一个终止的换行符组成.最后一行是否需要终止的换行符是实现定义的.*我错了"未定义",它应该是"实现定义的",但我的答案仍然是正确的,在精神上:-) (3认同)