専門家に聞いた!繰り返す痔の原因は!? >>

◎1--------------------------------------
#include<stdio.h>

int main(void)
{
int i=0;

while(i<=20){
printf("%d\t%d\t%d\t%d\t%d\n",++i,++i,++i,++i,++i);
}

return 0;
}
------------------------------------------
◎2--------------------------------------
#include<stdio.h>

int main(void)
{
int i=0;

while(i<=20){
printf("%d\t%d\t%d\t%d\t%d\n",i++,i++,i++,i++,i++);
}

return 0;
}
------------------------------------------

以上2つのプログラムについて疑問があるのですが、まず◎1についてですが、実行結果は、

5    4   3   2   1
10   9   8   7   6
15   14   13   12   11
20   19   18   17   16
25   24   23   22   21

以上のようになるのですが、自分の考えでは、「printf("%d\t%d\t%d\t%d\t%d\n",i++,i++,i++,i++,i++);」部分の、一番最初の「++i」から処理が始まり、
1  2  3  4  5
     ・
     ・
     ・
のようになる事を期待したのですが、一番最後の「++i」から処理が始まってしまいました。

次に、◎2についてですが実行結果は、

0   0   0   0   0
5   5   5   5   5
10  10  10  10  10
15  15  15  15  15
20  20  20  20  20

以上のようになりました。「printf("%d\t%d\t%d\t%d\t%d\n",i++,i++,i++,i++,i++);」部分で、iをいったん表示してから、iに1を加算するということで、次のiは1になっており、
4  3  2  1  0
     ・
     ・
     ・
のようにまた一番最後の「i++」から処理され以上のようになると思ったのですが、そうはなりませんでした。

以上、◎1と◎2について何故こうなるのかご回答いただければ嬉しいです。

このQ&Aに関連する最新のQ&A

A 回答 (6件)

既に回答があるように, Java とは異なり C/C++ では引数の評価順序について何も規定されていません. もっというと, 少数の演算子を除いて評価順序の規定は存在しません.つまり, 5個あるインクリメントをどの順に評価するかは全く分からないのです.


もっというと, この例では #1 でいわれるように 2つの副作用完了点の間で i の変更が複数回行われているので, その結果は未定義となります. つまり「どのような結果になるか規格では一切関知しない」ということです. なお, この場合「直前の副作用完了点」は前の分の終わりに, 「直後の副作用完了点」は関数 printf の呼び出しの直前にあります.
以下余談ですが, 「わざわざ区別するのも面倒なので, 常に後ろの引数からスタックに積む」処理系もわりと普通のような気がしますがいかがなもんでしょうか>#2.
ああ, もちろん「スタックに積む」とは規格のどこにも書かれていないので注意してください.
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。

>引数の評価順序について何も規定されていません. もっというと, 少
>数の演算子を除いて評価順序の規定は存在しません.つまり, 5個ある
>インクリメントをどの順に評価するかは全く分からないのです.
>もっというと, この例では #1 でいわれるように 2つの副作用完了点
>の間で i の変更が複数回行われているので, その結果は未定義となり>ます. つまり「どのような結果になるか規格では一切関知しない」と
>いうことです.

以上、細かくご説明ありがとうございます。
以上のご回答をふまえ、もっと勉強してみます!

お礼日時:2009/04/21 05:02

すでに回答があるように、この動作は、「未定義」とされています。


つまりは、printf() の実行後に i が 5 増えているとすら、断言できないというのが、規格上は正解です。

多くのコンパイラではそうなるでしょうが。

参考URL:http://www.kouno.jp/home/c_faq/c3.html#2
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。

みなさまからのご回答にもありましたが、「二つの副作用完了点の間に、オブジェクトの値を2回以上変更している場合」は動作が未定義であるとわかりました!

お礼日時:2009/04/21 05:23

当該のprintfの初回実行直後の


iの値は5である、としか言えません。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。

動作が未定義ということで、そうとしか言えないという事ですね!
理解しておきます。

