値0の代わりにNULLを使用できますか?


73

NULLの値の代わりにポインタを使用でき0ますか?

それとも、そのことについて何か問題がありますか?


たとえば、

int i = NULL;

の代わりとして:

int i = 0;

実験として、次のコードをコンパイルしました。

#include <stdio.h>

int main(void)
{
    int i = NULL;
    printf("%d",i);

    return 0;
}

出力:

0

確かにそれは私にこの警告を与えます、それ自体は完全に正しいです:

warning: initialization makes integer from pointer without a cast [-Wint-conversion] 

しかし、結果は同じです。


  • これで「未定義の動作」にぶつかりますか?
  • NULLこのように利用することは許されますか?
  • NULL算術式の数値として使用することに問題はありますか?
  • そして、この場合のC ++の結果と動作は何ですか?

私はの答え読み持っている「\ 0」、NULLの違いは何をして0との違いは何かについてNULL\0そして0それが正しい使用にも非常に許容され、場合、ですが、私はそこからの簡潔な情報を取得していないNULLとして、代入やその他の算術演算で操作する値。


コメントは詳細な議論のためのものではありません。この会話はチャットに移動さました
サミュエルLiew

CとC ++の2つの別々の質問をするほうが実際に良いでしょう。
Konrad Rudolph

回答:


82

値0の代わりにNULLポインターを使用できますか?

いいえ、そうするのは安全ではありませんNULLnullポインター定数であり、typeを持つことできますintが、より一般的にはtype void *(Cの場合)を持つか、さもなければ直接int(C ++> = 11の場合)に割り当てることができません。どちらの言語でも、ポインターを整数に変換できますが、そのような変換を暗黙的に実行することはできません(一部のコンパイラーはそれを拡張として提供します)。さらに、ヌルポインターを整数に変換して値0を生成することは一般的ですが、標準ではそれが保証されていません。タイプintと値が0の定数が必要な場合は、そのスペルを入力してください0

  • これで未定義の動作に入る可能性がありますか?

はいNULL。typeの値void *またはに直接割り当てられないその他の値に展開される実装ではint。標準は、そのような実装での割り当ての動作を定義していません。その動作は未定義です。

  • NULLをそのように操作することは許容されますか?

これは貧弱なスタイルであり、一部のシステムおよび特定の状況下で機能しなくなります。GCCを使用しているように見えるので、-Werrorオプションを使用してコンパイルすると、独自の例では機能しなくなります。

  • 算術式の数値としてNULLを使用することに問題はありますか?

はい。数値であるとは限りません。0を意味する場合は、0を書き込みます。0は明確に定義されているだけでなく、より短くて明確です。

  • そして、その場合のC ++の結果はどうですか?

C ++言語は、変換に関してCより​​も厳密でNULLあり、のルールが異なりますが、実装によって拡張機能が提供される場合もあります。繰り返しになりますが、0を意味する場合は、それを記述してください。


4
「より一般的には型を持つvoid *」はCにのみ当てはまることを指摘する必要があります。これ void *はC ++の正当な型ではありません(void*他のポインター型に割り当てることができないため)。C ++ 89とC ++ 03では、実際には型であるNULL 必要ありますintが、それ以降のバージョンでは型にすることができます(通常はそうです)nullptr_t
Martin Bonnerがモニカをサポートする

また、間違って変換するvoid*には、int未定義の動作です。そうではない; これは実装で指定された動作です。
Martin Bonnerがモニカをサポートする

@MartinBonnersupportsMonica、ポインタが整数に変換されることをCが指定するコンテキストでは、変換の結果は実際に実装指定ですが、それは私が話していることではありません。これは、未定義の動作を持つ整数型の左辺値へのポインターの割り当て(キャストを介して明示的に変換しない)です。この言語では、自動変換は定義されていません。
ジョンボリンジャー

@MartinBonnersupportsMonica、私はC ++の考慮事項をより包括的になるように編集しました。いずれの場合でも、中心的なテーマは両方の言語に等しく適用されます。整数0が必要な場合は、適切な型の整数定数として明示的に記述します。
ジョンボリンジャー

31

NULLいくつかのnullポインタ定数です。Cでは、値を持つ整数定数式0またはにキャストされるそのような式であるvoid*可能性があります。つまり、ゼロと交換可能に使用すると想定できませんNULL。たとえば、このコードサンプルでは

char const* foo = "bar"; 
foo + 0;

2つのポインター間の加算(異なるポインタータイプの単独)が定義されていないため、0withの置換NULLは有効なCプログラムであるとは限りません。制約違反のために診断が発行されます。追加のオペランドは無効になります


