ことの始まりの、私のコメント&ソース
私が読んでいるC言語のテキストに、小文字を大文字にする関数「toupper」が出てきたのだけれど、この関数の宣言がctype.hに入っているということの説明がなかったので「関数が宣言されていません」というエラーが出てコンパイルできなかった。
じゃあ試しに自分で作ってみよう、とやってみたのがこれ。
関数「upper」は、
文字列 moji を入力、
・moji[0]〜moji[n]まで1字1字に対して文字コードを調べ、
・97〜122[a-z]であれば文字コードの数字を-32し(65〜90はA-Z)、
・それ以外であればそのままにし、
・できあがった文字列を返す
という処理をするものにしたかった。
# include <stdio.h> char upper(char moji[ ]); /* 関数を宣言 */ main() { char a[80]; scanf("%s",&a); /* scanfを選んだのに深い意味はなく、getsでもよかった */ printf("%s\n",upper(a)); } char upper(char moji[ ]) { int i=0; while(moji[i]){ if(moji[i]>=97 && moji[i]<=122) /*moji[0]〜moji[n]の1字1字に対し文字コードを調べ、*/ moji[i]=moji[i]-32; /* 97[=a}以上122[=z]以下であれば文字コードの数字を-32し、 */ ++i; /* それ以外であればそのままにし、*/ } return(moji); /* できあがった文字列を返す */ }結果:
qwerty ←自分で入力
((((( ・瑞瑞瑞瑞瑞瑞瑞(以下略)
このソースでは最後のreturn(moji);のところに対して
「警告:ポインタの型が合わない (return)」
という警告が出る。
コンパイルはできるけれど、どのような文字列を入れても
((((( ・瑞瑞瑞瑞瑞瑞瑞(以下略)
という文字列を吐き出して終了する。
return(moji);の前に
printf("%s",moji);
と入れてみると、このわけわからん文字列の上の行に、全ての文字が大文字になった文字列がちゃんと出力されるので、文字列 moji には思った通りの値が入っていると思うのだけれど。
ちなみにこの後、
・文字 moji[n] を入力、
・moji[n]に対して文字コードを調べ、
・97[=a}以上122[=z]以下であれば文字コードの数字を-32し、
・それ以外であればそのままにした文字を返す
というものにしてみたら、と思って下↓のようにしたら、正常に(というか、とりあえず思ったようには)動いた。
両者の違いは、関数に渡す/関数から返される値が文字であるか、文字列であるかの違いだと思うのだけれど、どうして前者がダメなのか、ちょっと腑に落ちないのです。
# includechar upper(char moji); main() { char a[80]; int i; scanf("%s",&a); i=0; while(a[i]){ printf("%c",upper(a[i]));/*moji[n]の1文字だけを送る*/
i++; } putchar('\n'); } char upper(char moji) /*moji[n]の1文字だけを受け取り、処理して1文字返す*/
{ if(moji>=97 && moji<=122){ /*97[=a}以上122[=z]以下であれば文字コードの数字を-32し、*/
moji=moji-32; /*それ以外であればそのままにした文字を返す*/
} return(moji); }結果:
qwerty ←自分で入力
QWERTY
NH氏のコメント
おいらもあんまり詳しくないが・・・・
> ところがこれでは最後のreturn(moji);のところに対して
> 「警告:ポインタの型が合わない (return)」
> という警告がでる。
キャラクタ型変数をかえす、っていってるのに、返してるのはキャラクタ型配列のポインタだからでしょ。
1文字づつやるとうまくいくのはそれが原因。
うまく動いてない方は、mojiって配列のポインタを返しているけど、ローカル変数は関数が終了した時点で破棄されるはずだから、意味のないポインタを返していることになる。
で、配列を返したい時は、引数をポインタにしてそこに返したい値を入れていくのが一般的。
だから、次のようにしてみたら?
いけると思うが、あかんかったらすまんの。
# include <stdio.h> void upper(char *moji); main() { char a[80]; scanf( "%s",a ); /* aに&はいらんと思う */ upper( a ); printf( "%s\n",a ); } void upper(char *moji) /* <--引数をポインタに */ { int i=0; while(moji[i]){ /* <--ここちょっと気に食わんけどまぁいいや */ if(moji[i]>=97 && moji[i]<=122) moji[i]=moji[i]-32; ++i; } }結果:
qwerty ←自分で入力
QWERTY
私のコメント
アドバイスありがとう。
>> ところがこれでは最後のreturn(moji);のところに対して
>> 「警告:ポインタの型が合わない (return)」
>> という警告がでる。
>
>キャラクタ型変数をかえす、っていってるのに、返してるの
>はキャラクタ型配列のポインタだからでしょ。
確かにここのところに違いがあるのかなぁ、という予感はしていたんだけれど……。(^^;
実際のところ、「キャラクタ型配列のポインタ」ってのは、「キャラクタ型変数」ではないの?
ちゅうのは、「ポインタ」というのは、文字列の最初の文字のアドレスだと認識しているのだけれど、とすればそれは普通の数字ではないかと思えるのです……。
注)ここで私は、「アドレスを表す数字(ポインタの値)だって、中身は例えば「3910」とかなんだから、結局は普通の数字ちゃうのん」と言っている。
>1文字づつやるとうまくいくのはそれが原因。
>うまく動いてない方は、mojiって配列のポインタを返してい
>るけど、ローカル変数は関数が終了した時点で破棄されるは
>ずだから、意味のないポインタを返していることになる。
結局、upper 関数は、return(moji)で配列のポインタを返しはするけれど、ポインタを返した直後にその(ポインタが示す場所にある)データを破棄しちゃうので、ああいうぐちょぐちょのデータが出てきてしまうってことなのかな?
>で、配列を返したい時は、引数をポインタにしてそこに返し
>たい値を入れていくのが一般的。
なるほどなるほど。
しかしこれはこれで、どうしてその配列(ここではa[ ])が、グローバル変数でなくていいのか、ってことだなあ。
いや実際、main 関数内で宣言しても大丈夫だもんねえ(やってみたら)。
>だから、次のようにしてみたら?
>いけると思うが、あかんかったらすまんの。
さんきゅーー!
ちゃんと動きました。
上の君の助言に基づいて(下まで読み進まずに(^^;)訂正してみたら、↓とほとんど同じものを作っていました。
但し、2つだけ違いました。
1.
char a[80];
を、mainの前に置いていました。
→どちらでやっても動きました。
2. >void upper(char *moji) /* <--引数をポインタに */
というところ、私は
void upper(char moji[ ])
のままにしていたんだけれど、これも(どちらでも)ちゃんと動きました。
うーん、どうも文字配列とポインタの関係、その扱い方がまだよくわかってないみたいだなぁ。(^^;
「*moji」も「moji[ ]」も同じ意味(「moji」であらわされる文字配列の先頭アドレス)???
それとも全然違うの?
うむむむむむ。
>main()
>{
> char a[80];
>
> scanf( "%s",a ); /* aに&はいらんと思う */
> upper( a );
> printf( "%s\n",a );
>}
>
>void upper(char *moji) /* <--引数をポインタに */
>{
> int i=0;
>
> while(moji[i]){ /* <--ここちょっと気に食わんけどまぁいいや */
これは、
while(moji[i]!=0){
の方がいいってこと?
NH氏のコメント
> 実際のところ、「キャラクタ型配列のポインタ」ってのは、「キャラクタ型変
>数」ではないの?
ちがう。
「キャラクタ型配列(or変数)のポインタ」ってのはその配列の先頭アドレス(変数の場合はその変数のアドレス)
「キャラクタ型変数」ってのはそのアドレスに格納されてる数字。
※これは質問の意味を取り違えています。
→「追伸」へ
> ちゅうのは、「ポインタ」というのは、文字列の最初の文字のアドレスだと認識
>しているのだけれど、とすればそれは普通の数字ではないかと思えるのです……。
そうだよ。
> 結局、upper 関数は、return(moji)で配列のポインタを返しはするけれど、その
>直後にその(ポインタが示す場所にある)データを破棄しちゃうので、ああいうぐ
>ちょぐちょのデータが出てきてしまうってことなのかな?
そうだと思います。
> しかしこれはこれで、どうしてその配列(ここではa[ ])が、グローバル変数でな
>くていいのか、ってことだなあ。
> いや実際、main 関数内で宣言しても大丈夫だもんねえ(やってみたら)。
>1.
> char a[80];
> を、mainの前に置いていました。
>
>→どちらでやっても動きました。
そりゃ当然でしょう。
a[ ]はmainが終了するまで有効なんだから。
そういう意味では、グローバル変数もmain内のローカル変数も一緒。
違う点は、グローバル変数にすると、upper関数内で直接a[ ]という変数を使って値を操作できる点。
> 「*moji」も「moji[ ]」も同じ意味(「moji」であらわされる文字配列の先頭ア
>ドレス)???
> それとも全然違うの?
「*moji」はポインタ、「moji[ ]」は配列の引数ですよ、という意味。
で、配列を引数にしても、配列の各要素に入っている値がすべて引き渡されるわけではなく、結局配列の先頭アドレスが引き渡されるんよ。
だから、upper関数に引き渡す値は、君の言うとおり、
どっちでも「moji」であらわされる文字配列の先頭アドレス
になります。
>> while(moji[i]){ /* <--ここちょっと気に食わんけどまぁいいや */
>
> これは、
>
>while(moji[i]!=0){
>
>の方がいいってこと?
mojiに0が入ってなかったら無限ループになるし、a[ ]で確保した領域をオーバーしてしまって動作不良になるので、
while(moji[i] && i<80)
にした方がいいかなと。
けど、こんな小さいプログラムやったら別に問題ないとは思うが。
追伸:
質問の意味を勘違いしてたかも。
> 実際のところ、「キャラクタ型配列のポインタ」ってのは、「キャラクタ型変
>数」ではないの?
>
> ちゅうのは、「ポインタ」というのは、文字列の最初の文字のアドレスだと認識
>しているのだけれど、とすればそれは普通の数字ではないかと思えるのです……。
たしかに「キャラクタ型配列(or変数)のポインタ」ってのは数字。
「キャラクタ型変数」ってのも数字。
けど、数字の意味が違うってのは前のメールに書いた通り。
で、君は、
char upper・・・
と宣言している。
これは、upper関数は「キャラクタ型変数」が返り値ですよ、という意味。
ほんで、
char moji[ ]
と宣言し、
return(moji)
と書いている。
「moji」と書くと「moji[ ]という変数の先頭アドレス(ポインタ)」という意味になる。
コンパイラは宣言している返り値と意味が違うよって警告を出してくれるというわけ。
けど、君の言うとおり、数字という意味では間違いないから、エラーではなく、なんらかの動作はする実行ファイルを作ってくれるのだ。
わかった?
私のコメント
>で、君は、
>
>char upper・・・
>
>と宣言している。
>これは、upper関数は「キャラクタ型変数」が返り値ですよ、という意味。
>ほんで、
>
>char moji[ ]
>
>と宣言し、
>
>return(moji)
>
>と書いている。
>「moji」とかくと「moji[ ]という変数の先頭アドレス(ポインタ)」という
>意味になる。
>コンパイラは宣言している返り値と意味が違うよって警告を出してくれると
>いうわけ。
なるほどー。↓これの通り、
>「キャラクタ型配列(or変数)のポインタ」ってのはその配列の先頭アドレス
>(変数の場合はその変数のアドレス)
>「キャラクタ型変数」ってのはそのアドレスに格納されてる数字。
確かに2つとも「数字」には違いないけれど(=だからコンパイルはできる)、私が宣言しているのは「変数の値」であって、にも関わらず返り値は「その変数の先頭アドレス」になっているというわけですね。
てことは、こういう返り値が欲しい場合、どういうやり方をすればいいんだろう?
あるいは前のメールで教えてくれたとおり、返り値を返さないvoid関数で、直接(ポインタで示された)アドレスに対して操作するべきなのだろうか? (って、そうなんだろうな。「一般的」って書いてくれてたし。(^^))
>けど、君の言うとおり、数字という意味では間違いないから、エラーでは
>なく、なんらかの動作はする実行ファイルを作ってくれるのだ。
>わかった?
わかった! ……つもり。(^^;;
勉強になるよ。
>> char a[80];
>> を、mainの前に置いていました。
>>
>>→どちらでやっても動きました。
>
>そりゃ当然でしょう。
>a[ ]はmainが終了するまで有効なんだから。
>そういう意味では、グローバル変数もmain内のローカル変数も一緒。
>違う点は、グローバル変数にすると、upper関数内で直接a[ ]という変数を使
>って値を操作できる点。
これもなるほど、ですね。
moji[ ]の方は、あくまでサブルーチン……じゃなかった、関数内の「仮変数」だからダメなんですね。
>> > while(moji[i]){ /* <--ここちょっと気に食わんけどまぁいいや
> */
>>
>> これは、
>>
>>while(moji[i]!=0){
>>
>>の方がいいってこと?
>
>mojiに0が入ってなかったら無限ループになるし、a[ ]で確保した領域をオー
>バーしてしまって動作不良になるので、
>
>while(moji[i] && i<80)
>
>にした方がいいかなと。
>けど、こんな小さいプログラムやったら別に問題ないとは思うが。
なるほどなるほど。
文字配列の最後にはには必ず0が入るとテキストに書いてあったのでこうやったんだけれど、確かに何かの間違い(?)で0がなかった場合、トラブルが起こりそう。
安全サイドで考えたプログラムをすべきってことね。
NH氏のコメント
> 確かに2つとも「数字」には違いないけれど(=だからコンパイルはできる)、
>私が宣言しているのは「変数の値」であって、にも関わらず返り値は「その変数の
>先頭アドレス」になっているというわけですね。
そう。
追加すると、
「*」つけて宣言するとその変数はポインタ型の変数になります。
char *moji;
だと、mojiという変数は「キャラクタ型変数のポインタ型」 です。
で、キャラクタ型変数
char moji;
と何が違うか、ということですが、例えばmojiに100という値が入っていた場合、
char *moji;
moji=moji+1;
アドレス100にあるmojiの1つ隣のchar型変数のアドレスになります。
#mojiがどんな値になるかは、動作システムによって変わる。
#102が普通かなぁ。
#ためしにprintfして確認してみたら?
で、
char moji;
moji=moji+1;
の場合はmojiには当然101が入ります。
ちなみに
char moji[80];
と宣言すると、moji はキャラクタ型変数のポインタ型と定義されます。
moji+1;
とすると、moji[1]のアドレス=&moji[1] を示す値になります。
数字という意味では同じですが、コンパイラはこの型の違いを見分けて警告を出します。
> てことは、こういう返り値が欲しい場合、どういうやり方をすればいいんだろう?
配列の値すべてを返すことはできないでしょう。
少なくとも俺はやり方を知らん。
文字列のポインタを返したいなら、グローバル変数宣言して、関数内でそこに値を入れて、そのグローバル変数のポインタを返すしかないね。
#ローカル変数をstatic宣言するといけるのかなぁ。
#その辺はしらん。
けど、あんまり意味がない。
だから、
> あるいは前のメールで教えてくれたとおり、返り値を返さないvoid関数で、直接
>アドレスに対して操作するべきなのだろうか? (って、そうなんだろうな。「一
>般的」って書いてくれてたし。(^^))
これが一般的なのです。
> これもなるほど、ですね。
> moji[ ]の方は、あくまでサブルーチン……じゃなかった、関数内の「仮変数」だ
>からダメなんですね。
・・・・
・・・・
> なるほどなるほど。
> 文字配列の最後にはには必ず0が入るとテキストに書いてあったのでこうやったん
>だけれど、確かに何かの間違い(?)で0がなかった場合、トラブルが起こりそう。
> 安全サイドで考えたプログラムをすべきってことね。
そゆこと。
私のコメント
>追加すると、
>*つけて宣言するとその変数はポインタ型の変数になります。
>
>char *moji;
>
>だと、mojiという変数は「キャラクタ型変数のポインタ型」 です。
つまり、「moji」はあくまで「アドレスを示す数値」なわけですよね。
てことは、この時点では、char型の変数を入れるべき場所の先頭アドレスmojiを宣言したに過ぎないってことなんだよねえ。
ここではでも、mojiで指し示す変数がいくつあるか(文字配列であるかどうか)はわからないんだよねえ。
つまり、実際に変数が代入されないと、mojiで示されるアドレスから始まるデータは(配列なのかどうかは)わからないってことになるんだろうか?
何か勘違いしてるのかな?
注)ここでは、あらかじめ文字配列であるかわからない場合、メモリ領域がどれだけ必要なのかは、実際に変数が代入されるまでわからないのではないのか、とすればメモリ領域がちゃんと確保されないことがあるんじゃないのか、ということを聞いてます。
>で、キャラクタ型変数
>
>char moji;
>
>と何が違うか、ということですが、例えばmojiに100という値が
>入っていたばあい、
つまり、「100」というアドレス、ですね。
>char *moji;
>moji=moji+1;
>
>アドレス100にあるmojiの1つ隣のchar型変数のアドレスになります。
>#mojiがどんな値になるかは、動作システムによって変わる。
>#102が普通かなぁ。
>#ためしにprintfして確認してみたら?
うーん、やってみてどうもよくわからなくなってきた。(^^;
もちょっと勉強してみます。(^^;;;
>で、
>
>char moji;
>moji=moji+1;
>
>の場合はmojiには当然101が入ります。
>
>ちなみに
>
>char moji[80];
>
>と宣言すると、moji はキャラクタ型変数のポインタ型と
>定義されます。
>
>moji+1;
>
>とすると、moji[1]のアドレス=&moji[1] を示す値になります。
>数字という意味では同じですが、コンパイラはこの型の
>違いを見分けて警告を出します。
ここはわかった気がします。
>> てことは、こういう返り値が欲しい場合、どういうやり方をすればいいん
>>だろう?
>
>配列の値すべてを返すことはできないでしょう。
>少なくとも俺はやり方を知らん。
>文字列のポインタを返したいなら、グローバル変数宣言して、関数内でそこ
>に>値いれて、そのグローバル変数のポインタを返すしかないね。
>#ローカル変数をstatic宣言するといけるのかなぁ。
>#その辺はしらん。
>けど、あんまり意味がない。
>だから、
>
>> あるいは前のメールで教えてくれたとおり、返り値を返さないvoid関数で、
>>直接アドレスに対して操作するべきなのだろうか? (って、そうなんだろうな。
>>「一般的」って書いてくれてたし。(^^))
>
>これが一般的なのです。
こういう答えはほんとに勉強になります。
NH氏のコメント
> つまり、「moji」はあくまで「アドレスを示す数値」なわけですよね。
> てことは、この時点では、char型の変数を入れるべき場所の先頭アドレスmojiを
>宣言したに過ぎないってことなんだよねえ。
> ここではでも、mojiで指し示す変数がいくつあるか(文字配列であるかどうか)
>はわからないんだよねえ。
うむ。そゆこと。
> つまり、実際に変数が代入されないと、mojiで示されるアドレスから始まるデー
>タは(配列なのかどうかは)わからないってことになるんだろうか?
> 何か勘違いしてるのかな?
あってるよ。
だから、実際にポインタを使う時は、データを入れる領域を何らかの手段で確保して、その先頭アドレスをポインタに代入して使います。
例えば、
char moji[80]; /*データを入れる領域確保*/
char *a;
a= moji; /*先頭アドレスをポインタに代入*/
ってなかんじで、aを使う。
これだけやったら、「なんでaつかうねん、moji使えば良いやないか」と思いますが、プログラム書いてるとこうした方が書きやすい・使いやすい場合が出てきます。
あと、システムのH/Wに依存するけど、例えばデータアクセスの早いメモリ領域(当然そのメモリアドレスは既知)があって、宣言したポインタにそのメモリアドレスを代入して、計算頻度の高い変数をデータアクセスの早いメモリ領域に配置して使うって事が多いかな。
> うーん、やってみてどうもよくわからなくなってきた。(^^;
以下のようにしてみたら。 →次のメールにて
どんな結果が出たか教えて。
さらに、charをintにしてみてどうなるか比較すると面白いかも。
私のコメント
>> つまり、実際に変数が代入されないと、mojiで示されるアドレスから始ま
>>るデータは(配列なのかどうかは)わからないってことになるんだろうか?
>> 何か勘違いしてるのかな?
>
>あってるよ。
>だから、実際にポインタを使う時は、データを入れる領域を何らかの
>手段で確保して、その先頭アドレスをポインタに代入して使います。
>例えば、
>
>char moji[80]; /*データを入れる領域確保*/
>char *a;
>
>a= moji; /*先頭アドレスをポインタに代入*/
>
>ってなかんじで、aを使う。
なるほどなるほど。
こういっていただけるととてもわかりやすい。
>これだけやったら、「なんでaつかうねん、moji使えば良いやないか」
>と思いますが、プログラム書いてるとこうした方が書きやすい・使いや
>すい場合が出てきます。
少なくとも私には、これをしないとかなりややこしいような気がします。(^^)
>あと、システムのH/Wに依存するけど、例えばデータアクセスの早いメ
>モリ領域(当然そのメモリアドレスは既知)があって、宣言したポイン
>タにそのメモリアドレスを代入して、計算頻度の高い変数をデータアク
>セスの早いメモリ領域に配置して使うって事が多いかな。
ふむふむ。
>> うーん、やってみてどうもよくわからなくなってきた。(^^;
>
>以下のようにしてみたら。
>どんな結果が出たか教えて。
>さらに、charをintにしてみてどうなるか比較すると面白いかも。
やってみました。
間に3つ
printf("%d\n",a);
printf("%d\n",*a);
printf("%d\n",&a);
これを追加した形で。
# include <stdio.h> main() { char a[10]; char *b; a[0]= 10; a[1]= 20; b= a; printf("%d\n",b); printf("%d\n",*b); printf("%d\n",&b); printf("%d\n",a); printf("%d\n",*a); printf("%d\n",&a); b= b+1; printf("%d\n",b); printf("%d\n",*b); printf("%d\n",&b); }結果:
3920
10
3918
3920
10
3920
3921
20
3918
こんな感じになりました。
これは↓こういう理解でいいでしょうか?
# include <stdio.h> main( )
{char a[10];
char *b;
a[0]= 10;
a[1]= 20;
b= a;
結果 printf("%d\n",b); 3920 aの先頭アドレス=a[0]のアドレス。 printf("%d\n",*b); 10 「b」で表されるアドレス(=3920)に格納されている値。 printf("%d\n",&b); 3918 「b」そのもののアドレス。
「*b」「&b」といった表現は、char *b;でポインタ型変数?として宣言していて初めて可能になるということですね。
printf("%d\n",a); 3920 文字配列aの値ではなくaの先頭アドレス。 printf("%d\n",*a); 10 「a」で表されるアドレス(=3920)に格納されている値。 printf("%d\n",&a); 3920 「a」そのもののアドレス。 b= b+1; printf("%d\n",b); 3921 bの値はあくまでaのアドレス3920なので単なる数値計算になる、と。
←ここがマチガイ。単なる数値計算じゃない。こちらへ。printf("%d\n",*b); 20 3921というアドレス(=a[1]のアドレス) に格納されている値。 printf("%d\n",&b); 3918 bのアドレス自体は変わってない、と。 }
こんな感じでしょうか。
b a[0] a[1] a[2] ←名前 3920 10 20 ←値 └─┘ └─┘ └─┘ └─┘ └─┘ 3918 3919 3920 3921 3922 ←アドレス a charをintにした場合の結果:
3910
10
3908
3910
10
3910
3912
20
3908
あれれれのれ?
intで宣言すると、bの値が3910なのに、b=b+1だと、3911にならずに3912になってる……。
むむむ。
てことは……。
bという値はポインタ型なので、普通の数値としてではなく、あくまでアドレスの配置での計算をすることになる、ということなんでしょうか。
で、charで宣言すると1バイト、intで宣言すると2バイト単位となるから、ポインタ型の数値を計算するときも、そういう形で動く?
んんん??
とすると……うぎゃーーーーーーー!!!! わからんーーーーーーーー!
まいったな。(^^;
NH氏のコメント
こんな感じでしょうか。
> b a[0] a[1] a[2] ←名前 > 3920 10 20 ←値 > └─┘ └─┘ └─┘ └─┘ └─┘ > 3918 3919 3920 3921 3922 ←アドレス > a
そう。
charの場合の理解は完璧。
> あれれれのれ?
> intで宣言すると、bの値が3910なのに、b=b+1だと、3911にならずに3912になっ
>てる……。
> むむむ。
> てことは……。
>
> bという値はポインタ型なので、普通の数値としてではなく、あくまでアドレスの
>配置での計算をすることになる、ということなんでしょうか。
> で、charで宣言すると1バイト、intで宣言すると2バイト単位となるから、ポ
>インタ型の数値を計算するときも、そういう形で動く?
> んんん??
> とすると……うぎゃーーーーーーー!!!! わからんーーーーーーーー!
> まいったな。(^^;
はっはっは。
やっぱりはまったか。
なんか出題者の意図どおりにはまって面白いな。
これが分かれば、ポインタの理解はほぼ完璧やねんけど。
でも大体あってるよ。
要するにポインタってのが、メモリアドレスを操作するための変数ってのがみそ。
だから
b=b+1
ってのは、bが「キャラクタ変数のポインタ」(要するにchar *b)の場合、
「1こ隣のキャラクタ変数のアドレスをbに入れる」
という意味になる。
キャラクタ変数の大きさは1バイトだからbは1だけ大きくなる。
で、bが「short型変数のポインタ」(要するにshort *b。intってのはシステムによって大きさが変わる。君のシステムではshort=int)の場合、
「1こ隣のshort変数のアドレスをbに入れる」
という意味だから、short変数の大きさ分、すなわち2バイト分 bは大きくなる。
当然、longで宣言の場合、b=b+1とするとポインタの値は4ずつ大きくなるよ。
だから、a,bがcharでもshort(int)でもlongでもfloatでもdoubleでも、(a,bの型は同じじゃないとだめよ)
*(b+1) は a[1] の値を指す (b+1は&a[1]に等しいとも言う)
*(b+2) は a[2] の値を指す (b+2は&a[2]に等しいとも言う)
.....
*(b+i) は a[i] の値を指す (b+iは&a[i]に等しいとも言う)
ようになるわけだ。
ただし、ポインタの値の変化量は動作システムによって変わる。
たとえば、32bit(4バイト)単位でしかメモリアクセスできないシステムとかだと、double以外は、b=b+1をするとすべて4バイト増える。
このへんは、そのシステムに対応したコンパイラが勝手に判断してくれるのよ。
だから、ポインタを使えば上述のように、そんなことは気にせずにアドレス操作をするプログラミングができるわけだ。
#普通はポインタの中の値がいくつか、なんて気にしないからね。
これが、ポインタを使う利点。
以上、ポインタのお勉強でした。
私のコメント
>これが分かれば、ポインタの理解はほぼ完璧やねんけど。
>でも大体あってるよ。
>要するにポインタってのが、メモリアドレスを操作するための
>変数ってのがみそ。
>だから
>
>b=b+1
>
>ってのは、bが「キャラクタ変数のポインタ」(要するにchar *b)
>の場合、
>
>「1こ隣のキャラクタ変数のアドレスをbに入れる」
>
>という意味になる。
だいたいわかった気がする。
てことは、前の例だと
>charをintにした場合。
>
>3910
>10
>3908
>3910
>10
>3910
>3912
>20
>3908
これは、
b a[0] a[1] a[2] ←名前 3910 10 20 ←値 └─┘ └─┘ └─┘ └─┘ └─┘ 3906 3908 3910 3912 3914 ←アドレス charの場合は1単位だったけれど、2単位 a
と考えればいいわけですね。
>キャラクタ変数の大きさは1バイトだからbは1だけ大きくなる。
>で、bが「short型変数のポインタ」(要するにshort *b。intっての
>はシステムによって大きさが変わる。君のシステムではshort=int)
>の場合、
>
>「1こ隣のshort変数のアドレスをbに入れる」
>
>という意味だから、short変数の大きさ分、すなわち2バイト分 bは
>大きくなる。
>当然、longで宣言の場合、b=b+1とするとポインタの値は4ずつ大き
>くなるよ。
ということは、結局のところ、「b=b+1」という式(というのかわからないけれど)というのは、普通の演算として考えてはいけないわけですね。
つまり、bが表すアドレスを表すための数値(ここでいう3910とか)と「1」という数値は単位が違う、と。
なるほど。だからこそのポインタ型宣言なわけだ。
こういうわけわからん(^^)動き方をする変数だということは、確かに宣言しなきゃいけないなあ。
>だから、a,bがcharでもshort(int)でもlongでもfloatでもdoubleでも、
>(a,bの型は同じじゃないとだめよ)
>
>*(b+1) は a[1] の値を指す (b+1は&a[1]に等しいとも言う)
>*(b+2) は a[2] の値を指す (b+2は&a[2]に等しいとも言う)
>.....
>*(b+i) は a[i] の値を指す (b+iは&a[i]に等しいとも言う)
>
>ようになるわけだ。
>
>ただし、ポインタの値の変化量は動作システムによって変わる。
>たとえば、32bit(4バイト)単位でしかメモリアクセスできないシス
>テムとかだと、double以外は、b=b+1をするとすべて4バイト増える。
>このへんは、そのシステムに対応したコンパイラが勝手に判断してく
>れるのよ。
>だから、ポインタを使えば上述のように、そんなことは気にせずにアド
>レス操作をするプログラミングができるわけだ。
なるほど。
てことは、ポインタを使った方が移植性の高いプログラムを作ることができるってことかな?
>#普通はポインタの中の値がいくつか、なんて気にしないからね。
>これが、ポインタを使う利点。
>
>以上、ポインタのお勉強でした。
わかったような気がするぞ!\(^O^)/
いや、解れそうな気がする。(^^)
あといくつか自分で確認のためのプログラムを作ったら解れそうな気がする。
なんとか頑張ってみるよ。
ありがとう!
NH氏のコメント
>
> b a[0] a[1] a[2] ←名前 > 3910 10 20 ←値 > └─┘ └─┘ └─┘ └─┘ └─┘ > 3906 3908 3910 3912 3914 ←アドレス charの場合は1単位だったけれど、2単位 > a
> と考えればいいわけですね。
・・・
> ということは、結局のところ、「b=b+1」という式(というのかわからないけれ
>ど)というのは、普通の演算として考えてはいけないわけですね。
> つまり、bが表すアドレスを表すための数値(ここでいう3910とか)と「1」とい
>う数値は単位が違う、と。
> なるほど。だからこそのポインタ型宣言なわけだ。
> こういうわけわからん(^^)動き方をする変数だということは、確かに宣言しな
>きゃいけないなあ。
うむ。
そゆこと。
> なるほど。
> てことは、ポインタを使った方が移植性の高いプログラムを作ることができるっ
>てことかな?
移植性という意味ではポインタ使おうが配列使おうが一緒だけどね。
さっきも書いたけど、システムの違いや変数の型の違いを気にせずにアドレス操作のプログラムができるってことが大事かな。
> わかったような気がするぞ!\(^O^)/
>
> いや、解れそうな気がする。(^^)
> あといくつか自分で確認のためのプログラムを作ったら解れそうな気がする。
>
> なんとか頑張ってみるよ。
>
> ありがとう!
大体分かったみたいやね。
すばらしい。
仕事サボってメール書いたかいがあったというものだ。
がんばってね。
私のコメント&できあがったソース
で、最終的にできあがったソースが、これ。
# include <stdio.h> # include <string.h> void upper(char *moji); /* プロトタイプ宣言。返り値なし */ main( ) { char a[80]; char *b; b=a; scanf("%s",&a); upper(b); /* 関数に場所だけ教えて仕事をしてもらう */ printf("%s",*b); /* で、その場所にある値を表示する */ } void upper(char *moji) { int i=0; while(moji[i]){ if(moji[i]>=95 && moji[i]<=122) moji[i]=moji[i]-32; i++; } printf("%s",moji); }結果:
qwerty ←自分で入力
QWERTY