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

最終更新日 2025年10月31日


warning: comparison with string literal results in unspecified behavior

warning: result of comparison against a string literal is unspecified

警告:文字列リテラルに対する比較の結果は未規定動作です。
代わりにstrcmp関数を使用して下さい
[-Waddress]
[-Wstring-compare]

■この記事の概要

if("文字列" == "文字列")はコンパイル出来るのに
意図した動きをしなくてお困りではありませんか?

この記事では、C言語でif("文字列" == "文字列")のように文字列リテラルを==で比較すると、文字列の内容ではなくメモリ上のアドレスが比較される問題を解説しています。正しい文字列比較にはstrcmp関数を使用すべきことや、リテラルや配列の違いによる挙動の詳細が具体例とともに説明しています。


■文字列リテラル同士の比較は、ほぼバグです

#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    文字列直接比較(void){
    puts("abc" == "abc"?"同じ":"違う");
}
void    ポインタ比較(void){
    char    *p1 = "abc";
    char    *p2 = "abc";
    puts(p1 == p2?"同じ":"違う");
}
void    配列比較(void){
    char    a1[] = "abc";
    char    a2[] = "abc";
    puts(a1 == a2?"同じ":"違う");
}
int main(void){
    文字列直接比較();
    ポインタ比較();
    配列比較();
}

参考:

未規定の動作

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