41

失敗した場合と初期化されていない場合に特別な値を返すことができるようにしたい関数があります (成功するとポインターを返します)。

現在NULL、失敗した場合と初期化されていない場合に返されます-1が、これはうまくいくようです...しかし、システムをだましている可能性があります。IIRC、住所は常に正数ですよね?(ただし、コンパイラはアドレスを -1 に設定することを許可しているため、これは奇妙に思えます)。

[アップデート]

私が持っていた別のアイデア (-1 が危険な場合) は、グローバル スコープをmalloccharにし、そのアドレスをセンチネルとして使用することです。@

4

13 に答える 13

79

No, addresses aren't always positive - on x86_64, pointers are sign-extended and the address space is clustered symmetrically around 0 (though it is usual for the "negative" addresses to be kernel addresses).

However the point is mostly moot, since C only defines the meaning of < and > pointer comparisons between pointers that are to part of the same object, or one past the end of an array. Pointers to completely different objects cannot be meaningfully compared other than for exact equality, at least in standard C - if (p < NULL) has no well defined semantics.

You should create a dummy object with static storage duration and use its address as your unintialised value:

extern char uninit_sentinel;
#define UNINITIALISED ((void *)&uninit_sentinel)

It's guaranteed to have a single, unique address across your program.

于 2010-07-22T00:00:56.100 に答える
21

The valid values for a pointer are entirely implementation-dependent, so, yes, a pointer address could be negative.

More importantly, however, consider (as an example of a possible implementation choice) the case where you are on a 32-bit platform with a 32-bit pointer size. Any value that can be represented by that 32-bit value might be a valid pointer. Other than the null pointer, any pointer value might be a valid pointer to an object.

For your specific use case, you should consider returning a status code and perhaps taking the pointer as a parameter to the function.

于 2010-07-21T23:57:37.557 に答える
18

一般に、特殊な値を戻り値に多重化しようとするのは悪い設計です...単一の値でやりすぎています。戻り値ではなく、引数を介して「成功ポインター」を返す方がクリーンです。これにより、記述したいすべての条件の戻り値に多くの競合しないスペースが残ります。

int SomeFunction(SomeType **p)
{
    *p = NULL;
    if (/* check for uninitialized ... */)
        return UNINITIALIZED;
    if (/* check for failure ... */)
        return FAILURE;

    *p = yourValue;
    return SUCCESS;
}

また、通常の引数チェックも行う必要があります (「p」が NULL でないことを確認してください)。

于 2010-07-22T00:10:18.480 に答える
6

C 言語では、ポインターの「否定性」の概念は定義されていません。「負である」という特性は、主に算術的なものであり、ポインター型の値にはまったく適用できません。

ポインターを返す関数がある場合、-1その関数から意味のある値を返すことはできません。C 言語では、整数値 (ゼロ以外) は暗黙的にポインター型に変換できません。-1ポインターを返す関数から戻ろうとすると、すぐに制約違反になり、診断メッセージが表示されます。要するにエラーです。コンパイラがそれを許可している場合、それは単にその制約をあまり厳密に適用しないことを意味します (ほとんどの場合、標準化前のコードとの互換性のために行っています)。

明示的なキャストによっての値-1をポインター型に強制すると、キャストの結果は実装定義になります。言語自体はそれについて保証しません。他の有効なポインター値と同じであることが簡単に証明される場合があります。

予約済みのポインター値を作成する場合は、何もする必要はありませんmalloc。必要なタイプのグローバル変数を簡単に宣言し、そのアドレスを予約値として使用できます。一意であることが保証されています。

于 2010-07-22T02:04:09.577 に答える
4

Pointers can be negative like an unsigned integer can be negative. That is, sure, in a two's-complement interpretation, you could interpret the numerical value to be negative because the most-significant-bit is on.

于 2010-07-22T00:00:21.793 に答える
1

失敗とユニット化の違いは何ですか。unitialized が別の種類の障害でない場合は、これら 2 つの状態を分離するようにインターフェイスを再設計することをお勧めします。

おそらくこれを行う最善の方法は、パラメーターを介して結果を返すことです。そのため、戻り値はエラーのみを示します。たとえば、次のように記述します。

void* func();

void* result=func();
if (result==0)
  /* handle error */
else if (result==-1)
  /* unitialized */
else
  /* initialized */

これをに変更

// sets the *a to the returned object
// *a will be null if the object has not been initialized
// returns true on success, false otherwise
int func(void** a);

void* result;
if (func(&result)){
  /* handle error */
  return;
}

/*do real stuff now*/
if (!result){
  /* initialize */
}
/* continue using the result now that it's been initialized */
于 2010-07-22T00:15:09.227 に答える
0

実際には(少なくともx86では)、NULLポインター例外は、NULLポインターを逆参照するだけでなく、より広い範囲のアドレス(たとえば、最初の65kb)によって生成されます。これは、次のようなエラーをキャッチするのに役立ちます

