質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

4回答

5207閲覧

文字列リテラルと文字列定数

strike1217

総合スコア651

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

1クリップ

投稿2017/06/14 05:25

長い間、文字列リテラルと文字列定数は同じものだと考えていました。

ところが・・・私の持っている本にこうあります。

C言語には、文字列定数という概念は存在しない。

文字列リテラルは、定数であるとは限らない。

ん?
書き換えができない=定数として考えると文字列リテラルも文字列定数も同じ意味ではないのでしょうか?

では、C++では文字列定数という概念は登場するんですか?
const char 型かな?

#include<stdio.h> int main() { char *name = "Technology"; *name = 'Z'; //(2) printf("%s\n", name); name = "y"; printf("%s\n", name); return 0; }

上記の(2)には問題があります。
静的記憶期間の文字列リテラルを書き換えています。
未定義動作になるとおもいます。

しかし改めて考えるとなぜ上書き行為は危険なのでしょうか?

文字列リテラルの書き換えが可能であると仮定してはいけない。

グローバル変数も同じく静的記憶期間です。
しかしグローバル変数はプログラムの中でよく書き換えたりしますよね?
グローバル変数はよくて、文字列になるとダメというのはどういう事でしょうか?

別に「静的記憶期間は上書きを禁止している領域」というわけではないですよね?
もしそうならグローバル変数は書き換え不可能になっていまします。

文字列リテラルが定数とは限らないからこそ上書きしてはいけないのですか?

文字列リテラルという用語の登場とともに、まったく使われなくなった用語が文字列定数

・・・だそうです。
用語の問題もまた歴史が絡んでいるのでしょうか?

どなたか教えてください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

こんにちは。

しかし改めて考えるとなぜ上書き行為は危険なのでしょうか?

昔、文字列定数をバグで書き換えた同僚が苦しんでいしたことが有りました。
詳細は忘れたのですが、本質的には次のような流れだったと思います。

C

1#include<stdio.h> 2 3void foo(char* name) 4{ 5 *name='Z'; 6} 7 8void bar() 9{ 10 char* name; 11 printf("%s\n", name="Technology"); 12 foo(name); 13} 14 15int main() 16{ 17 bar(); 18 bar(); 19 20 return 0; 21}

g++とMinGWではsegfaultになりましたが、msvcの場合、bar()関数での出力が2回目以降は"Zechnology"になりました。
未定義なのでどちらも規格内の動作です。
しかし、後者は結構いやらしい振る舞いと思います。

グローバル変数はよくて、文字列になるとダメというのはどういう事でしょうか?

変数は書き換えても問題ないと思います。しかし、定数を書き換えるとソース上は'T'なのに実際には'Z'なのどが起こるのでこれは嫌な話です。

gccは文字列定数を定数として処理しているようですね。最近のOSは書き込み禁止ページを用意できますから、そこに配置すれば定数にできます。
msvcはC言語に「文字列リテラルがない」と言う概念に合わせた実装のように見えます。変数領域に配置しているのでしょう。

投稿2017/06/14 06:27

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2017/06/14 06:50

「OSは書き込み禁止ページを用意できます」 あ!なるほど。 どのページに配置するかで定数かそうでないかが決まるわけですね! それはコンパイラによって異なるわけですから未定義動作なのですね! OSの概念と組み合わせて考える非常に分かりやすいですね!
strike1217

2017/06/14 07:00

C言語によって明確に定めされていないものはコンパイラによって異なると考えて良いんですかね? コンパイラによって異なるというのは厄介ですね・・・
Chironian

2017/06/14 07:04

確かに厄介です。でも、細かい部分まで定めてもコンパイラ・ベンダが従わなければ絵に書いた餅ですから、仕方がないことかも知れません。
strike1217

2017/06/15 01:22

スタック領域やヒープ領域にも書き換え可能か不可能かといったページ領域が存在するんですか?
Chironian

2017/06/15 01:30

無いはずです。 書き換え不可制御はMMUでページ単位で制御します。MMUは任意のアドレスから任意バイト数書き換え不可な領域を多数管理できるハードウェアではありません。 そして、例えば、ヒープの1ページを書き換え不可にしたら、そのページにヒープ管理情報を書くことができないので、事実上ヒープとして使えません。 スタックも同様です。ちょうど書き換え不可ページをスタック・ポインタが指している時サブルーチンコールする際、戻り番地を書きたくても書けなくなります。
strike1217

2017/06/15 01:42

そうですか! わかりました。
guest

0

ベストアンサー

まずは、こちらをごらんください。

C言語では、文字列リテラルを書き換える操作は未定義となっています(何が起きても文句は言えません)。一例としては、以下のような現象が想定されます。

  • 文字列リテラルが書き換え不可能なメモリ領域に格納してあった場合…書き換えようとした時点でアクセスエラーになる
  • 文字列リテラルが普通のメモリ領域に入れてあった場合…同じリテラルを使っている、別な箇所にまで影響してしまう

C++では、文字列リテラルの型がconst char []となっていますので、constでないchar *に代入しようとすると、その時点でエラーや警告となります。

投稿2017/06/14 05:51

maisumakun

総合スコア145184

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2017/06/14 06:52

静的記憶期間には書き換え可能な領域と書き換え不可の領域の2種類があるんですね!
maisumakun

2017/06/14 07:01

