如何保持gcc -O2优化putchar?

时间:2014-08-22 14:24:29

标签: c gcc compiler-optimization putchar

我有一个使用自定义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前端解析的选项只是将第二个破折号之后的文本传递给忽略它的某个较低级别。< / p>

更详细: 三个文件try1.c,try2.c和makefile ...

try1.c:

#include <stdio.h>

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

try2.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:

1 个答案:

答案 0 :(得分:4)

理想情况下,您应该制作MCVE(Minimal, Complete, Verifiable Example)或 SSCCE(Short, Self-Contained, Correct Example) - 两个相同基本想法的名称(和链接)。

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

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

-O2-O3下的Mac OS X 10.9.4上使用GCC 4.9.1编译时,调用了我的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库函数的使用

     

除非在详细说明中另有明确说明,否则以下每个陈述均适用   以下描述:......

     

任何功能   在标题中声明的另外可以实现为在其中定义的类似函数的宏   标题,因此如果在包含标题时显式声明了库函数,则为一个   下面显示的技术可用于确保声明不受影响   这样的宏。可以通过封闭在本地抑制函数的任何宏定义   括号中函数的名称,因为该名称后面没有左边的名称   括号,表示宏函数名称的扩展。对于相同的句法   原因是,即使它也被定义为,也允许获取库函数的地址   一个宏。 185)使用#undef删除任何宏定义也将确保一个   参考实际功能。实现为的库函数的任何调用   一个宏应该扩展为代码,它完全一次地评估它的每个参数   必要时用括号保护,因此通常可以安全地使用任意方法   表达式作为参数。 186)同样,那些类似函数的宏描述于   以下子条款可以在带有a的函数的任何地方的表达式中调用   可以调用兼容的返回类型。 187)

     
     

185)这意味着实现应为每个库函数提供实际函数,即使它   还为该功能提供了一个宏。

     

186)此类宏可能不包含相应函数调用的序列点。

     

187)因为保留了以下划线开头的外部标识符和一些宏名称,   实现可以为这些名称提供特殊语义。例如,标识符   _BUILTIN_abs可用于指示abs函数的内联代码的生成。就这样   适当的标题可以指定

#define abs(x) _BUILTIN_abs(x)
     

代码生成器将接受它的编译器。   以这种方式,希望保证诸如abs的给定库函数将是真实的用户   功能可以写

#undef abs
     

实现的标头是否提供abs或内置的宏实现   实现。该函数的原型,在任何宏之前和之前都是隐藏的   由此也揭示了定义。

根据您观察到的情况,在一组标题中,putchar()未定义为宏(它不一定是,但可能是)。切换编译器/库意味着现在putchar()被定义为宏,缺少#undef putchar意味着事情不再像以前一样工作。