web-dev-qa-db-ja.com

警告:「文字列リテラルではなく、フォーマット引数なしでフォーマットしてください」

最新のXcode 3.2.1とSnow Leopardにアップグレードしてから、警告が表示されました

「文字列リテラルではなく、フォーマット引数なしのフォーマット」

次のコードから:

NSError *error = nil;

if (![self.managedObjectContext save:&error]) 
{
    NSLog([NSString stringWithFormat:@"%@ %@, %@", 
       errorMsgFormat, 
       error, 
       [error userInfo]]);      

}

errorMsgFormatがフォーマット指定子を持つNSStringである場合(例:"print me like this: %@")、上記のNSLog呼び出しの何が問題になっていますか?そして、警告が生成されないように修正するための推奨される方法は何ですか?

109
Alexi Groove

ブラケットを正しくネストしていますか? NSLog()は引数を1つだけ取るのが好きではないと思います。また、すでにフォーマットが行われています。なぜこれをしないのですか?

NSLog(@"%@ %@, %@", 
   errorMsgFormat, 
   error, 
   [error userInfo]);              

または、errorMsgFormatは単一のプレースホルダーを持つフォーマット文字列であると言うので、これをしようとしていますか?

NSLog(@"%@, %@", [NSString stringWithFormat:errorMsgFormat, error], 
   [error userInfo]);              
112
Sixten Otto

これはセキュリティ上の問題であるため、Xcodeは不満を言っています。

これはあなたのものに似たコードです:

NSString *nameFormat = @"%@ %@";
NSString *firstName = @"Jon";
NSString *lastName = @"Hess %@";
NSString *name = [NSString stringWithFormat:nameFormat, firstName, lastName];
NSLog(name);

その最後のNSLogステートメントは、これと同等のものを実行します:

NSLog(@"Jon Hess %@");

これにより、NSLogはもう1つの文字列引数を検索しますが、引数はありません。 C言語の動作方法により、スタックからランダムなガベージポインターを取得し、NSStringのように処理しようとします。ほとんどの場合、プログラムがクラッシュします。文字列にはおそらく%@が含まれていませんが、いつかは含まれる可能性があります。書式文字列(printf、scanf、NSLog、-[NSString stringWithFormat:]、...)を受け取る関数の最初の引数として、明示的に制御するデータを含む書式文字列を常に使用する必要があります。

Ottoが指摘しているように、おそらく次のようなことを行う必要があります。

NSLog(errorMsgFormat, error, [error userInfo]);
157
Jon Hess

最終回答:Jon Hessが言ったように、フォーマット文字列を期待する関数にWHATEVER文字列を渡すため、セキュリティの問題です。つまり、どの文字列内でもすべての書式指定子を評価します。素晴らしいものがなければ、悪いことは起こりえます。

適切なことは、フォーマット文字列を直接使用することです、例えば

NSLog(@"%@", myNSString);

そうすれば、myNSStringにフォーマット指定子があっても、NSLogによって評価されません。

38
Alex Whittemore

警告ISは実際の警告です。言語の動的な使用では、文字列に対してランタイムを実行することができます(つまり、新しい情報を挿入したり、プログラムをクラッシュさせます。)しかし、それがこのようなものであるべきであり、あなたが本当にそれについて警告されたくないことを知っていれば、強制的に抑制することができます。

#pragma GCC diagnostic ignored "-Wformat-security"

コンパイルの警告を一時的に無視するようにGCCに指示します。これも何も解決していませんが、実際に問題を修正する良い方法を見つけることができない場合があります。

編集:clangの時点で、プラグマが変更されました。これを参照してください: https://stackoverflow.com/a/17322337/3937

13
Qrikko

警告を無効にするためにnilを渡しましたが、おそらくそれはあなたのために働くでしょうか?

NSLog(myString、nil);

10
Martytoof

それを修正する最も簡単な方法は、@"%@",NSLog呼び出しの最初の引数として、つまり

NSLog(@"%@", [NSString stringWithFormat: ....]);

ただし、おそらく16のオットーの答えを検討する必要があります。

10
Anthony Cramp

「文字列リテラルではなく形式引数なしの形式」という警告を一度だけ取り除く場合は、ターゲットのビルド設定でGCC警告設定「printf/scanfへのタイプチェック呼び出し」(GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = NO)を無効にすることができます。

3
aldi

FWIW、これはiPhone開発にも当てはまります。私は3.1.3 SDKに対してコーディングしていますが、同じ問題(NSLog()内にstringWithFormatをネストする)で同じエラーが発生しました。 SixtenとJonはお金を稼いでいます。

2
Pettiross

NSLog()はフォーマット文字列を想定しています。渡されるのは単なる文字列です。 stringWithFormat:を使用する必要はありません。次のようにできます。

NSLog(@"%@ %@, %@", errorMsgFormat, error, [error userInfo])

そして、それは警告が消えるでしょう。

2
Elfred

NSMutableStringでappendFormatを使用していることを誰にでも知らせると、次のようなフォーマットされた文字列を渡そうとすると、この警告が表示されます。

NSMutableString *csv = [NSMutableString stringWithString:@""];
NSString *csvAddition = [NSString stringWithFormat:@"%@",WHATEVERYOUAREPUTTINGINYOURSTRING];
[csv appendFormat:csvAddition];

したがって、この警告を回避するには、上記を次のように変更します。

NSMutableString *csv = [NSMutableString stringWithString:@""];
[csv appendFormat:@"%@",WHATEVERYOUAREPUTTINGINYOURSTRING];

より簡潔で安全です。楽しい!

0
ColossalChris