激論中の文字列リテラル

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
non
記事: 1097
登録日時: 13年前

激論中の文字列リテラル

#1

投稿記事 by non » 10年前

あちらでは、トピ主に迷惑ですし、私も結論が知りたいので、別トピを立てました。
もしかしたら、お互いに誤解があるのかも知れませんので、こちらで激論してください。

さて、私も、屍さんと同じように、文字列リテラルは書き換えてはいけないと思っておりました。
一部のコンパイラ(たとえばBorlandC++)などでは、書き換えてしまいますが、これはコンパイラのバグだと
思っていたわけです。
私のパソコンはWindows7でVisualC++2008ExpressEditionです。

コード:

#include <stdio.h>
int main(void)
{
	
    char *str = "Hello!!",*p;
    p=str;
    *(++p) = 'a';
    printf("\n%s",str);
	return 0;
}
このプログラムを、コンパイル言語C言語でコンパイルしてもエラーは出ませんが、DEBUGでは
test.exe の 0x013f13d7 でハンドルされていない例外が発生しました: 0xC0000005: 場所 0x013f5741 に書き込み中にアクセス違反が発生しました。
のエラーが実行時に出ます。
Releaseでは、実行時にエラーは出ませんが、Hello!!のままで書き換えられません。

私が理解している考えは
"Hello!!"の部分は静的メモリエリアに用意され、マイコンの場合はROM領域だと思っております。
しかし、先日、Arduinoを勉強していて、実行時にわざわざ、flashメモリからRAMにコピーをしていました。
これは、Arduinoに使われているAVRマイコンが特別なのだと思っています。

論点1
規格で、文字列リテラルが書き換えられるかどうかは、未定義なのか。

論点2
VC++では、私は書き換えられなかったが、書き換えられたという人と、設定が違うのか。

このままでは、気持ちが悪いので、はっきりさせてください。
non

beatle
記事: 1281
登録日時: 12年前
住所: 埼玉
連絡を取る:

Re: 激論中の文字列リテラル

#2

投稿記事 by beatle » 10年前

1. 文字列リテラルを変更することは規格上未定義の動作です.
2. VC++をそこまで使い込んだことがないのでわかりません.

ym114

Re: 激論中の文字列リテラル

#3

投稿記事 by ym114 » 10年前

http://www.jisc.go.jp/app/pager?id=146478 p46
文字列リテラルは"静的記憶域期間を持ち...これらの配列をプログラムが変更しようとした場合、その動作は未定義とする"

未定義なので、たとえ可能でも変更しないほうがいいと思います

non
記事: 1097
登録日時: 13年前

Re: 激論中の文字列リテラル

#4

投稿記事 by non » 10年前

ym114 さんが書きました:http://www.jisc.go.jp/app/pager?id=146478 p46
文字列リテラルは"静的記憶域期間を持ち...これらの配列をプログラムが変更しようとした場合、その動作は未定義とする"

未定義なので、たとえ可能でも変更しないほうがいいと思います
ym114さん。引用されたページは開けません。会員限定とか?
誰でも見れる規格が載った場所ってありませんか?
non

ym114

Re: 激論中の文字列リテラル

#5

投稿記事 by ym114 » 10年前

失礼しました
http://www.jisc.go.jp/app/JPS/JPSO0020.html
X3010
で開けるかと思います

こんな記事もありました。参考までに。http://msdn.microsoft.com/en-us/library/69ze775t.aspx

non
記事: 1097
登録日時: 13年前

Re: 激論中の文字列リテラル

#6

投稿記事 by non » 10年前

おふたりとも情報ありがとうございます。

1 論点1 は未定義であることは間違いないですね。

2 論点2 Windowsでは書き換えられるとは書いてありますが、VC++で書き換えられるとは書いてないですね。
  私の、早とちりでしょうか?

 お互いの主張がかみ合っていませんが、
 整理すると
 文字列定数を書き換えが可能かは未定義である。
 一部のコンパイラでは、書き換えが可能である。
 そこまでは、お互い理解している。

主張A
 書き換えるコンパイラがあるのだから、文字列リテラルは変更できないというのは誤りである。
主張B
 書き換えられないコンパイラがあるのだから、すべてのコンパイラでも動くように文字列リテラルは変更できないと覚えた方が良い。

ということではないのですか?
non

アバター
usao
記事: 1887
登録日時: 11年前

Re: 激論中の文字列リテラル

#7

投稿記事 by usao » 10年前

規格上 未定義 なのだとすれば…

>書き換えるコンパイラがあるのだから、文字列リテラルは変更できないというのは誤りである。
”書き換えるコンパイラがあるから” ではなく ”未定義なのだから” では?
動作を定義されてないんだから,書き換えるかもしれないしエラーで止まるかもしれないしそれ以外の何かが起こるかもしれない.
なので「変更できない」「エラーになる」等と言い切ることは誤り.
(「C言語」について話しているときに「私の環境では云々だからどうの」と言うことにまず意味が無い.)