「(静的記憶域期間を持つオブジェクト)の生存期間はプログラム実行の全体とする。その値はプログラム開始処理の前に 1 回だけ初期化する。」ということなので、書き換え可能か不可能かとは別な概念です。
strike1217

2017/06/14 07:08

あ、そうなんですか! 静的記憶領域だけでなく、スタック領域やヒープ領域にも同じく書き換え可能か不可能かといった領域が存在するんですか?
strike1217

2017/06/14 07:09

char *p1 =“Hello”;  // 従来は問題なかったが、現在は不適切 やはり歴史が絡むですね・・・ 本によって違うのもそういう理由ですかね・・・
maisumakun

2017/06/14 07:16

「処理系は,volatileでないconstオブジェクトを,読取り専用記憶域に置いてもよい。さらに,処理系はそのアドレスが使われないならば, そのようなオブジェクトに記憶域を割り付けなくてもよい。」ということで、constな自動変数は使う場所に展開されて変数として消えてしまっても問題ない、とのことです。
strike1217

2017/06/14 07:21

「constな自動変数は使う場所に展開されて変数として消えてしまっても問題ない」 つまりconstな自動変数とは、書き換えられても問題ないという意味ですか? constな自動変数は直感的には「書き換え不可能なスタック領域」と考えられそうですが、そうではないと・・・?
maisumakun

2017/06/14 07:28 編集

全く逆で、とある関数で「const int i = 3;」となっていた場合に、iへのポインタ参照がなければ、「i」は変数として用意せずにすべてリテラルの「3」に置き換えてしまってコンパイルしてもかまわない、ということです。
strike1217

2017/06/14 07:35 編集

ああ! なるほど! そーゆー意味ですか! > 「i」は変数として用意せずにすべてリテラルの「3」に置き換えてしまってコンパイルしてもかまわない では、リテラルは必ず静的記憶期間に配置するという規制はC言語には存在するんですか?
maisumakun

2017/06/14 07:50

複合リテラルは、置かれた場所に応じた記憶域期間(静的 or 自動)となります。 数値や文字は、「リテラル」ではなく「定数」と呼びます(なぜかここで最初の質問に戻ってきた気もしますが)。
strike1217

2017/06/14 09:45

置かれた場所に応じた記憶域期間(静的 or 自動)となります。 スタック領域やヒープ領域にも同じく書き換え可能か不可能かといった領域が存在するんですか?
strike1217

2017/06/15 02:11

constな自動変数は使う場所に展開されて変数として消えてしまっても問題ない。 定数なのにスタック領域に配置されるわけですよね・・・ constな自動変数は定数であっても変数扱いという事ですか?
maisumakun

2017/06/15 05:44

消えてしまうようなconst変数は、「C言語から」見れば変数ですが、「機械語レベルでは」コードに埋め込まれた即値となります。変数として取られるメモリではなく、プログラムコードの一部となってしまう…なんて形で問題ないわけです。
strike1217

2017/06/15 09:02

ほお! なるほど!! コードの一部なんですか! スタック領域ではなく、テキスト領域(プログラム領域)になるという事ですかね・・・
guest

0

文字列リテラルは

char* name = "Technology";

だったら"Technology"の部分そのものです。

void func1(){ char* name1 = "Technology"; name1[0] = 'a'; printf("%s\n",name1): } void func2(){ char* name2 = "Technology"; name2[1] = 'b'; printf("%s\n",name2): } int main(){ func1(); func2(); }

とした場合、出力される文字列がどうなるか未定義です。
name1とname2は同じアドレスを示していて、
例えば上記の場合だと、func2の出力されている文字列の先頭がaになったりします。

投稿2017/06/14 13:42

hmmm

総合スコア818

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

C言語の実装について深く調べなくても推測することならできると思います。

例えば以下のようなコードを書いたとして、(2)によって書き換えが起きてしまったと仮定したとき(1)を実行すると何がおこるでしょうか。

コードを読めば右辺は"abc"と書いてはいるが実は"Abc"となっているかも知れないと予測することは可能かも知れません。しかしながら「"abc"を代入しているにもかかわらず"Abc"が実際の中身かも知れない」といった病的なプログラムコードを誰が好んで書きたい(or 解析したい or デバッグしたい or 言語仕様ですと仕様書に書きたい)と思うでしょうか?多分大多数の意見は否定的でしょう。

C

1for (...; ...; ...) { 2 char *sp = "abc"; // (1) 3 ... 4 sp[0] = 'A'; // (2) 5}

プログラムというのは分かりにくいものです。あちこちにバグの原因が転がっています。ですから「直感的に納得できる」「自然な仕様」が望まれると思います。


ちなみに、こうした病的な状態を少しでも減らそうという目的でCコンパイラーが文字列定数を仮想メモリ上の書き換え不能な領域へ配置するかも知れません。行儀よく書かれたプログラムには影響はないでしょう。しかしながら「例えデータの値が変化しなくてもwriteアクセスしようとするプログラム」はSegmentation faultなどを起こすことになるでしょう。実際Windows10上のgccでコンパイルしてみるとそうなりwriteアクセスした瞬間に異常終了します。

つまり文字列リテラルは「グローバル変数と同じものでは全然ない」ということになるでしょう。

投稿2017/06/14 05:58

KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2017/06/14 07:05

ほお! なるほど。 windowsのclコンパイラだと、質問中に出てきたプログラムは問題なく動作します。 OSによって異なるのではなく、コンパイラによって異なるで間違いなさそうですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問