1

我有一个使用自定义 putchar() 的应用程序;到目前为止,它一直运行良好。我将应用程序的优化级别提高到 -O2,现在我的 putchar 没有使用。我已经使用了-fno-builtin,并且基于一些谷歌搜索,我将-fno-builtin-putchar添加到了我的 CFLAGS 中,但这没关系。有没有一种“正确”的方法来解决这个问题,还是我必须进入我的代码并添加类似的东西

#define putchar myputchar

能够使用 -O2 并且仍然引入我自己的 putchar() 函数?

编辑—— 自从我最初发表这个问题以来,我偶然发现了-fno-builtin-functions=putchar,这是另一个 gcc 命令行选项。这和上面的都被 gcc 接受,但似乎没有任何明显的效果。

编辑更多 ——进一步试验我看到 gcc 也吞下了-fno-builtin-yadayada,所以显然在 gcc 前端解析的选项只是将第二个破折号后的文本传递到忽略它的较低级别。

更多细节: 三个文件 try1.c、try2.c 和 makefile...

尝试1.c:

#include <stdio.h>

int
main(int argc, char *argv[])
{
        putchar('a');
        printf("hello\n");
        return(0);
}

尝试2.c:

#include <stdio.h>

int
putchar(int c)
{
        printf("PUTCHAR: %c\n",c);
        return(1);
}

生成文件:

OPT=

try: try1.o try2.o
        gcc -o try try1.o try2.o

try1.o: try1.c
        gcc -o try1.o $(OPT) -c try1.c

try2.o: try2.c
        gcc -o try2.o $(OPT) -c try2.c

clean:
        rm -f try1.o try2.o try

这是输出: 请注意,没有优化它使用我提供的 putchar;但是使用 -O2 它可以从其他一些“神奇”的地方获得它......

els:make clean
rm -f try1.o try2.o try
els:make
gcc -o try1.o  -c try1.c
gcc -o try2.o  -c try2.c
gcc -o try try1.o try2.o
els:./try
PUTCHAR: a
hello
els:
els:
els:
els:make clean
rm -f try1.o try2.o try
els:make OPT=-O2
gcc -o try1.o -O2 -c try1.c
gcc -o try2.o -O2 -c try2.c
gcc -o try try1.o try2.o
els:./try
ahello
els:
4

1 回答 1

4

理想情况下,您应该生成一个 MCVE(最小、完整、可验证的示例)或 SSCCE(简短、自包含、正确的示例)——两个名称(和链接)用于相同的基本思想。

当我尝试重现该问题时,我创建了:

#include <stdio.h>

#undef putchar

int putchar(int c)
{
    fprintf(stderr, "%s: 0x%.2X\n", __func__, (unsigned char)c);
    return fputc(c, stdout);
}

int main(void)
{
    int c;
    while ((c = getchar()) != EOF)
        putchar(c);
    return 0;
}

当在 Mac OS X 10.9.4 上使用 GCC 4.9.1 编译时-O2-O3我的putchar函数被调用:

$ gcc -g -O2 -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Werror pc.c -o pc  
$ ./pc <<< "abc"
putchar: 0x61
putchar: 0x62
putchar: 0x63
putchar: 0x0A
abc
$

代码中唯一可能与您相关的是#undef putchar删除函数的宏覆盖。


为什么try1.c不调用你的putchar()函数

#include <stdio.h>

int
main(int argc, char *argv[])
{
        putchar('a');
        printf("hello\n");
        return(0);
}

该函数putchar()可能会被<stdio.h>. 如果您希望确保调用函数,则必须取消定义宏。

如果您不取消定义宏,它将覆盖您所做的任何事情。因此,编写以下内容至关重要#undef putchar(建议进行其他更改,但实际上不是强制性的):

#include <stdio.h>

#undef putchar

int main(void)
{
        putchar('a');
        printf("hello\n");
        return(0);
}

请注意,这putchar()是一个保留符号。虽然在实践中你会侥幸把它当作一个函数来使用,但如果你设法找到一个它不起作用的实现,你就没有理由抱怨。这适用于标准 C 库中的所有符号。因此,正式地,你应该使用类似的东西:

#include <stdio.h>

#undef putchar

extern int put_char(int c);     // Should be in a local header
#define putchar(c) put_char(c)  // Should be in the same header

int main(void)
{
        putchar('a');
        printf("hello\n");
        return(0);
}

这允许您保持“使用”源代码不变(除了包括本地头文件——但您可能已经有一个可以使用)。您只需更改实现以使用正确的本地名称。(我不相信这put_char()是一个好的名称选择,但我不喜欢my_前缀,因为它是答案中的常见约定。)

ISO/IEC 9899:2011 §7.1.4 库函数的使用

除非在随后的详细说明中另有明确说明,否则以下每个陈述均适用:……</p>

Any function declared in a header may be additionally implemented as a function-like macro defined in the header, so if a library function is declared explicitly when its header is included, one of the techniques shown below can be used to ensure the declaration is not affected by such a macro. Any macro definition of a function can be suppressed locally by enclosing the name of the function in parentheses, because the name is then not followed by the left parenthesis that indicates expansion of a macro function name. For the same syntactic reason, it is permitted to take the address of a library function even if it is also defined as a macro.185) The use of #undef to remove any macro definition will also ensure that an actual function is referred to. Any inv ocation of a library function that is implemented as a macro shall expand to code that evaluates each of its arguments exactly once, fully protected by parentheses where necessary, so it is generally safe to use arbitrary expressions as arguments.186) Likewise, those function-like macros described in the following subclauses may be invoked in an expression anywhere a function with a compatible return type could be called.187)


185) This means that an implementation shall provide an actual function for each library function, even if it also provides a macro for that function.

186) Such macros might not contain the sequence points that the corresponding function calls do.

187) Because external identifiers and some macro names beginning with an underscore are reserved, implementations may provide special semantics for such names. For example, the identifier _BUILTIN_abs could be used to indicate generation of in-line code for the abs function. Thus, the appropriate header could specify

#define abs(x) _BUILTIN_abs(x)

for a compiler whose code generator will accept it. In this manner, a user desiring to guarantee that a given library function such as abs will be a genuine function may write

#undef abs

whether the implementation’s header provides a macro implementation of abs or a built-in implementation. The prototype for the function, which precedes and is hidden by any macro definition, is thereby revealed also.

Judging from what you observe, in one set of headers, putchar() is not defined as a macro (it does not have to be, but it may be). And switching compilers/libraries means that now that putchar() is defined as a macro, the missing #undef putchar means that things no longer work as before.

于 2014-08-22T15:37:49.977 回答