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 演算子を置き換えることは違法です。私は知っています。)

パターンを見るのは簡単ですが、誰かが「標準的な」説明をしてくれませんか? .h ファイルではできないことを .c または .cc ファイルで実行できるのはなぜですか (古い typedef を再定義し、置換が違法な関数を置換します)。


返信いただきありがとうございます。ヘッダー ガードを含む一部のコードを省略しました。最後の .cc の例では、 << を & gt として HTML エンコードしました。> cstdlib を含めるのを忘れていました。コンパイルできるようにコードを修正しました。ただし、もう 1 つ省略していたのは #define _NEW で、これは非常に重要であることがわかりました。どうやら GNU では <new> のヘッダー ガードで _NEW が定義されているため、ここでそれを定義すると、標準の新しいヘッダーを含めることができなくなり、置換が機能しました。

m@m:~/Desktop/Library$ ./a.out
プレースメント 新しいと呼ばれる
オペレーターが新しく呼び出されました

ええ、説明されていない唯一のことは、.c/.cc ファイルで複数回 typedef を再定義できるのに、.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 から Xh にヘッダー ガード マクロを #define するだけです :)。

4

2 に答える 2

3

MacOS X 10.4.11(古いものですが、コンピューターもそうです)でGCC 4.0.1を使用すると、「test.h」を使用した例が機能します-または、私の適応は機能します。'size_t' の (同一の) グローバル typedef を好きなだけ持つことができるようですstddef.h

main 内の最初の typedef は「明らかに」有効です。これは新しいスコープであり、名前の新しい意味を定義できます。

C99の根拠は次のように述べています。

C89 では、型名を明示的に含む宣言を使用して、内側のブロックで typedef を再宣言できました。この規則により、typedef を型名として使用するか、再宣言の候補として使用するかについてのあいまいさが回避されました。C99 では、暗黙的な int 宣言が許可されていないため、このあいまいさ [sic!] は不可能であり、ルールは不要になりました。

後で、標準ヘッダーについて説明すると、次のようにも述べています。

C89 委員会は、ライブラリ ヘッダーを「冪等」にすることを決定しました。この要件は、広く普及している既存の慣行を反映しており、たとえば typedef の再定義を回避するために、ヘッダー内にいくつかの保護ラッパーが必要になる場合があります。このような保護ラッピングが確実に機能するようにし、typedef の適切なスコープを確保するために、標準ヘッダーは宣言の外にのみ含めることができます。

したがって、明らかにtypedef、単一のスコープ (ファイル スコープなど) での a の再定義は一般に許可されていません。size_tしたがって、 の複数の外部再定義は、おそらく GCC の機能またはバグのいずれかであると思います。実際には、機能です。

に変更size_tするとxyz、さらに多くのエラーが発生します。
于 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 に答える