【C言語】
if(“文字列”==”文字列”)は 文字列領域のアドレス比較で 文字列比較とは違う


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();
}

参考:

未規定の動作

C-FAQ 1.32: 以下の二つの初期化の違いは。

http://www.kouno.jp/home/c_faq/c1.html#32

STR30-C. 文字列リテラルを変更しない

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