>書き換えられないコンパイラがあるのだから、すべてのコンパイラでも動くように文字列リテラルは変更できないと覚えた方が良い。
こっちも ”そういうコンパイラも存在しているのだから” ではない.同上.
「変更できない」とか「変更してはならない」ではなく
「変更しようなどということを行うべきではない.なぜならば一般には何が起こるか決まってないから」.


…みたいな感じではないでしょうか.

#領域外参照とかも「やれる」のだから「できない」とは言えない.
 「(一般に)やるべきではない」→そのことを「(一般に,移植性のあるコードを書くなら?)してはならない」と説明することはまぁ悪くないと思う.

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: 激論中の文字列リテラル

#8

投稿記事 by softya(ソフト屋) » 10年前

これまでの内容まとめ。

元記事
「ポインタについて • C言語交流フォーラム ~ mixC++ ~」
http://dixq.net/forum/viewtopic.php?f=3&t=13390
ただの屍のようだ さんが書きました:ポインタは便利な機能ですが、やはり2,3行で説明しきれるものではないので、
「C言語 ポインタ完全制覇」をお勧めします。ざっくり読んだだけでもポインタの知識は身につきます。
ついでに、ヘッダファイルやら可変長構造体やらも勉強できるので、
時間取れるのであれば書物での学習のほうが効率いいです。そのぶん、金かかります。

せっかくなので課題出します。

コード:

	char str[] = "Hello!!",*p;		//なぜ str[] を *str にするとエラーとなるのか
	p=str;
	*(++p) = 'a';
	printf("\n%s",str);
あとで聞かれてもいつ返事返せるか(返す気になるのか)わからないので、答えあらかじめいっておきます。 文字列リテラルは変更できません
h2so5 さんが書きました:
ただの屍のようだ さんが書きました:ポインタは便利な機能ですが、やはり2,3行で説明しきれるものではないので、
「C言語 ポインタ完全制覇」をお勧めします。ざっくり読んだだけでもポインタの知識は身につきます。
ついでに、ヘッダファイルやら可変長構造体やらも勉強できるので、
時間取れるのであれば書物での学習のほうが効率いいです。そのぶん、金かかります。

せっかくなので課題出します。

コード:

	char str[] = "Hello!!",*p;		//なぜ str[] を *str にするとエラーとなるのか
	p=str;
	*(++p) = 'a';
	printf("\n%s",str);
あとで聞かれてもいつ返事返せるか(返す気になるのか)わからないので、答えあらかじめいっておきます。 文字列リテラルは変更できません
文字列リテラルはこの場合 "Hello!!" の部分を指します。
str[] と str* はリテラルとは関係ありませんから、文字列リテラルは変更できないというのは誤りです。
ただの屍のようだ さんが書きました:変更できません。実行時エラーになるはずです。
少なくとも自分のgccではちゃんと強制終了になりました。
あと、警告メッセージ見てみるとわかると思いますが、const付きなのでC++でconst_castしない限り変更できないはずです。
そして、変更できない理由としては、文字列リテラルは変数ではなく、定数ですから。
(結局、自分で問題出して、自分で解説して回答まで書く形になるとは・・・)
softya(ソフト屋) さんが書きました:私の環境で試すと、ファイル名をmain.cと仮定しますがcygwinのgcc main.cでもcl main.cでコンパイルしてもエラーは出ませんし、実行したらhalloに書き換えられています。
状況を限定するなら特定の状況ではと書くべきで、全部に通じるかのように書くのは危険だと思います。
ただの屍のようだ さんが書きました:もちろん、↓のようなコードで実行しましたよね?

コード:

    char *str = "Hello!!",*p;
    p=str;
    *(++p) = 'a';
    printf("\n%s",str);
softya(ソフト屋) さんが書きました:
ただの屍のようだ さんが書きました:もちろん、↓のようなコードで実行しましたよね?

コード:

    char *str = "Hello!!",*p;
    p=str;
    *(++p) = 'a';
    printf("\n%s",str);
その通りですが、検証方法書かれていないただの屍のようださんの方が不明確な論証だと思います。
ISLe さんが書きました:だからウィンドウズの処理系では文字列リテラルを書き換えられると既に書いているでしょう。

未定義の動作についてきちんと調べてから書くようにしてはいかがですか?
そもそも今回は未定義の動作に触れる必要ないですし。
ただの屍のようだ さんが書きました:ふむふむ、これは突っ込むべきか、スルーすべきか、実に悩むところです。
必要最低限だけ説明しよう。
char str[] = "Hello!!"; は単なる str = {'H','e','l','l','o','!','!','\0'}; の略で、配列をHello!!で初期化すると言う意味です。
char *str = "Hello!!" は 文字列リテラルの先頭アドレスをstrに代入するという意味です。
*ちなみに、自分のPCはwindows7です。コンパイラはgccでもVC++2010でも実行時エラーで異常終了します。
かずま さんが書きました:
ただの屍のようだ さんが書きました:変更できません。実行時エラーになるはずです。
少なくとも自分のgccではちゃんと強制終了になりました。
gcc -fwritable-strings main.c を試してみてください。
ただの屍のようだ さんが書きました:別にgcc -fwritable-strings main.c で試すまでもない、ボーランドC++5.5なら警告すら出さずに通してくれます。エラーにもなりません。
昔、この掲示板で言われたことをそのまま(正確に覚えてないが可能な限り原文に近い形で)言い返します。
VC++だけ通るコードをいくら書いてもなんの価値もないです。そもそも肝心のVC++すら通らないじゃないですか(笑)
usao さんが書きました:「実際にどうなるのか」については置いといても,
>ボーランドC++5.5なら警告すら出さずに通してくれます。エラーにもなりません。
これって,ご自身で 「なぜ エラーになる のか」 という課題とやらを出してることに対して矛盾してますよね?

