代码之家  ›  专栏  ›  技术社区  ›  WildCrustacean

用C + VC++中编译器编译对象文件中未使用的符号

  •  2
  • WildCrustacean  · 技术社区  · 15 年前

    这可能是一个愚蠢的问题,但也许有人能提供一些见解。

    如果源文件是C,那么编译器似乎忽略了全局符号,所有链接都没有错误。如果源文件是C++,则在两个对象文件中都包含符号,然后得到链接错误。对于C++,我在使用头时使用Extn“C”。

    这是我的密码:

    头文件(test.h):

    #ifndef __TEST_H
    #define __TEST_H
    
    /* declaration in header file */
    void *ptr;
    
    #endif
    

    #include "test.h"
    
    int main( ) {
        return 0;
    }
    

    #include "test.h"
    

    extern "C" {
    #include "test.h"
    }
    
    int main( ) {
        return 0;
    }
    

    test2.cpp

    extern "C" {
    #include "test.h"
    }
    

    Dump of file test1.obj
    
    File Type: COFF OBJECT
    
    COFF SYMBOL TABLE
    000 006DC627 ABS    notype       Static       | @comp.id
    001 00000001 ABS    notype       Static       | @feat.00
    002 00000000 SECT1  notype       Static       | .drectve
        Section length   2F, #relocs    0, #linenums    0, checksum        0
    004 00000000 SECT2  notype       Static       | .debug$S
        Section length  228, #relocs    7, #linenums    0, checksum        0
    006 00000004 UNDEF  notype       External     | _ptr
    007 00000000 SECT3  notype       Static       | .text
        Section length    7, #relocs    0, #linenums    0, checksum 96F779C9
    009 00000000 SECT3  notype ()    External     | _main
    00A 00000000 SECT4  notype       Static       | .debug$T
        Section length   1C, #relocs    0, #linenums    0, checksum        0
    
    String Table Size = 0x0 bytes
    

    Dump of file test1.obj
    
    File Type: COFF OBJECT
    
    COFF SYMBOL TABLE
    000 006EC627 ABS    notype       Static       | @comp.id
    001 00000001 ABS    notype       Static       | @feat.00
    002 00000000 SECT1  notype       Static       | .drectve
        Section length   2F, #relocs    0, #linenums    0, checksum        0
    004 00000000 SECT2  notype       Static       | .debug$S
        Section length  228, #relocs    7, #linenums    0, checksum        0
    006 00000000 SECT3  notype       Static       | .bss
        Section length    4, #relocs    0, #linenums    0, checksum        0
    008 00000000 SECT3  notype       External     | _ptr
    009 00000000 SECT4  notype       Static       | .text
        Section length    7, #relocs    0, #linenums    0, checksum 96F779C9
    00B 00000000 SECT4  notype ()    External     | _main
    00C 00000000 SECT5  notype       Static       | .debug$T
        Section length   1C, #relocs    0, #linenums    0, checksum        0
    
    String Table Size = 0x0 bytes
    

    我注意到,当编译C源时,将将PTR列为UNDEF,并且在编译C++源时定义了它,这导致链接错误。

    我明白这在现实生活中不是一件好事,我只是想理解为什么这是不同的。

    谢谢

    2 回复  |  直到 15 年前
        1
  •  12
  •   Alok Singhal    15 年前

    在C语言中,标识符有三种不同类型的“链接”:

    1. :大致上,这就是人们所说的“全局变量”。通常,它指的是“随处可见”的标识符。
    2. 内部连接 :这些是使用声明的对象 static 关键词。
    3. :这些是“临时”或“自动”的对象,例如在函数中声明的变量(通常称为“局部变量”)。

    定义。由于您的头文件定义了这样一个对象,并且包含在两个C文件中,所以它是未定义的行为(请参见下文)。C编译器不抱怨并不意味着可以在C中这样做。为此,您必须阅读C标准(或者,假设您的编译器中没有bug,如果在符合标准的模式下调用它,并且如果它抱怨某些东西[给出诊断],则可能意味着您的程序不符合标准。)

    换句话说,您无法通过测试某些内容并检查编译器是否允许来测试语言允许的内容。为此,您必须阅读标准。

    实验性的 定义。

    $ cat a.c
    int x = 0;
    $ cat b.c
    #include <stdio.h>
    int x = 0;
    int main(void)
    {
        printf("%d\n", x);
        return 0;
    }
    $ gcc -ansi -pedantic -W -Wall -c a.c
    $ gcc -ansi -pedantic -W -Wall -c b.c
    $ gcc -o def a.o b.o
    b.o:(.bss+0x0): multiple definition of `x'
    a.o:(.bss+0x0): first defined here
    collect2: ld returned 1 exit status
    

    a.c :

    $ cat a.c
    int x; /* Note missing " = 0", so tentative definition */
    

    现在编译它:

    $ gcc -ansi -pedantic -W -Wall -c a.c
    $ gcc -o def a.o b.o
    $ ./def
    0
    

    b.c 相反:

    $ cat a.c
    int x = 0;
    $ cat b.c
    #include <stdio.h>
    int x; /* tentative definition */
    int main(void)
    {
        printf("%d\n", x);
        return 0;
    }
    $ gcc -ansi -pedantic -W -Wall -c a.c
    $ gcc -ansi -pedantic -W -Wall -c b.c
    $ gcc -o def a.o b.o
    $ ./def
    0
    

    int x;

    因此,在头文件中可能有一个暂定的定义。我们需要查看实际代码才能确定。

    使用带有外部链接的标识符,但在程序中不存在 标识符存在多个外部定义。

    C++可能有不同的规则。

    编辑 :根据 this thread on comp.lang.c++ ,C++没有 暂定定义 . 原因是:

    这样可以避免对内置类型和用户定义类型使用不同的初始化规则。

    (顺便说一句,该线程处理相同的问题。)

    现在我几乎可以肯定,OP的代码包含了C文件在头文件中所称的“临时定义”,这使得它在C中合法,C++中是非法的。但是,只有当我们看到代码时,我们才能确定。

    关于“暂定定义”以及为什么需要这些定义的更多信息,请参见本节 excellent post on comp.lang.c

        2
  •  1
  •   Jonathan Leffler vy32    15 年前

    仅在标头中声明变量-使用显式 extern

    不同之处在于C++明确地要求一个定义规则——在C++程序中,只有一个给定的变量,具有外部链接。

    严格来说,C标准也有同样的要求。然而,该标准的附录J列出了允许将多个未初始化定义视为一个定义的通用扩展-它被称为“通用定义”,因为它类似于(老式)Fortran(例如,Fortran IV、又名Fortran 66和Fortran 77)中的通用块的行为。


    是的,如果您知道的足够多,不需要问这样的问题,那么偶尔,非常少,但只是偶尔,会有一个在标题中定义变量的原因。但这样的情况非常少,以至于说“不要在标题中定义变量”几乎足够准确。


    克里斯托夫提出了一个有趣的观点: static const 变量'。围绕这个问题的一个狡猾的措辞是声称“常数不是变量”。但是,克里斯托夫是正确的:在C++中,静态const变量使用。在C语言中,我认为这样的常量会引起编译器发出“未使用”的警告;但是,GCC 4.4.2不适用 给出此最小代码时给出任何警告:

    static const int x = 3;
    extern int p(void);
    int p(void)
    {
        return(3);
    }
    

    它不会抱怨未使用的设备 x 即使在’ -Wall -Wextra '. 因此,在标题中定义常量是可以的-如' static const SomeType constName = InitialValue; cc -v ':

    C compiler: /compilers/v12/SUNWspro/bin/cc
    cc: Sun C 5.9 SunOS_sparc Patch 124867-09 2008/11/25
    acomp: Sun C 5.9 SunOS_sparc Patch 124867-09 2008/11/25
    iropt: Sun Compiler Common 12 SunOS_sparc Patch 124861-13 2009/03/10
    cg: Sun Compiler Common 12 SunOS_sparc Patch 124861-13 2009/03/10
    ld: Software Generation Utilities - Solaris Link Editors: 5.10-1.486