重複の可能性:
タイプセーフとは何ですか?
型安全性とは何ですか?
私はc ++ベクトルについて読んでいて、Cのmemcpy
関数とprintf
関数はタイプセーフではないと言われました。ここの記事: http://en.wikipedia.org/wiki/Vector_(C%2B%2B) 。
質問:簡単な英語で、型安全性とは何ですか?「型安全性」の選択肢は何ですか?
型安全性とは、コンパイラが正しい型を使用しているかどうかをチェックできることを意味します。たとえば、printf
を使用している場合、次のように記述してプログラムを誤ってクラッシュさせる可能性があります。
printf("The meaning of life is %s", 42);
42は整数であり、文字列ではないためです。
型安全性 は、コンパイラが(互換性のない)データ型を混在させていないことを確認するのに役立つことを意味します。
たとえば、memcpy
を呼び出すと、関数(およびコンパイラ)はメモリ内の2つのポインタのみを認識し、データのコピーを正常に開始します。これは、次のような互換性のないデータ型を混在させることができることを意味します。
SomeClass a;
AnotherClass b;
memcpy((void*)&a, (void*)&b, sizeof(b));
型安全性を得るための多くのアプローチがあります。テンプレートを使用してmempcy()のラッパーを作成し、2つのポインターが同じデータ型を指すようにするか、他の方法を使用できます。
すでにSTLからのベクトルを使用しているので、多かれ少なかれタイプセーフな実装をすでに使用しています。
型安全性は、変数が正しい型であるかどうかをチェックするコンパイラーの使用法を管理します。 Cはデータ型の安全性について非常に緩いです。たとえば、これは実際にはANSI C標準にあり、データ型char
に対して型の昇格が発生すると述べています。この割り当ての例で、これを説明します。
char ch = 32; /* that is a space character accordingly to ASCII */
int n = ch + 3;
ch
変数が「プロモート」されてint
と入力されることに注目してください。それは合法ですが、それがあなたが暗示しているものである場合は、綿密な調査が必要です。
C#コンパイラなどのコンパイラでは、これが発生しません。これが、Cでキャストの演算子が使用されている理由です。たとえば、次のようになります。
int n = (int)3.1415926535f;
ちなみに、円周率の値は、n
の値が3になるということです。
上記は型安全性を説明するのに役立ち、Cはこの点で非常に緩いです。
変数の使用法と意味を制限するために、Java、C#などの現代言語での型安全性はより厳格です。 PHPは、これを行うことができるルーズタイピングの優れた例です。
$myvar = 34;
$myvar = $myvar + "foo";
は$myvar
整数、または浮動小数点か文字列か。ここでの型安全性は、バグにつながる可能性のある意図と、何が起こっているのかを理解しようとする幸せなデバッグセッションについてはあまり明確ではありません。
お役に立てれば
とにかくウィキペディアにいたので: 型安全性 。
型安全性とは、大まかに言えば、言語が誤って型を混同することを禁止していることを意味します。
memcpy
はタイプセーフではありません。int
のメモリをchar
配列に簡単にコピーして、意味のないデータになってしまう可能性があるためです。 printf
は、%i
形式指定子に文字列を指定できるため、タイプセーフではありません。繰り返しになりますが、文字列はint
として解釈され、ゴミになってしまいます。 (ちなみに、VC++コンパイラはいくつかの状況でフォーマット文字列をチェックします。)
std::vector<T>
は、指定されたタイプT
の値のみを入力できるため、タイプセーフです。 (もちろん、明示的な型キャストを行うことはできますが、重要なのは、型安全ではないことを行うことについて明示的でなければならないということです)。
「型安全性」とは、コンパイラが正しい型で正しいことを行っていることを確認することを意味します(たとえば、バナナをオレンジとして処理しようとした場合、または整数の出力を期待する関数に文字列を渡そうとした場合、コンパイラエラーがトリガーされます)。 。
void*
が登場すると、型安全性は(ほとんど)ウィンドウの外に出ます。これは、あらゆるものを指すことができるポインターであり(関連する型を完全に認識していません)、言語は完全にそれをそのままにします。プログラマーの手(たとえば、void*
は、元の型にキャストバックされることを除いて、ほとんど何にも適していません。それは何でも表すことができますが、使用する前にそれが何であるかを知る必要があります)。
型の安全性は、printfのような可変個引数関数でも発生します(コンパイラーは、引数の数とその型が何であるかを気にしません-ここでも、フォーマット文字列が引数とその型と一致することを確認するのは呼び出し元の責任です) 。
Memcpy(配列およびコンテナー用)のタイプセーフな代替手段は、std::copy
の<algorithm>
である可能性があります-関連するすべてのタイプが特定の要件を満たしている場合はmemmoveの観点から実装でき、そうでない場合は割り当てを実行します-一部のクラスでパブリックインターフェイスをバイパスして、メモリ内で移動/コピーするだけで、特定の不変条件を壊すことができます(たとえば、memcpyを使用してコピーを作成すると、重要なコピーコンストラクターを持つクラスが誤動作すると思います) 。
C I/Oルーチンのタイプセーフな代替手段はiostreamです(フォーマット文字列の利点が必要な場合は、boost::format
)。
「型安全性」は「型システム」を使用して、エラーがプログラム内で伝播されないようにします。たとえば、型の安全性がなければ、望ましくない方法で文字列型を浮動小数点型に(サイレントに)追加できる可能性があります。
あなたが話しているインスタンスでは、memcpy()とprintf()、型安全性の欠如は、関数が引数をどのように扱うかによるものです。たとえば、memcpy(arg1、arg2、len)の場合、lenメモリアドレスarg2で始まるバイトはメモリアドレスarg1にコピーされます、arg1が指すバイト数に関係なく、プログラムの他の部分を上書きする可能性があります。
これは、その型にとって意味のない方法で型を使用しようとしても、コンパイラが警告を生成しないことを意味します。たとえば、以下は未定義の動作であり、実際には、ポインタのビットをfloatのビットにコピーしますが、実際にはまったく意味がありません。 sizeof(char*)
> sizeof(float)
の場合、f
が存在する場所のすぐ上にあるメモリ位置が上書きされます。
float f;
char *c = someString();
memcpy(&f, &c, sizeof(char*));
型安全性とは、コンパイル時にすべての変数に専用の型を強制するコーディングパラダイムを指します(例:int a = 4; double d = 100.0; struct ms {char s;} mystruct;
変数の型が「失われる」ことはありません。タイプをaからbに変更する場合は、明示的または暗黙的な変換を定義する必要があります。
printf
はnotタイプセーフです。これは、可変引数リストで引数を渡すためです。
float f = 1.f;
printf("This is a float: %f\nAnd this is a string: %s",f,f);
printf
は、彼女が受け取る値の種類を知りません。フォーマット文字列は実装によって検出に使用されますが、文字列が間違っていると、コンパイル時に利用可能な型情報がないため、実装はそれを検出する機会がありません。上記のprintf
呼び出しは、壊滅的な結果になる可能性が最も高くなります。printfは、2番目のパラメーターとして文字列を予期しますが、浮動小数点数を取得します。
答えの短いバージョン:
class Person;
person.DoSomething(); // This is type safe.
void * p = &person; // You can now start doing unsafe things with p.
人をmemcpyに渡すことはできません。それは記憶だけを知り、気にします。バイト。
Memcpy関数のシグネチャは
void *memcpy (void* destination, const void* source, size_t num);
ご覧のとおり、コピーに関連するポインタについては何も想定しておらず、単なるポインタです。したがって、たとえば、ints
の範囲をfloats
の範囲にコピーする場合、コンパイラはそれについて文句を言いません。
Type Safetyは、ある種の誤ったコードがコンパイルされる(そして最近実行される)のを防ぐことにより、開発者が特定のエラーを回避するのに役立つツールです。ソースコードのセマンティックな側面を分析して、タイプとタイプ間の変換が一般的にコホーレントであるかどうかを確認します。
どういう意味ですか?これは、プログラムが型チェックフェーズに合格した場合、実行時に特定の種類のエラーを生成しないようにすることができることを意味します-時間。
もちろん、このチェックを強制的に行わないようにする必要がある場合もあります。そのため、キャストを使用して、物事を希望どおりに強制することができます。別の例を考えてみましょう。malloc
:次のように定義されています
void* malloc (size_t size);
したがって、たとえばfloats
にポインタを割り当てたい場合は、次のようにします。
float* ptr = (float*)malloc(sizeof(float*)*COUNT);
関数の結果をfloat*
にキャストする必要があります。そうしないと、タイプチェックでvoid*
のfloat*
への割り当てが検出されますが、void*
は一般的すぎて割り当てることができません。だから:TYPE CHECK FAIL!
そのため、memcpy
はタイプセーフではありません。何もチェックせず、ポインタから別のポインタにコピーするだけです。