本題については 私も
>ISLe さんのNo: 8の説明が一番明確だと思います
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: 激論中の文字列リテラル

#9

投稿記事 by softya(ソフト屋) » 10年前

私の主張は、書き換えれる環境がある(あるいは可能性がある)なら、それは例題として不適格だと言うことです。
試してみなさい。と言われて動いたら例題の意味が無いからです。

私の環境と結果をまとめてみました。【追記】
[table=width:50%;border:1px solid #cccccc;][tr=text-align:center;][td=border:1px solid #cccccc;padding 2px;]名前[/td][td=border:1px solid #cccccc;padding 2px;]OS[/td][td=border:1px solid #cccccc;padding 2px;]コンパイラ[/td][td=border:1px solid #cccccc;padding 2px;]バージョン[/td][td=border:1px solid #cccccc;padding 2px;]オプション等[/td][td=border:1px solid #cccccc;padding 2px;]結果[/td][/tr]
[tr=text-align:center;][td=border:1px solid #cccccc;padding 2px;] ソフト屋[/td][td=border:1px solid #cccccc;padding 2px;]Windows7 64bit[/td][td=border:1px solid #cccccc;padding 2px;]VC++ コマンドライン[/td][td=border:1px solid #cccccc;padding 2px;]2008[/td][td=border:1px solid #cccccc;padding 2px;]cl main.c[/td][td=border:1px solid #cccccc;padding 2px;]コンパイルエラーなし 書き換え◯[/td][/tr]
[tr=text-align:center;][td=border:1px solid #cccccc;padding 2px;]〃[/td][td=border:1px solid #cccccc;padding 2px;]〃[/td][td=border:1px solid #cccccc;padding 2px;]cygwin gcc[/td][td=border:1px solid #cccccc;padding 2px;]cygwin 1.7.18(0.263/5/3) gcc 4.5.3[/td][td=border:1px solid #cccccc;padding 2px;]gcc main.c[/td][td=border:1px solid #cccccc;padding 2px;]コンパイルエラーなし 書き換え◯[/td][/tr]
[tr=text-align:center;][td=border:1px solid #cccccc;padding 2px;]〃[/td][td=border:1px solid #cccccc;padding 2px;]〃[/td][td=border:1px solid #cccccc;padding 2px;]vc++ IDE上[/td][td=border:1px solid #cccccc;padding 2px;]2008[/td][td=border:1px solid #cccccc;padding 2px;]/TC指定 及び /TPも同様 DEBUGビルド win32(x86)[/td][td=border:1px solid #cccccc;padding 2px;]コンパイルエラーなし 書き換え◯[/td][/tr][/table]
non さんの結果と食い違いますね。 ExpressとExpressなしでそこまで違うと思えませんが。

問題が有ったので下に再検証。

検証コード

コード:

#include <stdio.h>

int main( void )
{
    char str[] = "Hello!!",*p;      //なぜ str[] を *str にするとエラーとなるのか
    p=str;
    *(++p) = 'a';
    printf("\n%s",str);

	return 0;
}
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: 激論中の文字列リテラル

#10

投稿記事 by softya(ソフト屋) » 10年前

あっごめんなさい。こっちのコードですね。これならnonさん方式ならエラーです。[誤解が有るので修正]

コード:

#include <stdio.h>
int main(void)
{
    
    char *str = "Hello!!",*p;
    p=str;
    *(++p) = 'a';
    printf("\n%s",str);
    return 0;
}
私が思い込みでコードを見なおさずに混乱を広げた犯人の様です。申し訳ありません。
【補足】言い訳になりますが、経験上そういう動作をすると知っていたのでソースコードの確認を怠りました。
混乱させて申し訳なかったです。なお、IDE上で/TCオプション時に実行時エラーになるのは知りませんでした。/TPだけだと思っていました。

改めて検証してみました。
[table=width:50%;border:1px solid #cccccc;][tr=text-align:center;][td=border:1px solid #cccccc;padding 2px;]名前[/td][td=border:1px solid #cccccc;padding 2px;]OS[/td][td=border:1px solid #cccccc;padding 2px;]コンパイラ[/td][td=border:1px solid #cccccc;padding 2px;]バージョン[/td][td=border:1px solid #cccccc;padding 2px;]オプション等[/td][td=border:1px solid #cccccc;padding 2px;]結果[/td][/tr]
[tr=text-align:center;][td=border:1px solid #cccccc;padding 2px;] ソフト屋[/td][td=border:1px solid #cccccc;padding 2px;]Windows7 64bit[/td][td=border:1px solid #cccccc;padding 2px;]VC++ コマンドライン[/td][td=border:1px solid #cccccc;padding 2px;]2008[/td][td=border:1px solid #cccccc;padding 2px;]cl main.c[/td][td=border:1px solid #cccccc;padding 2px;]コンパイルエラーなし 書き換え◯[/td][/tr]
[tr=text-align:center;][td=border:1px solid #cccccc;padding 2px;]〃[/td][td=border:1px solid #cccccc;padding 2px;]〃[/td][td=border:1px solid #cccccc;padding 2px;]cygwin gcc[/td][td=border:1px solid #cccccc;padding 2px;]cygwin 1.7.18(0.263/5/3) gcc 4.5.3[/td][td=border:1px solid #cccccc;padding 2px;]gcc main.c[/td][td=border:1px solid #cccccc;padding 2px;]コンパイルエラーなし 書き換え◯[/td][/tr]
[tr=text-align:center;][td=border:1px solid #cccccc;padding 2px;]〃[/td][td=border:1px solid #cccccc;padding 2px;]〃[/td][td=border:1px solid #cccccc;padding 2px;]vc++ IDE上[/td][td=border:1px solid #cccccc;padding 2px;]2008[/td][td=border:1px solid #cccccc;padding 2px;]/TC指定 及び /TPも同様 DEBUGビルド win32(x86)[/td][td=border:1px solid #cccccc;padding 2px;]コンパイルエラーなし 書き換えNG[/td][/tr][/table]
やっぱり書き換えられる状況があります。なお、nonさんと状況は一致しました。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

beatle
記事: 1281
登録日時: 12年前
住所: 埼玉
連絡を取る:

Re: 激論中の文字列リテラル

#11

投稿記事 by beatle » 10年前

メモリの属性がそれぞれの処理系で違うのでしょうね。
Windowsですと、メモリセクションに対して次のような属性があるらしいです
http://msdn.microsoft.com/ja-jp/library ... s.80).aspx リンクを修正しました。by softya(ソフト屋)

Write属性を使えば、定数データを置いたセクションを本当に書込み禁止にするかどうかを制御できますので、処理系によって差が出ているのでしょうね。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: 激論中の文字列リテラル

#12

投稿記事 by softya(ソフト屋) » 10年前

VC++の場合、ビルドオプションがメモリの配置に影響を与えていると思います。
どのオプションかは検証していませんが、VisualStudio(IDE)上でDEBUGビルドしたものは、コマンドラインで実行すると異常終了します。

【補足】 配置というよりセッションセクションの構成ですね。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

non
記事: 1097
登録日時: 13年前

Re: 激論中の文字列リテラル

#13

投稿記事 by non » 10年前

usao さんが書きました: >書き換えるコンパイラがあるのだから、文字列リテラルは変更できないというのは誤りである。
”書き換えるコンパイラがあるから” ではなく ”未定義なのだから” では?
厳密にいえばそうでしょうね。
未定義である(規約)->書き換えるコンパイラの存在もある(事実)->変更できないは誤り(結論)

ただの屍のようだ さんが書きました:せっかくなので課題出します。

コード:

	char str[] = "Hello!!",*p;		//なぜ str[] を *str にするとエラーとなるのか
	p=str;
	*(++p) = 'a';
	printf("\n%s",str);
あとで聞かれてもいつ返事返せるか(返す気になるのか)わからないので、答えあらかじめいっておきます。 文字列リテラルは変更できません
ここで、環境も指定せず、断言されたことに対する反論だということですね。
しかし、何かの本でも文字列リテラルは変更できませんと書かれていた本があったと記憶してます。(断言できないのですが)
とすれば、初学者にたいし、説明足らずでそのように説明したとしても、そんなに的外れではないのかと。
ただ、初学者の環境によっては動くかも知れないので混乱させるだろうという主張もよくわかります。

最近、掲示板が荒れてきている印象があるので、相手の心情を慮ることも必要だなと考える次第です。
私も、最初にちょこっとカチンときましたので。反省・・・反省。
non

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: 激論中の文字列リテラル

#14

投稿記事 by softya(ソフト屋) » 10年前

そうですね。
ポインタで出来ること出来ないとがありますって所から説明に入れば問題が無いんですが、かなり唐突な例題だった思います。
質問主を混乱させるだけでは? と思いました。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
usao
記事: 1887
登録日時: 11年前

Re: 激論中の文字列リテラル

#15

投稿記事 by usao » 10年前

>初学者にたいし、説明足らずでそのように説明したとしても、そんなに的外れではないのかと。
「変更できない」と覚えてしまうのは本当にその問題に直面した時に「あれ?」ってなるので困るかもですが
「変更できないものとして」コーディングする(ようにしましょう)という感じかなぁ?
ただ,そういった”断言したしない”よりも,
なんというか,質問者の質問内容とあまりにもかけ離れているという点の方が 適切でない度(?)が高いような.


>未定義である(規約)->書き換えるコンパイラの存在もある(事実)->変更できないは誤り(結論)
うーん,私的には↓こうです.間の事実は不要.
未定義である(規約)->変更できないは誤り(結論)

non
記事: 1097
登録日時: 13年前

Re: 激論中の文字列リテラル

#16

投稿記事 by non » 10年前

softya(ソフト屋) さんが書きました:そうですね。
ポインタで出来ること出来ないとがありますって所から説明に入れば問題が無いんですが、かなり唐突な例題だった思います。
質問主を混乱させるだけでは? と思いました。
あのときのあの例題が適切だったのかと言われれば、それは私も同意します。

>ほとんど回答になってない回答では質問者がかえって混乱するので、わかりやすく説明します。

って言われると・・・
そうだよね。中途半端だったよねって思っていますが。そう指摘されると、そっちの説明はもっとわからんだろうと思ったのも事実でして。

しかし、こっちの件(回答のやり方がへただった点)は、あんまり追求すべきではないと思います。
気楽に回答できなくなってしまいますからね。
non

non
記事: 1097
登録日時: 13年前

Re: 激論中の文字列リテラル

#17

投稿記事 by non » 10年前

usao さんが書きました:#領域外参照とかも「やれる」のだから「できない」とは言えない.
 「(一般に)やるべきではない」→そのことを「(一般に,移植性のあるコードを書くなら?)してはならない」と説明することはまぁ悪くないと思う.
これ、よくわかります。ナイス例です。いまさら・・・ですが。読み直していてそう思いました。

「領域外参照はしてはいけません」って言いますものね。「領域外参照はできません」とは言わない。
non

ただの屍のようだ

Re: 激論中の文字列リテラル

#18

投稿記事 by ただの屍のようだ » 10年前

なんか、結論すでに出てますが、文字列リテラルを上げたのはprintf()で使ったからです。
printfでの第一引数は両方文字列リテラルで渡しているので、どの道、ただの配列とは違うんですよ!!と強調しなければなりません。
配列を変更する処理を定義し、その上でリテラルだけ渡して、あとでデバッグに苦労するという実践派の人(初心者に限らず)、わりと多いんじゃないですか?
現に熟練のみなさんがこれほど顔赤くして議論し続けたんですから、まだ学習中の人にとりあえず課題として取り上げた自分の優しさに感謝してほしいぐらいです!

さて、通るコンパイラとしてborland C++ 5.5を挙げたのは単純に制限がゆるく、ほとんどの実行時エラーについて警告を出さないからです。
あとusaoさんがなぜ自分の揚げ足を取ろうとしていたのはわかりませんが、第二引数(&x)はprintf()で変更される処理がないだけで、変更できます。

beatle
記事: 1281
登録日時: 12年前
住所: 埼玉
連絡を取る:

Re: 激論中の文字列リテラル

#19

投稿記事 by beatle » 10年前

ただの屍のようだ さんが書きました:第二引数(&x)はprintf()で変更される処理がないだけで、変更できます。
printfが変な実装になっていて、引数を変更しようと思えば変更できますね。それは確かにそうです。
ただの屍のようだ さんが書きました:アドレスによって参照されたxは変更されたままです。
問題なのは、printfは引数を変更する仕様ではなく、実際はxは変更されないのに、「xは変更されたままです」と書いたことです。
それをusaoさんが指摘しただけだと思います。
ただの屍のようだ さんが書きました:現に熟練のみなさんがこれほど顔赤くして議論し続けたんですから、まだ学習中の人にとりあえず課題として取り上げた自分の優しさに感謝してほしいぐらいです!
顔を赤くしたのはただの屍のようださんの課題が原因だと思いますけどね。
C言語の仕様としてどうなのか、というのは重要なことなので盛り上がりますね。

SDさんの疑問は「値渡しだと元の変数は変更されないのに、ポインタ渡しだと変更されるのはなんで?」でした。
その疑問とただの屍のようださんの出した課題の論点がずれているのが、議論が白熱した原因の一つだと思います。

M.R

Re: 激論中の文字列リテラル

#20

投稿記事 by M.R » 10年前

25年ほどプログラムをやってますが
配列を変更する処理にリテラルを渡した事はないですね

ただの屍のようだ

Re: 激論中の文字列リテラル

#21

投稿記事 by ただの屍のようだ » 10年前

14:09 フォーラムルールに抵触する書き込みですので添削させて頂きました。 添削者:softya(ソフト屋)
・親しくない人に対して丁寧語を使わない行為 (ネタや冗談などは常識の範囲内で)
・円滑なコミュニケーションを心がけない行為
 申し訳ありませんが、冷静な議論をお願いします。

後、何度も書いているのでしつこいと思われるでしょうが、マナーを勉強して頂きたいのでお願いします。
「[IT資格]ネットワークスペシャリストについて • C言語交流フォーラム ~ mixC++ ~」
http://dixq.net/forum/viewtopic.php?f=3&t=13296#p106747

YuO
記事: 947
登録日時: 13年前
住所: 東京都世田谷区

Re: 激論中の文字列リテラル

#22

投稿記事 by YuO » 10年前

なんか知らないうちにこんなことが……。

参考用に「未定義の動作」という言葉の定義を書いておきます。
規格を知らない人がこの議論を読む上で参考になるかと思いますので。
# というか,この定義を知らないと話について行けないかと。

in ISO/IEC 9899:1999 Programming languages -- C / 3. Terms, definitions, and symbols / 3.4.3
1 undefined behavior
behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements
2 NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).
in JIS X 3010:2003 プログラム言語C / 3. 用語及び記号の定義
3.4.3 未定義の動作(undefined behavior) 可搬性がない若しくは正しくないプログラム構成要素を使用したときの動作,又は正しくないデータを使用したときの動作であり,この規格が何ら要求を課さないもの。
  参考 未定義の動作に対して,その状況を無視して予測不可能な結果を返してもよい。翻訳時又はプログラム実行時に,文書化された,環境に特有な方法で処理してもよい(診断メッセージの発行を伴っても伴わなくてもよい。)。さらに(診断メッセージを出力し)翻訳又は実行を中断してもよい。
私は,undefined behaviorは「してはいけないこと」という認識です。
「できる」「できない」と「してはいけない」は別のことなので。
# undefined behaviorは処理系が文書化してくれないと何がおこるかわからない。

ちなみに,文字列リテラルは同一の値であればまとめることも許されています (ym114さんが規格から引用された文のうち,後段の文の直前の文に書かれています)。
なので,文字列リテラルを書き換えた場合,別の文字列リテラルも一緒に変更される可能性があります。

アバター
usao
記事: 1887
登録日時: 11年前

Re: 激論中の文字列リテラル

#23

投稿記事 by usao » 10年前

>問題なのは、printfは引数を変更する仕様ではなく、実際はxは変更されないのに、「xは変更されたままです」と書いたことです。
>それをusaoさんが指摘しただけだと思います。
単にこれだけのことなんですけどね.
#この件については「リテラルがどうの」とは関係なく,向こうのスレッドで話すべき内容に思いますが,
 こっちにレスが来てしまっているので,ここで返答しておきます.

「&xを渡した先の関数でxの値を変更する」ことの例として,
なぜ「実際には変更しない」関数を「わざわざ選んで」書いてるのか本当に到底理解しかねます.
相手が初学者であれば,なおのこと,なるべく混乱や誤解を招かないような単純かつ明確な例を提示する方が良いと思うのですが.
(今回のレスを見るまでは,
 ”scanfで例を書くところをうっかり上のprintfのとこをコピペか何かで書く際に書き換え損ねた”
 みたいなレスが返ってくるのかなぁとか思ってたくらいです.)

質問者様なり他の第三者が混乱を招きそうだと思う箇所を先んじて訂正なり事実確認しようとしているだけであって
揚げ足取り とかいうつもりはないのですが,難しいですね.

beatle
記事: 1281
登録日時: 12年前
住所: 埼玉
連絡を取る:

Re: 激論中の文字列リテラル

#24

投稿記事 by beatle » 10年前

揚げ足と感じるかは人それぞれなので難しいですよね。
非表示エリア
この非表示エリアを表示するには、登録し、ログインする必要があります。

アバター
usao
記事: 1887
登録日時: 11年前

Re: 激論中の文字列リテラル

#25

投稿記事 by usao » 10年前

「XXXさんの書き込みはどうしても攻撃的に見えちゃう」とかまぁ私にもあります(!)し,仕方ないことかと思いますけど.
非表示エリア
この非表示エリアを表示するには、登録し、ログインする必要があります。

non
記事: 1097
登録日時: 13年前

Re: 激論中の文字列リテラル

#26

投稿記事 by non » 10年前

私の疑問も解決しましたし、これで解決にさせていただきます。
大変お疲れ様でした。これからも、初学者にとって、尋ねやすく、また少しかじった先学者が回答しやすい掲示板であって欲しいと思います。
この掲示板は呆け防止に大変役立っております。
non

beatle
記事: 1281
登録日時: 12年前
住所: 埼玉
連絡を取る:

Re: 激論中の文字列リテラル

#27

投稿記事 by beatle » 10年前

相手の書き込みが攻撃的に見えても、あくまでも冷静に対応するのがうまい対処だと思いますよ。
内心怒るのは仕方ないとしても、掲示板に投稿するテキストはあくまで冷静に、淡白に。
テキストは心が伝わりにくいので、テキストで怒った様子を表すと想像以上の効果になり得ます。
非表示エリア
この非表示エリアを表示するには、登録し、ログインする必要があります。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: 激論中の文字列リテラル

#28

投稿記事 by softya(ソフト屋) » 10年前

最後にコンパイラと動作状況をまとめておきます。追記を2つしました。
【補足】 今回の結論。リテラルの書き換えなど未定義な動作はどうなるかは運次第。なので、こうなると断言するのはやめましょう。
[table=width:50%;border:1px solid #cccccc;][tr=text-align:center;][td=border:1px solid #cccccc;padding 2px;]名前[/td][td=border:1px solid #cccccc;padding 2px;]OS[/td][td=border:1px solid #cccccc;padding 2px;]コンパイラ[/td][td=border:1px solid #cccccc;padding 2px;]バージョン[/td][td=border:1px solid #cccccc;padding 2px;]オプション等[/td][td=border:1px solid #cccccc;padding 2px;]結果[/td][/tr]
[tr=text-align:center;][td=border:1px solid #cccccc;padding 2px;] ソフト屋[/td][td=border:1px solid #cccccc;padding 2px;]Windows7 64bit[/td][td=border:1px solid #cccccc;padding 2px;]VC++ コマンドライン[/td][td=border:1px solid #cccccc;padding 2px;]2008[/td][td=border:1px solid #cccccc;padding 2px;]cl main.c[/td][td=border:1px solid #cccccc;padding 2px;]コンパイルエラーなし 書き換え◯[/td][/tr]

[tr=text-align:center;][td=border:1px solid #cccccc;padding 2px;]〃[/td][td=border:1px solid #cccccc;padding 2px;]〃[/td][td=border:1px solid #cccccc;padding 2px;]cygwin gcc[/td][td=border:1px solid #cccccc;padding 2px;]cygwin 1.7.18(0.263/5/3) gcc 4.5.3[/td][td=border:1px solid #cccccc;padding 2px;]gcc main.c[/td][td=border:1px solid #cccccc;padding 2px;]コンパイルエラーなし 書き換え◯[/td][/tr]

[tr=text-align:center;][td=border:1px solid #cccccc;padding 2px;]〃[/td][td=border:1px solid #cccccc;padding 2px;]〃[/td][td=border:1px solid #cccccc;padding 2px;]vc++ IDE上[/td][td=border:1px solid #cccccc;padding 2px;]2008[/td][td=border:1px solid #cccccc;padding 2px;]/TC指定 及び /TPも同様 DEBUGビルド win32(x86)[/td][td=border:1px solid #cccccc;padding 2px;]コンパイルエラーなし 書き換えNG[/td][/tr]

[tr=text-align:center;][td=border:1px solid #cccccc;padding 2px;;]〃[/td][td=border:1px solid #cccccc;padding 2px;]Ubuntu(Linux) 9.10[/td][td=border:1px solid #cccccc;padding 2px;]gcc[/td][td=border:1px solid #cccccc;padding 2px;]4.4.1[/td][td=border:1px solid #cccccc;padding 2px;]gcc main.c[/td][td=border:1px solid #cccccc;padding 2px;]コンパイルエラーなし 書き換えNG[/td][/tr]

[tr=text-align:center;][td=border:1px solid #cccccc;padding 2px;]〃[/td][td=border:1px solid #cccccc;padding 2px;]Windows7 64bit[/td][td=border:1px solid #cccccc;padding 2px;]学習用C言語開発環境[/td][td=border:1px solid #cccccc;padding 2px;]EasyIDEC Ver.0.9.0.0[/td][td=border:1px solid #cccccc;padding 2px;]デフォルトのまま[/td][td=border:1px solid #cccccc;padding 2px;]コンパイルエラーなし 書き換え◯[/td][/tr]

[tr=text-align:center;][td=border:1px solid #cccccc;padding 2px;]nonさん[/td][td=border:1px solid #cccccc;padding 2px;]Windows7[/td][td=border:1px solid #cccccc;padding 2px;]vc++ IDE上[/td][td=border:1px solid #cccccc;padding 2px;]2008ExpressEdition[/td][td=border:1px solid #cccccc;padding 2px;]DEBUGビルド[/td][td=border:1px solid #cccccc;padding 2px;]コンパイルエラーなし 書き換えNG[/td][/tr]

[tr=text-align:center;][td=border:1px solid #cccccc;padding 2px;]〃[/td][td=border:1px solid #cccccc;padding 2px;]Windows7[/td][td=border:1px solid #cccccc;padding 2px;]vc++ IDE上[/td][td=border:1px solid #cccccc;padding 2px;]2008ExpressEdition[/td][td=border:1px solid #cccccc;padding 2px;]RELEASEビルド[/td][td=border:1px solid #cccccc;padding 2px;]コンパイルエラーなし 書き換えされない[/td][/tr]

[tr=text-align:center;][td=border:1px solid #cccccc;padding 2px;]ただの屍のようださん[/td][td=border:1px solid #cccccc;padding 2px;]Windows7[/td][td=border:1px solid #cccccc;padding 2px;]vc++ ?[/td][td=border:1px solid #cccccc;padding 2px;]2010[/td][td=border:1px solid #cccccc;padding 2px;]?[/td][td=border:1px solid #cccccc;padding 2px;]コンパイルエラーなし 書き換えNG[/td][/tr]

[tr=text-align:center;][td=border:1px solid #cccccc;padding 2px;]〃[/td][td=border:1px solid #cccccc;padding 2px;]Windows7[/td][td=border:1px solid #cccccc;padding 2px;]gcc[/td][td=border:1px solid #cccccc;padding 2px;]?[/td][td=border:1px solid #cccccc;padding 2px;]?[/td][td=border:1px solid #cccccc;padding 2px;]コンパイルエラーなし 書き換えNG[/td][/tr]

[tr=text-align:center;][td=border:1px solid #cccccc;padding 2px;]〃[/td][td=border:1px solid #cccccc;padding 2px;]Windows7[/td][td=border:1px solid #cccccc;padding 2px;]borland C++[/td][td=border:1px solid #cccccc;padding 2px;]5.5[/td][td=border:1px solid #cccccc;padding 2px;]?[/td][td=border:1px solid #cccccc;padding 2px;]コンパイルエラーなし 書き換え◯[/td][/tr][/table]
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ISLe
記事: 2650
登録日時: 13年前
連絡を取る:

Re: 激論中の文字列リテラル

#29

投稿記事 by ISLe » 10年前

コマンドラインでオプション付けずにコンパイルしていたので最近のVisualStudioIDEでプロジェクトを作ると例外が出るのはわたしも知りませんでした。
そもそもそんなコードを書かないですし。

わざわざ触れることでもなく、未定義の動作なのに、ということは既に元のトピックで指摘していたのですけど、その意味を理解してはもらえなかったですね。

ISLe
記事: 2650
登録日時: 13年前
連絡を取る:

Re: 激論中の文字列リテラル

#30

投稿記事 by ISLe » 10年前

YuO さんが書きました:ちなみに,文字列リテラルは同一の値であればまとめることも許されています (ym114さんが規格から引用された文のうち,後段の文の直前の文に書かれています)。
なので,文字列リテラルを書き換えた場合,別の文字列リテラルも一緒に変更される可能性があります。
こっちで書き換えて、あっちで確認すると書き換わってなくて、こっちで確認してみたら書き換わってたけど、あっちではやっぱり書き換わってない、ということも起きるんですよね。

アバター
usao
記事: 1887
登録日時: 11年前

Re: 激論中の文字列リテラル

#31

投稿記事 by usao » 10年前

ところで,ほとんど関係ない話なのですけど

コード:

char str[] = "Hello!!";
char *str = "Hello!!";
の2つの書き方に関してですが,実用上,
上側と下側とで,メリット/デメリット とか(使い分け というか)あったりするのでしょうか?
(何か私は上側でしか書いたことが無いような…?)
非表示エリア
この非表示エリアを表示するには、登録し、ログインする必要があります。

ISLe
記事: 2650
登録日時: 13年前
連絡を取る:

Re: 激論中の文字列リテラル

#32

投稿記事 by ISLe » 10年前

コード:

char str[] = "Hello!!";
1. 文字列を格納するのに十分なchar型の要素を持つ配列の記憶域が確保されます。
2. 文字列リテラルが1.で確保された記憶域にコピーされます。
メリットは、文字列を書き換えることができること
デメリットは、文字列リテラルと重複する記憶域を確保することと文字列リテラルのコピーが発生すること
です。

コード:

char *str = "Hello!!";
1. charへのポインタ(アドレス)を格納する記憶域が確保されます。
2. 文字列リテラルの先頭要素を指すポインタ(アドレス)が1.で確保された記憶域にコピーされます。
メリットは、使用する記憶域が少ないこと
デメリットは、文字列を書き換えられない(正確には未定義の動作)こと
です。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: 激論中の文字列リテラル

#33

投稿記事 by softya(ソフト屋) » 10年前

ISLe さんに追加して書き加えるとしたら、char str[] = "Hello!!";は文字列コピーで、char *str = "Hello!!";はポインタ代入なので、ローカル変数に有った場合関数呼び出し毎に動作するので、深いループに有った場合速度差が累積する可能性がありますね。ISLe さんが暗に書いてますが念押しで書いときます。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ISLe
記事: 2650
登録日時: 13年前
連絡を取る:

Re: 激論中の文字列リテラル

#34

投稿記事 by ISLe » 10年前

特に組み込み系だと初期値のコピーが量産されてヒープやスタックをあっという間に食い潰してしまうので使い分けがとても重要です。

アバター
usao
記事: 1887
登録日時: 11年前

Re: 激論中の文字列リテラル

#35

投稿記事 by usao » 10年前

なるほど,確かに上側だと文字列コピーが発生する点を問題視するときに 下側のメリットがありますね.
ありがとうございました.

コード:

const char * const str = "Hello!!";  //下側的な書き方を使う場合でもこう書かない場合,
const char *str = "Hello!!";  //変数str(のメモリ)まで使いまわす予定,ということか.
char *str = "Hello!!";  //なら,これだと,変数strを 変更可能な対象 を指すために使いまわす予定,か.

閉鎖

“C言語何でも質問掲示板” へ戻る