int* x = NULL;
x[10] = 1;

したがって、逆参照されたときにNULLポインター例外を生成することが保証されているアドレスがさらにあります。ここで、このコード(AndreyT用にコンパイル可能になっています)について考えてみましょう。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define ERR_NOT_ENOUGH_MEM (int)NULL
#define ERR_NEGATIVE       (int)NULL + 1
#define ERR_NOT_DIGIT      (int)NULL + 2

char* fn(int i){
    if (i < 0)
        return (char*)ERR_NEGATIVE;
    if (i >= 10)
        return (char*)ERR_NOT_DIGIT;
    char* rez = (char*)malloc(strlen("Hello World ")+sizeof(char)*2);
    if (rez)
        sprintf(rez, "Hello World %d", i);
    return rez;
};

int main(){
    char* rez = fn(3);
    switch((int)rez){
        case ERR_NOT_ENOUGH_MEM:    printf("Not enough memory!\n"); break;
        case ERR_NEGATIVE:          printf("The parameter was negative\n"); break;
        case ERR_NOT_DIGIT:         printf("The parameter is not a digit\n"); break;
        default:                    printf("we received %s\n", rez);
    };
    return 0;
};

これは場合によっては役立つ可能性があります。一部のハーバードアーキテクチャでは機能しませんが、フォンノイマンアーキテクチャでは機能します。

于 2010-07-22T00:24:21.620 に答える
0

mallocこの目的には使用しないでください。不要なメモリが拘束されたままになる可能性があり (mallocたとえば、呼び出されたときに大量のメモリが使用されていて、センチネルが上位アドレスに割り当てられた場合)、メモリ デバッガー/リーク検出器を混乱させます。代わりに、単純にローカルstatic const charオブジェクトへのポインタを返します。このポインターは、プログラムが他の方法で取得できるポインターと等しくなることはなく、bss の 1 バイトを浪費するだけです。

于 2010-08-07T12:19:35.737 に答える
0

ジェームズの答えはおそらく正しいですが、もちろん、あなたができる選択ではなく、実装の選択について説明しています。

個人的には、アドレスは「直感的に」署名されていないと思います。null ポインターよりも小さいと比較されるポインターを見つけることは、間違っているように思えます。しかし~0、 と-1は、同じ整数型に対して同じ値を与えます。直感的に署名されていない~0場合、より直感的な特別なケースの値になる可能性があります-私はエラーケースの符号なし整数にかなり多く使用します。実際には違いはありませ(ゼロはデフォルトで int なので、キャストするまでは違います) が、~0見た目異なります。-1

32 ビット システム上のポインターは、32 ビットすべてを使用できますが、実際には本物の割り当てで発生する可能性は非常に低いです-1~0プラットフォーム固有のルールもあります。たとえば、32 ビット Windows では、プロセスは 2GB のアドレス空間しか持つことができず、ポインタの最上位ビットにある種のフラグをエンコードするコードがたくさんあります (たとえば、バランスを取るため)。バランスの取れたバイナリ ツリーのフラグ)。

于 2010-07-22T00:09:31.743 に答える
0

@James is correct, of course, but I'd like to add that pointers don't always represent absolute memory addresses, which theoretically would always be positive. Pointers also represent relative addresses to some point in memory, often a stack or frame pointer, and those can be both positive and negative.

So your best bet is to have your function accept a pointer to a pointer as a parameter and fill that pointer with a valid pointer value on success while returning a result code from the actual function.

于 2010-07-22T00:02:39.870 に答える
-1

NULLこの場合、唯一の有効なエラー リターンです。これは、ポインターなどの符号なし値が返される場合はいつでも当てはまります。場合によっては、ポインターが符号ビットをデータビットとして使用するのに十分な大きさでないことは事実かもしれませんが、ポインターはプログラムではなく OS によって制御されるため、この動作には依存しません。

ポインターは基本的に 32 ビット値であることを思い出してください。これが可能な負の数であるか常に正の数であるかは、解釈の問題です (つまり、32番目のビットが符号ビットとして解釈されるかデータ ビットとして解釈されるか)。したがって、0xFFFFFFF を符号付きの数値として解釈した場合は -1 になり、符号なしの数値として解釈した場合は 4294967295 になります。技術的には、ポインターがこれほど大きくなることはまずありませんが、とにかくこのケースを考慮する必要があります。

別の方法として、追加のoutパラメータ (すべての失敗に対してNULLを返す) を使用することもできますが、これには、特定のエラーを区別する必要がない場合でも、クライアントが値を作成して渡す必要があります。

GetLastError/メカニズムを使用してSetLastError追加のエラー情報を提供する (これは Windows に固有のものであり、それが問題であるかどうかは不明です) か、代わりにエラー時に例外をスローする別の方法があります。

于 2010-07-22T01:55:09.697 に答える