C ++については、状況が多少異なります。void*他のオブジェクトタイプへの暗黙的な変換がないことNULL0、これまでC ++コードと同様に定義されていたことを意味します。C ++ 03では、おそらくそれでうまくいくでしょう。しかし、C ++ 11以降では、それを合法的nullptrキーワードとして定義できます。std::nullptr_tポインタ型に追加されない可能性があるため、ここでもエラーを生成します。

NULLがとして定義されている場合nullptr、実験も無効になります。からstd::nullptr_t整数への変換はありません。そのため、より安全なnullポインター定数と見なされます。


完全を期すために、0Lもnullポインター定数でありNULL、両方の言語と同様に使用できます。
eerorika

1
@jamesqf標準では、値0の整数定数はnullポインター定数であると規定されています。したがって、0LはNULLポインター定数です。
eerorika

1
@eerorika:世界が必要としているもの、現実を無視する標準:-) '80286アセンブリを正しく覚えていると、farポインターを単一の操作として割り当てることもできないため、コンパイラー作成者は特別な操作を行う必要があります-それをケース。
jamesqf

2
パー@jamesqf Cよくある質問作る:、再0ヌルポインタ定数を「明らかに間違った仮定に行われたすべての現存悪い書かれたCコードへのSOPとして」
アンドリュー・ヘンレ

3
@jamesqf、値0のすべての整数定数がnullポインター定数(C)であることは、ハードウェアポインターの実装とは関係ありません。また、標準Cでは、nearポインターとfarポインターの違いは認識されませんが、ポインター間の割り当てはサポートされています。また、(一部の)ポインター比較もサポートします。これは、286などのセグメント化されたアドレッシング形式に興味深い問題をもたらします。
ジョンボリンジャー

21

値0の代わりにNULLポインターを使用できますか?

int i = NULL;

ルールは言語とそのバージョンによって異なります。できる場合とできない場合があります。とにかく、すべきではありません。運が良ければ、コンパイラーはそれを試みたときに警告を発し、さらに良いことに、コンパイルに失敗します。

C ++では、C ++ 11より前(C ++ 03からの引用):

[lib.support.types]

NULLは、この国際標準で実装定義のC ++ nullポインター定数です。

nullポインタ定数を整数として使用することはほとんど意味がありません。しかしながら...

[conv.ptr]

nullポインタ定数は、整数型の整数定数式(5.19)の右辺値であり、ゼロに評価されます。

したがって、たとえそれが無意味であっても、技術的には機能します。この専門性のために、悪用される不十分に書かれたプログラムに遭遇するかもしれませんNULL

C ++ 11以降(最新のドラフトからの引用):

[conv.ptr]

ヌルポインタ定数はゼロの値を有する整数リテラル([lex.icon])であるか、タイプSTDのprvalue :: nullptr_t

A std​::​nullptr_­tは整数に変換できないためNULL、言語実装による選択に応じて、整数としての使用は条件付きでのみ機能します。

PS nullptrは、タイプのprvalueですstd​::​nullptr_­t。C ++ 11より前のバージョンでプログラムをコンパイルする必要がない限り、のnullptr代わりにを常に使用する必要がありますNULL


Cは少し異なります(C11ドラフトN1548からの引用):

6.3.2.3言語/変換/その他のオペランド/ポインタ

3値が0の整数定数式、 またはtypevoid *キャストされた式は、nullポインター定数と呼ばれます。...

したがって、ケースはC ++ 11以降と似ています。つまりNULL、言語実装による選択に応じて、条件付きで作業を乱用します。


10

はい。ただし、実装によってはキャスト必要になる場合あります。しかし、そうです、それ以外の場合は100%正当です。

それは本当に、本当に、本当に悪いスタイルですが(言うまでもなく?)

NULLは、または実際にはC ++ではなく、Cです。ただし、標準に、多くのCの遺産と同様に、技術的にNULLC ++を作成する2つの句([diff.null]と[support.types.nullptr])があります。これは、実装定義のnullポインター定数です。したがって、たとえそれが悪いスタイルであったとしても、それは技術的には可能な限りC ++です。脚注
指摘されているように、可能な実装はor かもしれませんが、そうではありません00L (void*)0

NULLもちろん、標準では明示的にそうは言われていませんが、0またはの後に残る唯一の選択肢です0Lnullptr。そんなことはほとんどありませんが、それは法的な可能性です。

コンパイラが表示した警告は、コンパイラが実際に準拠していないことを示しています(Cモードでコンパイルした場合を除く)。というのも、警告によると、nullポインターは変換されため(nullポインターnullptrではなくnullptr_t、区別されます)、明らかにの定義NULLは確かに(void*)0であり、そうではない可能性があります。

どちらの場合も、2つの正当な(つまり、コンパイラが壊れていない)ケースが考えられます。どちらか(現実的なケース)、NULLのようなものである00L、そして、あなたが持っている「ゼロまたは1」整数への変換を、あなたが行ってもいいです。

