ページ 11

入力したcharの中身が消えてしまいます。

Posted: 2014年12月03日(水) 23:28
by ユキ
charの中身を別関数で変更するとmainに戻った時に消えてしまいます。

---------------------------------------------------------
コード1
---------------------------------------------------------
 #include <stdio.h>
 
 void input(char one) {
   //どちらでやっても同じです。
   //scanf("%c", &one); 
   //one = getchar();
 }
 
 int main(void) {
   char one = '\0';

   input(one);
   printf("%c\n", one);

   return 0;
 }
---------------------------------------------------------
---------------------------------------------------------
実行結果1 (入力文字 a)
---------------------------------------------------------
 Macintosh:~ user$
 a
  
 Macintosh:~ user$
---------------------------------------------------------

以上のコード1だと消えてしまいますが
以下のコード2のように配列にすると受け取れます。

---------------------------------------------------------
コード2
---------------------------------------------------------
 #include <stdio.h>
 
 void input(char one[]) {
   scanf("%c", one);
 }
 
 int main(void) {
   char one[1];

   input(one);
   printf("%s\n", one);

   return 0;
 }
---------------------------------------------------------
---------------------------------------------------------
実行結果2 (入力文字 a)
---------------------------------------------------------
 Macintosh:~ user$
 a
 a
 Macintosh:~ user$
---------------------------------------------------------

C言語を学び始めて間もないので、とても初歩的なミスかもしれませんが
char には何か特別なルールでもあるのでしょうか?
自分で調べても理由がわからなかったのでご教授お願いします。

ーーーーーーーーーーーーーーーー
環境

使用コンパイラ CC
・Apple LLVM version 6.0 (clang-600.0.54) (based on LLVM 3.5svn)
・Target: x86_64-apple-darwin14.0.0
・Thread model: posix

OS
・OS X Yosemite (10.10.1)
・MacBook Air (11-inch, Mid 2012)
・プロセッサ 1.7 GHz Intel Core i5
・メモリ 4 GB 1600 MHz DDR3

使用エディタ
・CotEditor 2.0.2

保存形式
・改行コード CR/LF
・エンコーディング Unicode (UTF-8)
・拡張子 .c

Re: 入力したcharの中身が消えてしまいます。

Posted: 2014年12月03日(水) 23:41
by nullptr
1で関数内で変更しているのはoneのコピーであり、2ではoneを書き換えています。
char[]は配列、つまりポインタなので、引数として渡したoneそのものを参照できますが、charとした場合は値をコピーしただけの別物なので、元のoneが変更されることはありません。

例えば、
void input(char two)とした場合に、input(one)と呼び出した場合、twoにはoneのコピーが生成され代入されます。コピーした別物なので、関数内部でtwoを変更してもoneは変更されません。
void input(char* two)とした場合、input(&one)と呼び出せば、twoにはoneのポインタが代入されますので、関数内で参照することでoneそのものを書き換えることが可能になります。
void input(char two[])とした場合ですが、配列型は配列の最初の要素へのポインタを指しただけのポインタに過ぎませんので、この場合はvoid input(char* two)とした場合と意味は違いますが本質は全く同じです。

Re: 入力したcharの中身が消えてしまいます。

Posted: 2014年12月03日(水) 23:42
by みけCAT
ユキ さんが書きました:C言語を学び始めて間もないので、とても初歩的なミスかもしれませんが
char には何か特別なルールでもあるのでしょうか?
自分で調べても理由がわからなかったのでご教授お願いします。
なぜ「char には何か特別なルールでもある」と予想したのでしょうか?intなどで実験し、「消えない」ことを確認したのでしょうか?
この場合引数の型がcharかどうかは関係なく、intだろうがdoubleだろうが構造体だろうが消えるはずです。
C言語では、関数の仮引数の値は呼び出し元の値とは関係ない、コピーされた値です。
そして、関数から戻った時点で、staticでないローカル変数と同様に消えます。
ある関数から呼び出し元の関数で保存している値を書き換えるには、ポインタを渡してください。
(もしくは、C++なら「参照」が使えます)
配列はポインタとして渡されるので、書き換えることができます。

ついでに、
ユキ さんが書きました:---------------------------------------------------------
コード2
---------------------------------------------------------
 #include <stdio.h>
 
 void input(char one[]) {
   scanf("%c", one);
 }
 
 int main(void) {
   char one[1];

   input(one);
   printf("%s\n", one);

   return 0;
 }
---------------------------------------------------------
このコードはバッファオーバーランを起こす、危険なコードです。

コード:

#include <stdio.h>

void input(char one[]) {
	scanf("%c", one);
}

int main(void) {
	char one[1];

	input(one);
	printf("%c\n", one[0]);

	return 0;
}
とすると良いでしょう。

Re: 入力したcharの中身が消えてしまいます。

Posted: 2014年12月04日(木) 00:02
by ユキ
新ゝ月さん、みけCATさん返信ありがとうございます。

charで行った時になったので「char には何か特別なルールでもある」と勝手にcharのみだと決めつけていました。
次からは他の型の場合も確認します。
コード2の書き直しありがとうございます。

配列はポインタとして渡されるということを理解していませんでした。

今日1日悩んでいたので、本当に助かりました。
お二人とも、ありがとうございます。