/* program84.c */
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void rev(char *);
int main()
{
char string[64];
printf("文字列を入力してください --- ");
gets(string);
printf("入力された文字列は「%s」です\n", string);
rev(string);
printf("逆順に並び替えると「%s」になります\n", string);
return 0;
}
void rev(char *str)
{
char c, *p;
p = str + strlen(str) - 1;
while (str < p) {
c = *str;
*str++ = *p;
*p-- = c;
}
return;
}
[code/]
C言語におけるポインタを使った文字列入れ替え
C言語におけるポインタを使った文字列入れ替え
C言語の参考書に表記されていた練習問題で、入力した文字列を反転させる(abcd→dsba)関数をつくり、プログラムを組めというものなのですが、そこに記されていた模範解答の関数revの動きが理解できませんでした。どなたか入力した文字列が正常に入れ替えを果たすまでの解説をしていただけないでしょうか。
- bitter_fox
- 記事: 607
- 登録日時: 14年前
- 住所: 大阪府
Re: C言語におけるポインタを使った文字列入れ替え
「abcdefgh」を渡してみた場合を視覚的に考えてみます。ted さんが書きました:C言語の参考書に表記されていた練習問題で、入力した文字列を反転させる(abcd→dsba)関数をつくり、プログラムを組めというものなのですが、そこに記されていた模範解答の関数revの動きが理解できませんでした。どなたか入力した文字列が正常に入れ替えを果たすまでの解説をしていただけないでしょうか。
(1)の段階では次のようになっています。
+-------------------------------+
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
+-------------------------------+
| a | b | c | d | e | f | j | h | // 前
+-------------------------------+
|str| | | | | | | p | // (1)
+-------------------------------+
+-------------------------------+
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
+-------------------------------+
| a | b | c | d | e | f | j | h | // 前
+-------------------------------+
|str| | | | | | | p | // (2)は真
+-------------------------------+
| h | b | c | d | e | f | j | a | // (3)が実行された(ポインタの移動も同時に行われるのでstr, pが左右に一つずれる)
+-------------------------------+
| |str| | | | | p | | // (2)は真
+-------------------------------+
| h | j | c | d | e | f | b | a | // (3)が実行された(ポインタの移動も同時に行われるのでstr, pが左右に一つずれる)
+-------------------------------+
| | |str| | | p | | | // (2)は真
+-------------------------------+
| h | j | f | d | e | c | b | a | // (3)が実行された(ポインタの移動も同時に行われるのでstr, pが左右に一つずれる)
+-------------------------------+
| | | |str| p | | | | // (2)は真
+-------------------------------+
| h | j | f | e | d | c | b | a | // (3)が実行された(ポインタの移動も同時に行われるのでstr, pが左右に一つずれる)
+-------------------------------+
| | | | p |str| | | | // (2)は偽なので終了
+-------------------------------+
| h | j | f | e | d | c | b | a | // 後
+-------------------------------+
最後に編集したユーザー bitter_fox on 2011年10月24日(月) 02:44 [ 編集 1 回目 ]
Re: C言語におけるポインタを使った文字列入れ替え
関数の中身のみ説明しますが
char c, *p;
文字を一時的に入れておくcと、アドレスを入れておくためのpを宣言します。
p = str + strlen(str) - 1;
pにはアドレスが代入されるわけですが、「abcd」と入力された場合
strは文字「a」が入っているアドレスです。
strlen(str)は、この場合「abcd」の4文字なので4になります。
strlen(str) - 1なので3ということです。
strのアドレスに3を足しているので、つまりpには「d」の文字が入っているアドレスが入ります。
c = *str;
cにstrの中身つまり「a」の文字が代入されます。
*はアドレスの中身です。
最初のchar c, *p; のときの * はpにはアドレスを入れますよって意味ですが
代入するときは意味が違います。
*str++ = *p;
++が書いてありますが
*str = *p; をしたあと *str++; という意味です。
つまりstrには「a」が入っていたのですが、pつまり「d」が代入されるので
strは「d」になります。
文字列は「dbcd」になりました。
*str++; が行われるのでstrのアドレスは2つめの「b」の文字が入っているところになります。
*p-- = c;
これも同じく*p = c; のあと *p--; です。
pつまり4つめの「d」にcつまり「a」が代入されます。
文字列は「dbca」になりました。
*p--; が行われpのアドレスは3つめの「c」の文字が入っているところになります。
while (str < p) の繰り返しですが
strは2つめ、pは3つめなので繰り返します。
c = *str;
cにはstrの中身、つまりstrは今2つめのアドレスなので「b」の文字が代入されます。
*str++ = *p;
str、つまり2つめのところに、pつまり3つめの文字「c」が代入されます。
文字列は「dcca」になりました。
*str++なのでstrは3つめのアドレスになります。
*p-- = c;
p、つまり3つめのところにcつまり「b」の文字が代入されます。
文字列は「dcba」になりました。
*p--なのでpのアドレスは2つめになります。
またwhile (str < p)の繰り返しですが
strは3つめ、pは2つめなので繰り返し終わりです。
文字列は「dcba」になってますね。
これでprintfのstringが「dcba」になるわけです。
こんな説明で分かりますかね。
strとpが今どこのアドレスを指しているのか
cには何の文字が入っているのかを1行ごとに書いてみれば
そんなに難しくないと思います。
ところで #include <string.h> は書かなくて大丈夫なのでしょうか。
追記
*str++ = *p; を分けて書く場合
*str = *p;
str++;
で良いです。
*str++; でもアドレスがインクリメントされるので問題はないですが。
もしstrの中身をインクリメントしたい場合は(*str)++; と書く必要があります。
char c, *p;
文字を一時的に入れておくcと、アドレスを入れておくためのpを宣言します。
p = str + strlen(str) - 1;
pにはアドレスが代入されるわけですが、「abcd」と入力された場合
strは文字「a」が入っているアドレスです。
strlen(str)は、この場合「abcd」の4文字なので4になります。
strlen(str) - 1なので3ということです。
strのアドレスに3を足しているので、つまりpには「d」の文字が入っているアドレスが入ります。
c = *str;
cにstrの中身つまり「a」の文字が代入されます。
*はアドレスの中身です。
最初のchar c, *p; のときの * はpにはアドレスを入れますよって意味ですが
代入するときは意味が違います。
*str++ = *p;
++が書いてありますが
*str = *p; をしたあと *str++; という意味です。
つまりstrには「a」が入っていたのですが、pつまり「d」が代入されるので
strは「d」になります。
文字列は「dbcd」になりました。
*str++; が行われるのでstrのアドレスは2つめの「b」の文字が入っているところになります。
*p-- = c;
これも同じく*p = c; のあと *p--; です。
pつまり4つめの「d」にcつまり「a」が代入されます。
文字列は「dbca」になりました。
*p--; が行われpのアドレスは3つめの「c」の文字が入っているところになります。
while (str < p) の繰り返しですが
strは2つめ、pは3つめなので繰り返します。
c = *str;
cにはstrの中身、つまりstrは今2つめのアドレスなので「b」の文字が代入されます。
*str++ = *p;
str、つまり2つめのところに、pつまり3つめの文字「c」が代入されます。
文字列は「dcca」になりました。
*str++なのでstrは3つめのアドレスになります。
*p-- = c;
p、つまり3つめのところにcつまり「b」の文字が代入されます。
文字列は「dcba」になりました。
*p--なのでpのアドレスは2つめになります。
またwhile (str < p)の繰り返しですが
strは3つめ、pは2つめなので繰り返し終わりです。
文字列は「dcba」になってますね。
これでprintfのstringが「dcba」になるわけです。
こんな説明で分かりますかね。
strとpが今どこのアドレスを指しているのか
cには何の文字が入っているのかを1行ごとに書いてみれば
そんなに難しくないと思います。
ところで #include <string.h> は書かなくて大丈夫なのでしょうか。
追記
*str++ = *p; を分けて書く場合
*str = *p;
str++;
で良いです。
*str++; でもアドレスがインクリメントされるので問題はないですが。
もしstrの中身をインクリメントしたい場合は(*str)++; と書く必要があります。
Re: C言語におけるポインタを使った文字列入れ替え
どうやら++や--の処理の順番が理解出来ていなかったようです。
bitter_foxさん、hss12さん解説ありがとうございました。
bitter_foxさん、hss12さん解説ありがとうございました。
これは私も思いましたがサンプルプログラムには書かれていませんでした。無しで実行しても正しく動作していました。それでも自分で書く場合には表記することにしますが何故動いたのでしょう。hss12 さんが書きました:ところで #include <string.h> は書かなくて大丈夫なのでしょうか。