お礼日時:2009/04/22 08:04

#2 です。


間違いを書いてしまったので訂正です。(まあ、「例」なので、間違いじゃないと言えなくもないかも)

ご指摘を受けて、調べてみたら、そもそも、「引数を積むのは無条件に右から左」がむしろ、主流(少なくともよくある例)のようです。

そういうわけで、

---------------------------
「多くのコンパイラが、引数を、「左から
(先頭から)順に積み上げ、反対側(後ろ
から)順に処理する」という挙動をします。
---------------------------
というのは間違いでした。

※よく考えたら、「多くのコンパイラが」と書いている時点で間違いですがな。
    • good
    • 0
この回答へのお礼

>「引数を積むのは無条件に右から左」がむしろ、主流(少なくともよくある
>例)のようです。

以上のご回答理解しておきます!
ありがとうございました。

お礼日時:2009/04/22 08:02

正解は既に回答があるとおり、「何が


起こるかわからない」「何が起こって
も文句は言えない」です。

ただ、具体的な動作の例は、以下のよ
うなものがあります。
※これは、あくまでも、「あるコンパ
イラはこういう動きをする」というだ
けで、そうでなければ、ならないとか、
そのような動きを期待してもいいとか
言う意味ではありません。

C言語では、多くの場合、関数への引
数渡しは「スタック」が使われます。
結論から言えば、多くのコンパイラが、
引数を、「左から(先頭から)順に積み
上げ、反対側(後ろから)順に処理す
る」という挙動をします。

引数の評価は、関数の呼び出し前(引
数をスタックに積み上げるとき)に行
われるので、多くの場合、引数は左 →
右の順に評価されます。

ただ、printf() のような「可変引数」
の関数は事情が異なってきます。
この場合、最初の引数を理解しないと、
引数の数すら把握できません。

従って、「可変引数」の関数は、逆に
「右から(先頭から)順に積み上げ、
反対側(前から)順に処理する」
ということになります。このため、引
数は通常とは逆に、後ろから(右 →
左)の順序で評価されることになります。

あと、++ を前置したときと後置したと
きの挙動ですが、入門書には、時々

前置:インクリメントしてから、評価
後置:評価してからインクリメント

と書かれていますが、これは、厳密に
は間違いです。
正解は、

前置:評価結果はインクリメント後のもの
   副作用として、インクリメント実施

後置:評価結果はインクリメントする前の
   もの、副作用としてインクリメント
   実施
   
になります。
つまり、評価とインクリメントの時間的な
関係までは規定されていないのです。

ですから、

a = (i++) + (j++);

の場合、
・i を評価して、インクリメント
・j を評価して、インクリメント
でもいいですし、
・i を評価
・j を評価
・i をインクリメント
・j をインクリメント
の順序で動作してもOKです。
今回の例では、
・i をそれぞれ評価して、
・あとで、i をそれぞれインクリメント
という動作にしているのでしょう。

(ただ、次の式が評価される「前」には終
わっている必要はあります。これが、回答
にある「副作用完了点」というものです)

以上は、あくまでも、「そういう例もある」
「そういう例なら、今回のような結果になる」
というだけで、「これが正しい動作」では
ありません。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。

インクリメント処理が副作用、評価結果が副作用完了点とわかりました。

評価とインクリメントの時間的な関係までは規定されていないということもわかりました!

お礼日時:2009/04/21 04:57

副作用完了点から次の副作用完了点までの間に、同一のオブジェクトを複数回更新していますので、未定義の動作を引き起こしています。


簡単にいうと、やってはいけない反則行為を行っていますので、何が起きても仕方がありません。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。

動作が未定義であるということがわかりました。
もっと理解を深めるために、副作用について調べてみます!

お礼日時:2009/04/21 04:48

このQ&Aに関連する人気のQ&A

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人が検索しているワード

このQ&Aと関連する良く見られている質問

Qfgetsなどのときのstdinのバッファを消すには?

