1

我对 C/C++ 并不是特别陌生,但今天我发现了一些我没想到的东西。这在 gcc 中编译:


/* test.c */
#include <stddef.h> // !

typedef unsigned long int size_t; // NO ERROR
typedef unsigned long int size_t; // NO ERROR

int
main(void)
{
  typedef unsigned long int size_t; // NO ERROR
  return 0;
}

这不会:


/* test.c */
#include <stddef.h>

typedef unsigned long int size_t; // NO ERROR
typedef unsigned long int size_t; // NO ERROR

int
main(void)
{
  typedef unsigned long int size_t; // NO ERROR
  typedef unsigned long int size_t; // ERROR
  return 0;
}

这也不是:


/* test.h */ // ! header
typedef unsigned long int size_t;
typedef unsigned long int size_t; // ERROR

同样在 g++ 中编译:


/* test.h */ // ! header
#include <cstddef>

inline void* operator new(size_t, void* p) throw() { return p; }

这不会:


/* test.h */ // ! header
#include <new> // !

inline void* operator new(size_t, void* p) throw() { return p; } // ERROR

这样做:


/* test.cc */
#define _NEW

#include <new> // !
#include <iostream>
#include <cstdlib>

using std::cout;
using std::endl;

inline void* operator new(size_t size) throw() // NO ERROR EXPECTED
{
  cout << "OPERATOR NEW CALLED" << endl;
  return malloc(size);
}

inline void* operator new(size_t, void* p) throw() // NO ERROR
{
  cout << "PLACEMENT NEW CALLED" << endl;
  return p;
}

int main()
{
  char *buffer[4];
  int *i = new (buffer) int;
  int *j = new int;
  return 0;
}

(替换标准的 new 操作符在上述所有情况下都有效。替换替换的 new 操作符是非法的,我知道。)

很容易看到一种模式,但是有人可以给我一个“标准”的解释吗?为什么我可以在 .c 或 .cc 文件中做我在 .h 文件中不能做的事情(重新定义旧的 typedef,替换非法替换的函数)?


谢谢回复。我省略了一些代码,包括标题保护。在最后一个 .cc 示例中,我将 HTML 编码为 << 为 & gt ;> 错误地忘记包含 cstdlib。我已经修复了代码,所以它可以编译。然而,我忽略的另一件事是#define _NEW,它被证明是至关重要的。显然在 GNU 中,<new> 的标头保护定义了 _NEW,所以我在这里定义它阻止了标准新标头的包含,所以我替换工作了

m@m:~/Desktop/Library$ ./a.out
安置新呼叫
操作员新呼叫

所以,是的,唯一无法解释的是我为什么可以在 .c/.cc 文件中多次重新键入定义内容,但不能像这样在 .h 中重新键入:

/* test.c */
#include <stddef.h>

typedef unsigned long int size_t; // NO ERROR
typedef unsigned long int size_t; // NO ERROR

int
main(void)
{
  typedef unsigned long int size_t; // NO ERROR
  return 0;
}

反正我也不想那样做,只是想知道。

编辑:谢谢,这真的回答了所有问题。将其更改为 xyz 不允许在给定范围内进行多个定义,这感觉不错。:) 我做这些小测试的原因是我正在为一个小型操作系统编写 C 和 C++ 的一个子集,但是由于我使用现有的库而没有删除任何东西来帮助我进行测试,所以我正在尝试弄清楚如何确保(1)我的代码在测试中被调用(例如我的新位置)和(2)我的代码不会与 GNU 库发生冲突。(1)的答案现在看起来很明显。我只是将标准 Xh 中的标头保护宏#define 到我的 Xh 中:)。

4

2 回答 2

3

在 MacOS X 10.4.11(古老的 - 但计算机也是如此)上使用 GCC 4.0.1,带有“test.h”的示例有效 - 或者我对它的改编。看来您可以拥有任意数量的(相同的)“size_t”全局类型定义 - 我有 5 个版本为stddef.h.

main 中的第一个 typedef 显然是合法的;它是一个新的范围,可以为名称定义新的含义。

C99 基本原理说:

在 C89 中,可以在内部块中使用显式包含类型名称的声明重新声明 typedef。该规则避免了是否将 typedef 作为类型名称或重新声明的候选者的歧义。在 C99 中,不允许隐式 int 声明,因此这种歧义 [原文如此!] 是不可能的,并且不再需要该规则。

稍后,在讨论标准标头时,它还说:

C89 委员会决定让库头文件“幂等”,也就是说,它们应该可以包含任意次数,并且可以以任意顺序包含。这一要求反映了广泛的现有实践,可能需要在标头中进行一些保护性包装,以避免例如重新定义 typedef。为了确保可以使这种保护性包装起作用,并确保 typedef 的正确范围,标准头文件只能包含在任何声明之外。

因此,很明显,typedef通常不允许在单个范围(例如文件范围)中重新定义 a。因此,我认为,多个外部重新定义size_t可能是 GCC 的一个特性或一个错误 - 实际上是一个特性。

如果您更改size_txyz,则会收到更多错误。
于 2009-04-18T05:10:09.613 回答
2

我不确定typedef在您的情况下多个 s 是如何编译的。请记住,标头不会自行编译,而是与实现类一起编译。此外,您的标头缺少标头保护或#pragma once指令。您正在使用哪些选项?

至于放置new- 标准(18.5.1.3)明确禁止 - 所以这是行不通的。

这里没有模式——除了重新定义已经声明的符号。

顺便说一句:您的示例.c或示例均未使用Comeau.cpp编译。

于 2009-04-18T04:27:33.773 回答