warning: comparison with string literal results in unspecified behavior
warning: result of comparison against a string literal is unspecified
警告:文字列リテラルに対する比較の結果は未規定動作です。
代わりにstrcmp関数を使用して下さい
[-Waddress]
[-Wstring-compare]
■文字列リテラル同士の比較は、ほぼバグです
#include <stdio.h>
int main(void)
{
//以下のAとBは同じ意味
if("文字列" == "文字列") { //A:未規定=非推奨
puts("コンパイラによっては成立");
}
if(&"文字列"[0] == &"文字列"[0]) { //B:未規定=非推奨
puts("コンパイラによっては成立");
}
}
同じ文字列リテラルはコンパイル時に
共有領域に割り付けても良いし、
共有領域に割り付けなくても良い。
⇒どちらでも良い=コンパイラ依存
なので
if(“abc” == “abc”)の
左辺の文字列リテラル”abc”と
右辺の文字列リテラル”abc”を
同じ領域に割り付けるコンパイラではif文は成立し、
別の領域に割り付けるコンパイラではif文は成立しません。
簡単に言うとこのコードは文字列の比較ではなくて、
文字列のアドレスの比較を行っています。
■文字列リテラルとポインタの比較は、ほぼバグです
#include <stdio.h>
int main(void)
{
char *文字列を指すポインタ = "文字列";
if(文字列を指すポインタ == "文字列") {//未規定-非推奨
puts("コンパイラによっては成立");
}
if(文字列を指すポインタ == &"文字列"[0]) {//未規定-非推奨
puts("コンパイラによっては成立");
}
}
文字列リテラル”文字列”を
同じ領域に割り付けるコンパイラではif文は成立し、
別の領域に割り付けるコンパイラではif文は成立しません。
gcc,clangでは成立しました。
■配列と文字列リテラルの比較は、バグです
#include <stdio.h>
int main(void)
{
char 文字列のコピーで初期化された配列[] = "abc" ;
if(文字列のコピーで初期化された配列 == "abc") {
puts("ここには来ない、これはバグ");
}
if(&文字列のコピーで初期化された配列[0] == &"abc"[0]) {
puts("ここには来ない、これはバグ");
}
}
これは先のポインタと似ていますが
6行目の比較は成立する事はありません。
char ary[] = “abc” ; の宣言では文字列リテラル”abc”は
自動変数領域のary[]配列にコピーします。
このため文字列リテラル”abc”とary[]配列の内容は同じでも
メモリ上のアドレスは別物です。
よってif文はコンパイラの種類にかかわらず成立する事はありません。
■文字列比較はstrcmpを使うのが正解です
#include <stdio.h>
#include <string.h>
int main(void) {
char *ptr = "文字列";
char ary[] = "文字列" ;
if(strcmp(ptr,"文字列") == 0) {
puts("ポインタで成立");
}
if(strcmp(ary,"文字列") == 0) {
puts("配列で成立");
}
if(strcmp(ptr,ary) == 0) {
puts("ポインタと配列で成立");
}
}
文字列リテラルとの比較は == で直接比較すると
未規定動作 となるため
strcmp()等の文字列操作関数を使用して下さい。
■文字列定数に書き込みできません
[-Wanalyzer-write-to-string-literal]
warning: write to string literal
警告:文字列定数への書き込み
void f(char *cp)
{
*cp = 'X' ;
}
int main(void)
{
char *ptr = "文字列定数";
f(ptr); //ダメ:文字列定数に書き込みしている
}
”文字列定数”領域には書き込みできません。
書き込みしようとするプログラムの動きは未定義です。
■配列には書き込みできます
void f(char *cp)
{
*cp = 'X' ;
}
int main(void)
{
char ary[] = "文字列定数";
f(ary); //OK:文字列定数をコピーした自動変数領域に書き込み
}
”文字列定数”で初期化した配列には書き込みできます。
■練習問題
あなたの環境では何と表示されますか?
#include <stdio.h>
#include <string.h>
void f1(void)
{ //文字列リテラル直接比較
if("abc" == "abc"){
printf("同じ\n");
} else {
printf("違う\n");
}
}
void f2(void)
{ //ポインタ比較
char *p1 = "abc";
char *p2 = "abc";
if(p1 == p2){
printf("同じ\n");
} else {
printf("違う\n");
}
}
void f3(void)
{ //配列比較
char a1[] = "abc";
char a2[] = "abc";
if(a1 == a2){
printf("同じ\n");
} else {
printf("違う\n");
}
}
int main(void)
{
f1();
f2();
f3();
}
参考:
http://www.kouno.jp/home/c_faq/c1.html#32
https://www.jpcert.or.jp/sc-rules/c-str30-c.html
CFA-Q 8.2: 文字列が、ある値と一致するかどうか調べるプログラムを書いている。 なぜ以下のコードではうまくいかないのか。
http://www.kouno.jp/home/c_faq/c8.html#2