こんにちは,今C(C++でない)を使用しています。
たとえば,
char str[20]
fgets(str,sizeof(str),stdin)
としたときに20字以上を打つと,stdinのバッファに20字以上の分が残ったままになります。

C++などでは
fflush(stdin)で,うまくいきますが,普通のCでは対応がされていないみたいでうまくいきません。

よろしくお願いします。

Aベストアンサー

あ,テキスト入力だからこんな大掛かりなことしなくてもいいんだ.
末尾に'\n'が出るまで掃出せばいいんですよね.

fgets(str, sizeof(str), stdin);
if ( str[strlen(str)-1] != '\n' ){
while( getchar() != '\n' );
}

でいいんだ.失礼しました.

Qchar型にint型の数値を代入する。

たとえば、
int num;
char box; 

numに何らかの整数値が入っているときに、そのnumの中に入っている値をchar型に文字列として代入したいときはどのようにすればいいのでしょうか?

Aベストアンサー

sprintf()っていう関数がありますよ。書式は

sprintf(char型の配列の先頭ポインタ,フォーマット,変数...)

二番目の引数以降はprintf()の引数と同じです。たとえば

int num;
char box[256];
num=100;
sprintf(box,"%d",num);
printf("%s",box);

→100と出力される

Q%P と %X の違い

アドレスを表示させるときの、%p は、%x と同じ16進数で表示される
んでしょうか?
表示の違いと言えば、大文字か小文字の違いだけなんでしょうか?
16進数の大大文字は「%X」というふうに、Xを大文字で指定すれば、
結果も大文字で表示されますよね。
%pはどういった意味なんでしょうか?



#include <stdio.h>
main()
{
char a;
short b;
printf("bのアドレスは%d,%p,%x\n", &b, &b, &b);
return 0;
}

Aベストアンサー

およそ #1 の通りで, アドレス値を出力するためには %p を使わなければなりません. %x は unsigned int を 16進で出力するという指定であり, アドレス値を出力するときに %x を使ってはいけません. 処理系によっては動いてしまうこともありますが正しいプログラムではありません.

QC言語のプログラムが実行できません。

C言語のプログラムが実行できません。

コンパイルは出来るんですが、実行すると、「Segmentation fault」と表示されてしまいます。

これは何のエラーなんでしょうか?
基本的な事かもしれませんが、分かる方宜しくお願い致します。

Aベストアンサー

僕も何度も出したなぁ。

ひとくちにSegmentation faultといっても、それこそさまざまな要因があるので、
これだけで原因を突き止めるのは非常に難しいです。

コンパイルはあくまで文法としてみているだけであり、
処理の流れ、メモリ確保など、プログラムそのものを見ているわけではありません。
このエラーが出るのは文法などよりもっと上位の原因なのです。
たとえばですが。
長さ10の配列があったとして、11番目以降を参照したりすると、
そういうのが出たような気がします。

ですから、変数があれば、その内容をprintf文で逐一出していき、
変な値が入っていないとか、少しずつ直していくしかないと思います。

Qcharと%c , %s の関係について

char型の変数の扱いで悩んでいます。
具体的には以下の二つのプログラムの差異についてです。

----------------------
char c;

scanf("%c", &c);
printf("%c\n", c);

-----------------------
char c;

scanf("%s", &c);
printf("%s\n", &c);

-----------------------


上のプログラムは正しいと思うのですが、下のプログラムが正しいのかどうか、わかる方に教えていただきたいと思い質問させていただきました。

どちらのプログラムも問題なく動作します。
僕自身は 下のプログラムの printf 関数については間違った使い方なのではないかと思っています。

scanf("%s", &c) は入力された文字のうち、終端文字の手前までの文字を引数のポインタが示すオブジェクトへ順に格納していく関数だと理解しているので、入力された文字が一文字だった場合、&cの示すオブジェクトに文字が代入されると考えたからです。

