【问题标题】:C: const initializer and debugging symbolsC:常量初始化器和调试符号
【发布时间】:2014-03-24 14:25:40
【问题描述】:

在代码审查中,我要求使用下面的选项 (1),因为它会导致创建符号(用于调试),而 (2) 和 (3) 至少对于 gcc 和 icc 似乎没有这样做。但是 (1) 不是真正的 const 并且不能在所有编译器上用作数组大小。有没有更好的选择,包括调试符号并且对于 C 来说是真正的 const?

符号:

gcc f.c -ggdb3 -g ; nm  -a a.out | grep _sym
0000000100000f3c s _symA
0000000100000f3c - 04 0000 STSYM _symA

代码:

static const int symA = 1;  // 1

#define symB 2 // 2

enum { symC = 3 }; // 3

GDB 输出:

(gdb) p symA
$1 = 1

(gdb) p symB
No symbol "symB" in current context.

(gdb) p symC
No symbol "symC" in current context.

为了完整起见,来源:

#include <stdio.h>

static const int symA = 1;

#define symB 2

enum { symC = 3 };

int main (int   argc, char *argv[])
{
    printf("symA %d symB %d symC %d\n", symA, symB, symC);
    return (0);
}

【问题讨论】:

  • 您应该查看static const vs #define。我很惊讶您认为枚举符号不在调试信息中,尽管它不会在内存映射中显示为分配的符号。我强烈推荐enum
  • 更新了上面的示例以包含 GDB。似乎只有 A 存在。
  • 好奇:在 Ubuntu 12.04 衍生版本和您的代码上使用 GCC 4.8.2 和 GDB 7.7,我得到:(gdb) br main / Breakpoint 1 at 0x4004df: file gdb.c, line 11. / (gdb) r / Starting program: /home/jleffler/soq/gdb-test / Breakpoint 1, main (argc=1, argv=0x7fffffffe1e8) at gdb.c:11 / 11 printf("symA %d symB %d symC %d\n", symA, symB, symC); / (gdb) p symC / $1 = symC / (gdb) p (int)symC / $2 = 3 这表明这个平台上的这个GCC包含symC的符号信息。我不确定你在哪里使用什么,但我对你的工具链的行为感到困惑。
  • 有趣。这是在 MacOSX 上,i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1(基于 Apple Inc. build 5658)(LLVM build 2336.1.00)
  • 我尝试了几个使用 gcc 3.4.6 和 4.8.1 的 linux 构建。 4.8.1 具有与您相同的输出(适用于枚举)。旧版本有我报告的内容。所以似乎这不是 linux/macos 依赖的,而是在 gcc 版本中修复的东西。所以我认为对于更新的代码,我会倾向于枚举。

标签: c constants declaration


【解决方案1】:

-ggdb3 选项应该为您提供宏调试信息。但这是一种不同类型的调试信息(它必须不同——它告诉调试器如何扩展宏,可能包括参数和### 运算符)所以你不能用@987654324 看到它@。

如果您的目标是在nm 中显示某些内容,那么我想您不能使用宏。但这是一个愚蠢的目标。您应该想要在调试器中实际工作的东西,对吗?在gdb 中尝试print symC,看看它是否有效。

由于可以重新定义宏,gdb 要求程序在宏存在的位置停止,以便找到正确的定义。在这个程序中:

#include <stdio.h>
int main(void)
{
  #define X 1
  printf("%d\n", X);
  #undef X
  printf("---\n");
  #define X 2
  printf("%d\n", X);
}

如果您在第一个 printfprint X 上打断,您将获得 1; next 到第二个printfgdb 会告诉你没有X;再次next,它将显示 2。

同样gdb 命令info macro foo 也很有用,如果foo 是一个接受参数的宏并且您想查看它的定义而不是用一组特定的参数扩展它。如果一个宏扩展为不是表达式的东西,gdb 不能print 它所以info macro 是你唯一可以用它做的事情。

为了更好地检查原始调试信息,请尝试使用objdump -W 而不是nm

【讨论】:

    【解决方案2】:

    但是 (1) 不是真正的 const 并且不能在所有编译器上用作数组大小。

    这可以用作所有支持 C99 和更高版本(gcc、clang)的编译器上的数组大小。对于其他人(如 MSVC),您只有最后两个选项。
    首选使用选项 3 2。enums 与 #define 常量不同。您可以使用它们进行调试。您可以使用enum 常量作为左值,这与#define 常量不同。

    【讨论】:

    • MSVC 绝对支持 C99。是否存在阻止它允许 static const 数组大小的错误? (编辑:刚刚检查过......它在 2012 年和 2010 年的 VC++ 中都能正常工作。)
    • @dvnrrs;不,不是的。您可以通过在 MSVC 上编译一个使用可变长度数组的简单程序进行测试。
    • @dvnrrs Visual Studio 确实支持 C99,但 I don't think it supports VLA.
    • 我的立场是正确的,我很抱歉。他们显然(用他们自己的话说)“精心挑选”了一些 C99 功能并且不支持完整的标准。我一直认为它是完全支持,因为它确实支持我曾经尝试使用的 C99 中的所有内容。在任何情况下,static const 数组大小是允许的,所以这个答案仍然需要更新(所有 3 个选项在 MSVC 中都是可行的)。
    • @dvnrrs; MSVC 是否允许混合类型声明?
    猜你喜欢
    • 2020-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多