これは私の警告です。
Missing sentinel in function call
削除する方法.
linuxとGCCコンパイラを使用しています。
NULL
で配列宣言を終了していないようです。 nullがないと、ランタイムが配列の終了位置と次のメモリビットの開始位置を認識しないため、メモリが少し変になることがあります。
私はちょうど同じ問題に出くわしました。私のためにそれを引き起こしていたコードは...
execl("/bin/bash", "/bin/bash", fname, '\0');
しかし、それは...
execl("/bin/bash", "/bin/bash", fname, (char *)0);
最初のバージョンの問題は、パラメーターのリストがヌルポインターで終わることを意図していることです。しかし、「\ 0」はヌルポインターではなく、ヌル文字です。そのため、値(0)は正しいです。タイプが間違っているだけです。
(char *)0もゼロですが、char pointerとしてキャストされます。これはヌルポインターです(つまり、アドレス0を指します)。これは、システムがパラメーターリストの終了位置を認識できるようにするために必要です。これにより、最後のパラメーターリストの後にパラメーターのスキャンが継続されなくなります。これを行うと、無効なポインタが取得され、任意のメモリを指す可能性があります。これにより、セグメンテーションエラーが発生する可能性があります。
その(char *)0はセンチネルと呼ばれ、最初の例では欠けていました。
最後に、NULLは(void *)0として定義されていることに注意してください。
execl("/bin/bash", "/bin/bash", fname, NULL);
同様に機能し、もう少し便利です。 (@mahに感謝します)。
Xcodeでは、objective-cでコーディングしていて、変数パラメーターリストを使用するメソッドを使用している場合、nilオブジェクトを最後に追加する必要がありますリスト。
例えば:
N SArray * names = [NSArray arrayWithObjects:@ "Name1"、@ "Name2"]; //上記の警告が発生します
ただし、NSArray * names = [NSArray arrayWithObjects:@ "Name1"、@ "Name2"、nil]; //正しい
これが役立つことを願っています!
NULL
の代わりに(void*)0
または(void *)NULL
を使用します
警告は、 Jichao で説明されているsentinel
関数属性によって生成されます。ドキュメント: https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gcc/Function-Attributes.html#index-g_t_0040code_007bsentinel_007d-function-attribute-3226
このコンテキストでの有効なNULLは、任意のポインタータイプでゼロとして定義されます。システムが整数型でNULLマクロを定義している場合、明示的なキャストを追加する必要があります。 GCCは、stddef.hをNULLを適切に再定義するコピーに置き換えます。
ANSI Cでは、NULLは0
または(void *)0
のいずれかですが、ポインターのみがこの属性を満たします。以下も参照してください。 NULL、 '\ 0'と0の違いは =
0
は通常のポインター引数を使用してNULLポインターにキャストされますが、varargを使用してゼロポインターとゼロポインターを区別することはできないため、その違いは重要です。参照: ポインタのデフォルト引数プロモーション
確かに、GCCはNULLがポインターであることを保証し、NULLはstddef.h
(一部のANSI CヘッダーはGCCにあり、他はglibcにある)でGCCによって提供されるため、sentinel
は、開始するGCC拡張機能です。
しかし、念のため、(void *)NULL
または(void*)0
と書いています。
さらに、POSIXとman execl
の両方は、 execl()関数 終了のために「ヌルポインター」を必要とします。これは、センチネルの使用の典型例です。
NULL
は、「nullポインター」ではない「nullポインター定数」であるため、そこで動作することが保証されていません。したがって、(void *)0
を使用することで、よく知られたAPIとより一貫性があります。参照: Is((void *)0)a null pointer constant? || (int *)0はNULLポインターですか?
GCC 5.2.0はexecl
組み込みとして を提供し、プログラムでsentinel
を設定します:
DEF_EXT_LIB_BUILTIN (BUILT_IN_EXECL, "execl", BT_FN_INT_CONST_STRING_CONST_STRING_VAR, ATTR_SENTINEL_NOTHROW_LIST)
属性を持たないexecl
のglibc 2.21バージョンもあります。
簡単な例:
_#include <stdio.h>
#include <stdarg.h>
void print_strings(const char* first, ...) __attribute__((sentinel));
void print_strings(const char* first, ...)
{
va_list ap;
const char* tmp;
if (!first)
return ;
printf("%s\n", first);
va_start(ap, first);
while (1) {
tmp = va_arg(ap, const char*);
if (tmp == 0)
break;
printf("%s\n", tmp);
};
va_end(ap);
}
int main()
{
print_strings("how are you?", "i'm fine", "and you?", NULL);
return 0;
}
_
主に、末尾のNULL
を持たないprint_strings("how are you?", "I'm fine", "and you?")
としてprint_stringsを呼び出すと、GCCは「番人がいない」と文句を言います。
センチネル関数属性を関数_print_strings
_に追加するからです。変数の引数がNULLで終わることを指定するgcc拡張です。したがって、変数引数をNULLで終了しない場合、コンパイラはそれを検出して警告を表示できます。
Sentinelは、保護または保護することを意味します。したがって、このコンテキストではエラーが発生しているため、保護パラメーターが欠落している可能性があります。配列または辞書を使用している場合は、オブジェクトに名前を付けた後、キーワードnilで終了することを確認してください。
例:
[NSDictionary dictionaryWithObjectsAndKeys:@"UIAlertView", kSectionTitleKey,
@"Show Custom", kLabelKey,
@"AlertsViewController.m - alertOtherAction", kSourceKey];
上記のステートメントは、「関数呼び出しで番兵が見つかりません」というエラーを生成します
正しい構文:
[NSDictionary dictionaryWithObjectsAndKeys:@"UIAlertView", kSectionTitleKey,
@"Show Custom", kLabelKey,
@"AlertsViewController.m - alertOtherAction",kSourceKey,nil];
次のようにNULLを渡すことができます:execl( "/ bin/bash"、 "ls"、 "-l"、NULL);最後のパラメーターは常に0でなければなりません。これはNULLターミネーターです。引数リストは可変であるため、Cが終了するタイミングを伝える何らかの方法が必要です。