逆に printf("%s", &c) は、&cの示すオブジェクトから”ヌル文字”の手前までの文字列を順に表示する関数だと理解しているので、問題なく動作しているのは&cで示されるオブジェクトの後ろの領域が偶然'\0'だったからではないかと考えたからです。

何かの本で、未使用の領域は0である確率が高いという記述をみたことがあり、'\0'は0と同じだということなので問題なく動作する率が高いのではないかと思っています。


僕の考え方がどの程度正しくて、正確にはどうなのかを教えて欲しいです。


ちなみに、

-----------------------
char c;
char str[100];

scanf("%s", str);
scanf("%c", &c);
------------------------

と書くと c には改行文字が代入されてしまいます。
scanf("%s", str);
において"aasssdd "と最後に空白を入れると
c には空白文字が代入されます。

しかし、
--------------------------
char str1[100];
char str2[100];

scanf("%s", str1);
scanf("%s", str2);
--------------------------
においては、
scanf("%s", str1);
で "asdfg "と最後に空白を入れても次のstr2が空白で始まることはありません。


この辺りの処理がどのような法則で実行されているのかが分かりづらくて悩んでいます。
おそらく、
scanf("%s", str);
の場合には最初の文字が空白や改行文字でも、その次に有効な文字があればそれらの改行や空白を無視するのではないかと思っています。


分かる方がいましたら回答をよろしくお願いします。

char型の変数の扱いで悩んでいます。
具体的には以下の二つのプログラムの差異についてです。

----------------------
char c;

scanf("%c", &c);
printf("%c\n", c);

-----------------------
char c;

scanf("%s", &c);
printf("%s\n", &c);

-----------------------


上のプログラムは正しいと思うのですが、下のプログラムが正しいのかどうか、わかる方に教えていただきたいと思い質問させていただきました。

どちらのプログラムも問題なく動作します。
僕自身は 下...続きを読む

Aベストアンサー

> char c;
> scanf("%s", &c);
char c では1文字分の領域しか確保されていないので、
1文字以上の文字列を無理矢理格納すると
他のデータが存在しているかもしれない領域を書き換えてしまいます。(メモリ破壊)

なお、'\0'はscanfが(本来書き込んではいけない領域に対してですが)書き込んでいます。


> と書くと c には改行文字が代入されてしまいます。
最初のscanfが改行文字を読み込んでいないからです。
以下のページに書かれている内容と本質的には同じ。
http://www9.plala.or.jp/sgwr-t/c/sec05.html#s5-

> 最初の文字が空白や改行文字でも、その次に有効な文字があればそれらの改行や空白を無視するのではないかと思っています。
そう考えて問題ないと思います。

Q簡単なようで分からない?for文での加算処理方法及び出力表示方法

忙しい中失礼します。

・・・???分からないので質問させていただきます。

プログラム過程
1.5桁の数値入力: 68562
2.偶数桁のみ2倍する: 8x2=16 6x2=12
3.68592→6165122という処理を行う。
4.6165182→処理:6 1+6 5 1+2 2→再度5桁表示:67532 (※2桁目と4桁目の数字が1.とは異なります)
5.67532各桁の加算処理を行い(6+7+5+3+2=23)、”23”をディスプレイ上に表示させる。

希望出力結果:
   5桁入力 68592
67532
合計 23    ←加算処理方法及び出力表示方法(ここが分からないのです)
を望んでいます。

分からないところ:どうやって67532の加算処理を行い、どうやってそれをディスプレイ上に表示させれば良いのかが分かりません???
           以下のプログラム[1]、[2]から、[2]を[1]に組み込もうとしています。ですが、[2]for文のカッコ内()の条件???、及び[2]を[1]のどこに組み込めば”合計23”が表示されるのかも分かりません。
そもそもfor文で行うこと自体が無理?そんなことはない、と思っているのですが・・・。それとも[2]を[1]に組み込もうとしてること自体が間違っている?のですかね???