またはNULL確かにnullptrです。その場合、あなたは比較だけでなく、明確に定義の変換についての保証を持っている個別の値持っているからしかし運悪くない、整数整数。ただし、明確に定義されたbool(結果としてfalseboolへの変換があり、整数への明確に定義された変換(結果として0)があります。

残念ながら、これは2つの変換であるため、[conv]で指摘されているように、「0または1」以内ではありません。したがって、実装がNULLとして定義されnullptrている場合、コードを正しくするために明示的なキャストを追加する必要があります。


6

CのFAQから:

Q: NULLと0がnullポインター定数として同等である場合、どちらを使用すればよいですか?

A:同等NULLであるの0は、ポインターコンテキスト内のみです。NULLすべきではない 0の別の種類が必要なときにそうすることが、間違った文体メッセージを送信するため、それが動作する場合でも、使用すること。(さらに、ANSIはNULLの定義をにすることができますが((void *)0)、これは非ポインターコンテキストではまったく機能しません。)特に、NULLASCIIヌル文字(NUL)が必要な場合は使用しないでください。独自の定義を提供する

http://c-faq.com/null/nullor0.html


5

免責事項:私はC ++を知りません。私の答えはC ++のコンテキストで適用されることを意図していません

'\0'int値がゼロので、ちょうど100%とまったく同じ0です。

for (int k = 10; k > '\0'; k--) /* void */;
for (int k = 10; k > 0; k--) /* void */;

ポインタの文脈では0NULL100%同じです。

if (ptr) /* ... */;
if (ptr != NULL) /* ... */;
if (ptr != '\0') /* ... */;
if (ptr != 0) /* ... */;

すべて100%相当です。


についての注意 ptr + NULL

のコンテキストはポインタのコンテキストでptr + NULLはありません。C言語でのポインタの追加に関する定義はありません。ポインタと整数を加算(または減算)できます。内ptr + NULLのいずれかであればptr、またはNULLポインタであり、他方はでなければならないので、整数ptr + NULL効果的である(int)ptr + NULLか、ptr + (int)NULLとの定義に依存ptrし、NULLいくつかの行動が期待できる:それはすべての作業、ポインタと整数との間の変換のための警告、コンパイルに失敗し、.. 。


#define NULL (void *)0以前見たことがあります。NULLとプレーン0は100%同等ですか?
machine_1

2
ポインタのコンテキストでは、はい...私の答えで強調された状態、ありがとう
pmg

@phuclv:私はC ++についてまったく知りません。私の答え(括弧内のビットを除く)はCについてです
pmg

@phuclv ptr + NULLNULLポインタのコンテキストで使用していません
pmg

3
@JesperJuhl:ポインターのコンテキストでは100%等価です。私は何は考えていないnullptrですが、しかし、((void*)0)および0(または'\0'...)ポインタの文脈で等価ですif (ptr == '\0' /* or equivalent 0, NULL */)
PMG

5

いいえ、使用することはもう推奨されていませんNULL(古い方法のポインター初期化)。

C ++ 11以降:

キーワードnullptrは、ポインタリテラルを示します。これはタイプstd :: nullptr_tのprvalueです。からnullptr任意のポインター型のnullポインター値への暗黙的な変換と、メンバー型への任意のポインターが存在します。同様の変換がstd::nullptr_t、マクロのほかに、型の値を含むすべてのnullポインター定数にも存在しますNULL

https://en.cppreference.com/w/cpp/language/nullptr

実際には、std :: nullptr_tはnullポインターリテラルの型ですnullptr。それは、それ自体がポインター型でもメンバー型へのポインターでもない特殊な型です。

#include <cstddef>
#include <iostream>

void f(int* pi)
{
   std::cout << "Pointer to integer overload\n";
}

void f(double* pd)
{
   std::cout << "Pointer to double overload\n";
}

void f(std::nullptr_t nullp)
{
   std::cout << "null pointer overload\n";
}

int main()
{
    int* pi; double* pd;

    f(pi);
    f(pd);
    f(nullptr);  // would be ambiguous without void f(nullptr_t)
    // f(0);  // ambiguous call: all three functions are candidates
    // f(NULL); // ambiguous if NULL is an integral null pointer constant 
                // (as is the case in most implementations)
}

出力:

Pointer to integer overload
Pointer to double overload
null pointer overload

問題は、整数のNULLを0に割り当てることです。この意味で、NULLの代わりにnullptrを使用しても何も変化しません。
ivan.ukr

彼は言葉を「NULLポインター」として使用しました。
Mannoj

ちなみに、C ++には、C ++ 11以降のNULL概念はありません。著者は、constexprの使用について混乱している場合や、古い方法の初期化を定義している場合があります。en.cppreference.com/w/cpp/language/default_initialization
Mannoj
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.