大変忙しいとは思いますが、アドバイス等ありましたらお願いします。
<使用環境: Borland?? C>

[1] 1~4までのプログラミング(完成済)
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main()
{
int num,tra;
int inpt=5;
int inptnum;
int s;

printf("5桁入力; ");
scanf("%d", &num);

for(inpt ; inpt >=1 ; inpt--)
{
if(inpt%2 == 0) {
inptnum = pow(10,inpt-1);
tra = (num/inptnum)%10;
s=(tra*2)/10+(tra*2)%10;
printf("%d",s);
}

else
{inptnum = pow(10,inpt-1);
tra = (num/inptnum)%10;
printf("%d",tra);}
}

system("pause");

return 0;
}

[2]単独での加算処理プログラム(完成済)
#include <stdio.h>
#include <stdlib.h>
int main()
{
int count;
int Sum;
Sum=0;
for (count = 1; count <= 6; count++)
{printf("%d ",count);
Sum = Sum + count; }
printf("\nSum%d\n", Sum);

system("pause");
return 0;
}

忙しい中失礼します。

・・・???分からないので質問させていただきます。

プログラム過程
1.5桁の数値入力: 68562
2.偶数桁のみ2倍する: 8x2=16 6x2=12
3.68592→6165122という処理を行う。
4.6165182→処理:6 1+6 5 1+2 2→再度5桁表示:67532 (※2桁目と4桁目の数字が1.とは異なります)
5.67532各桁の加算処理を行い(6+7+5+3+2=23)、”23”をディスプレイ上に表示させる。

希望出力結果:
   5桁入力 68592
67532
合計 23    ←加算処理方法及び出...続きを読む

Aベストアンサー

あなたのコードをベースにするならこうでしょうか。
(ANo.2の回答と同じです。)
----source1-----------------------------
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main()
{
 int num,tra;
 int inpt=5;
 int inptnum;
 int sum=0; /* 追加 */

 printf("5桁入力; ");
 scanf("%d", &num);

 for(inpt ; inpt >=1 ; inpt--) {
  if(inpt%2 == 0) {
   inptnum = pow(10,inpt-1);
   tra = (num/inptnum)%10;
   tra=(tra*2)/10+(tra*2)%10; /* traに結果が入っていて欲しいので書き換え */
   printf("%d",tra);
  }
  else {
   inptnum = pow(10,inpt-1);
   tra = (num/inptnum)%10;
   printf("%d",tra);
  }

  sum += tra; /* 追加 */
 }

 printf("\nSum %d\n", sum); /* 追加 */

 return 0;
}
----------------------------------------
「6+7+5+3+2=23」をやりたいのだから、
6、7、5、3、2が出現する場所を考えれば悩むところはないと思います。
(例えば、何も考えずに2カ所のprintf("%d",tra);の直前にsum += tra;を挿入しても結果は得られる。)

あと、
> inptnum = pow(10,inpt-1);
> tra = (num/inptnum)%10;
この2行は(inpt%2 == 0)が真でも偽でも必ず実行されるので
ifの外に出せば2回も書く必要はありません。
2つのprintfも、上のコードのように出力する変数を同じにしておけば、外に出して1つだけですみます。
(ここまでやるとelseブロックが空になり、elseは不必要なことが分かります。)


なお私なら、
・5桁限定にしたくない
・powは必要ない(下の桁から処理すれば、pow使わなくても10で割り続けるだけで良い)
ので以下のように書きます。
(各桁の分解はANo.1と基本的に同じ。)

----source2-----------------------------
#include <stdio.h>
int main(){
 int nums[20] = {0};
 int i;
 int num, val;
 int sum = 0;

 printf("Input Number: ");
 scanf ("%d", &val);

 i=0;
 while(val!=0) {
  i++;
  num =val%10;
  val /= 10;

  if(i%2 == 0){ /*偶数桁*/
   num = (num*2)/10+(num*2)%10;
  }
  nums[i-1] = num; /* あとで表示するために保存 */
  sum += num; /* 各桁の加算 */
 }

 for(;i>=1;i--){
  printf("%d",nums[i-1]);
 }
 printf("\nSum: %d",sum);

 return 0;
}
----------------------------------------

入力が数値でなくて(数字のみからなる)文字列で良いなら、
桁の分解は
http://okwave.jp/qa3427592.html
のANo.2の様に、文字列中の各文字を取り出す形でもできる。
(分かりにくくなるかもしれないので今回は除算と剰余算で取り出しました。)

あなたのコードをベースにするならこうでしょうか。
(ANo.2の回答と同じです。)
----source1-----------------------------
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main()
{
 int num,tra;
 int inpt=5;
 int inptnum;
 int sum=0; /* 追加 */

 printf("5桁入力; ");
 scanf("%d", &num);

 for(inpt ; inpt >=1 ; inpt--) {
  if(inpt%2 == 0) {
   inptnum = pow(10,inpt-1);
   tra = (num/inptnum)%10;
   tra=(tra*2)/10+(tra*2)%10; /* t...続きを読む

QEnterキーを押されたら次の処理に移るという事をしたい。

コンソールアプリケーション上で文字列を表示させた後、ユーザーがエンターキーを押したら次の文字列を表示するという仕様にしたいのですが、エンターキーだけ入力待ちにするっていうのはどのように書けばいいんでしょうか?

Aベストアンサー

#include <stdio.h>

int main(void)
{
char *str[] = {"abc", "def", "ghi", "jkl"};
int i;

for(i = 0; i < 4; i ++){
while(getchar() != '\n') ;
puts(str[i]);
}
return 0;
}

Q*をユーザーが入力した数字の数だけ表示するプログラムの作り方を教えてください

はじめまして。
現在C言語の勉強している大学生です。

『*』をユーザーが入力した数字の数だけ表示するプログラムの作り方を教えてください。
例えばユーザーが5と入力したら*****とでるもの。

(本に載っている演習なのですが答えがのっていないのです。)
whileかforを使うのだと思うんですが。。
よろしくお願い致します。

Aベストアンサー

これで、OKです。
とても簡単なプログラムです。

#include<stdio.h>
int main(void)
{
int i,num;
printf("何個表示しますか?->");scanf("%d",&num);

for(i=0;i<num;i++)
{
printf("*");
}
printf("\n");

return 0;
}

Qfgetsで拾われる改行文字を削除したい

お世話になります

 C言語初心者のものです。今課題でC言語を用いたプログラミングを
Fedora上でやっています。問題は、fgetsでテキストファイルから、取得
した文字列の中から改行文字を削除できないことです。文字変数のアド
レスはわかっているのですが、終端文字に置換しようとすると、セグメ
ントエラーになってしまいます。これは如何にして解決すべきでしょう
か。よろしくお願いします。

Aベストアンサー

ポインタとかアドレスとか、C言語の用語としてあるものを別の意味に使うとまぎらわしいです。

「ポインタ」「アドレス」と言われたら、 この例なら str, str+i が思い浮びます。
「文字変数のアドレス」だと
char c ;
に対しての
&c
が思い浮びます。

配列なら「添字」、意味的には「x文字目」ですね。

> for(i=0;;i++){
> if(*(str+i)=='/n') {
> *(str+i)='\0';
> break;
> }
> }
/nが\nの間違いなら、この方法で半分正解です。もう少し広い範囲(可能なら全体)で見ないことにはなんとも言えません。
fgetsが最大文字数に達したり、ファイルの最後になったりで、strに改行文字が含まれない場合には、このループは止まりません(Segmentension Falutになって止まる)

・そのような状態になってないか、予めチェックする
・ループを終了させる仕組みを用意しておく
: forの終了条件を記述する、for中で if(*(str+i)=='\0') { break;} 等としておく、等
といった対策が必要です。


あと細かいところを言えば
・strを配列で用意したなら *(s+i)じゃなくてs[i]でいいんじゃないかな
・あるいは char *pみたいにしておいて、 iのループでなく pでループを組む( for(p=str;*p!='\0';p++) )とか。

ポインタとかアドレスとか、C言語の用語としてあるものを別の意味に使うとまぎらわしいです。

「ポインタ」「アドレス」と言われたら、 この例なら str, str+i が思い浮びます。
「文字変数のアドレス」だと
char c ;
に対しての
&c
が思い浮びます。

配列なら「添字」、意味的には「x文字目」ですね。

> for(i=0;;i++){
> if(*(str+i)=='/n') {
> *(str+i)='\0';
> break;
> }
> }
/nが\nの間違いなら、この方法で半分正解です。もう少し広い範囲(可能なら全体)で見ないことにはなんとも言えません。
fgetsが...続きを読む

Qc言語のポインタへの文字列入力についてです。

当方c言語初学者なのですがscanfを使ってポインタに文字列を入力したいのですがバグが発生して進みません・・・どういうことなのでしょうか?
#include <stdio.h>

int main(void)
{
char *a;

scanf("%s", a);
printf("%s", a);

return (0);
}

*aをa[128]; のように配列に変えるとうまくいくことはなんとかわかるのですが・・・助けてください(^_^;)

Aベストアンサー

ポインターというのは格納された値のアドレスのメモリーを指すための変数です。
一方、配列はデータを格納するためのメモリーを確保して、その先頭のアドレスが入っていますよ。

char *a;
scanf("%s", a);

と書くと、aを初期化せずにaの指す先にscanfで文字列を入れます。
大抵、プログラムが書き込むことを許されていないメモリーに書き込みを行おうとしたことを検出したOSからそのプログラムは強制終了されます。

char *a = "aaaaa";
scanf("%s", a);
となっているとどうなるか。
aは"aaaaa"が格納されているメモリーを指すように初期化されます。
ただ、大抵"aaaaa"は固定値を入れるために書き込みができないメモリーに格納されています。
よって、scanf("%s", a);も書き込みができないところに書こうとしたことをOSに検出され、強制終了となります。

2つ方向があると思います。
1. char a[128];のように宣言し、スタック上にメモリーを確保し、その先頭アドレスが入っているaを渡す方法。
2. mallocなどでヒープにメモリーを確保する方法。

1だと、
char a[128];
scanf("%127s", a);
のように書き、scanfで読み込んだ分をaから始まるメモリーに書くことになりますが、char a[128];で配列を確保しているので書き込みを行うことができ、強制終了はされません。

2だと、
char *a = malloc(128);
scanf("%127s", a);
printf("%s", a);
free(a);
のように書きます。
配列はスタック上に取られ、スタック上に取った値はプログラムがその関数を抜けるときに自動的に解放されますが、mallocで確保したメモリーは自動で解放されないので自分でfreeを呼んで開放する必要があります。

%127sがそろそろ気になっていると思います。
こうやって127文字までしか受け取らないようにscanfに指示しています。
C言語では文字列の最後は終了を示すNUL文字が入るので、確保したメモリーよりも1少ない値となっています。

というわけで、ちゃんとメモリーを確保してからscanfで書き込みましょう。ポインターはあくまでどこかのメモリーアドレスを指すだけで、指した先がちゃんと確保されているかどうかは知りませんから。

ポインターというのは格納された値のアドレスのメモリーを指すための変数です。
一方、配列はデータを格納するためのメモリーを確保して、その先頭のアドレスが入っていますよ。

char *a;
scanf("%s", a);

と書くと、aを初期化せずにaの指す先にscanfで文字列を入れます。
大抵、プログラムが書き込むことを許されていないメモリーに書き込みを行おうとしたことを検出したOSからそのプログラムは強制終了されます。

char *a = "aaaaa";
scanf("%s", a);
となっているとどうなるか。
aは"aaaaa"が格納されている...続きを読む


このカテゴリの人気Q&Aランキング