C言語におけるポインタを使った文字列入れ替え

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ted

C言語におけるポインタを使った文字列入れ替え

#1

投稿記事 by ted » 8年前

C言語の参考書に表記されていた練習問題で、入力した文字列を反転させる(abcd→dsba)関数をつくり、プログラムを組めというものなのですが、そこに記されていた模範解答の関数revの動きが理解できませんでした。どなたか入力した文字列が正常に入れ替えを果たすまでの解説をしていただけないでしょうか。

コード:

/* 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/]

アバター
bitter_fox
記事: 607
登録日時: 9年前
住所: 大阪府

Re: C言語におけるポインタを使った文字列入れ替え

#2

投稿記事 by bitter_fox » 8年前

ted さんが書きました:C言語の参考書に表記されていた練習問題で、入力した文字列を反転させる(abcd→dsba)関数をつくり、プログラムを組めというものなのですが、そこに記されていた模範解答の関数revの動きが理解できませんでした。どなたか入力した文字列が正常に入れ替えを果たすまでの解説をしていただけないでしょうか。

コード:

void rev(char *str)
{
	char c, *p;

	p = str + strlen(str) - 1; // (1)

	while (str < p) { // (2)
		c = *str; // (3)=======
		*str++ = *p;
		*p-- = c; // =======(3)
	}
	return;
}

「abcdefgh」を渡してみた場合を視覚的に考えてみます。
(1)の段階では次のようになっています。

コード:

+-------------------------------+
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
+-------------------------------+
| a | b | c | d | e | f | j | h | // 前
+-------------------------------+
|str|   |   |   |   |   |   | p | // (1)
+-------------------------------+
(2)というのはpがstrの右側に居る間という意味なので(2)が偽になるまで実行してみましょう。

コード:

+-------------------------------+
| 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 | // 後
+-------------------------------+
ちなみに、(3)というのは入れ替えの典型的なアルゴリズムです。

コード:

+-----------+
|str| c | p |
+-----------+
| a |   | z | // 前
+-----------+
| a | a | z | // c = *str
+-----------+
| z | a | z | // *str++ = *p
+-----------+
| z | a | a | // *p-- = c
+-----------+
最後に編集したユーザー bitter_fox on 2011年10月24日(月) 02:44 [ 編集 1 回目 ]

hss12
記事: 40
登録日時: 8年前

Re: C言語におけるポインタを使った文字列入れ替え

#3

投稿記事 by hss12 » 8年前

関数の中身のみ説明しますが

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)++; と書く必要があります。

ted

Re: C言語におけるポインタを使った文字列入れ替え

#4

投稿記事 by ted » 8年前

どうやら++や--の処理の順番が理解出来ていなかったようです。
bitter_foxさん、hss12さん解説ありがとうございました。
hss12 さんが書きました:ところで #include <string.h> は書かなくて大丈夫なのでしょうか。
これは私も思いましたがサンプルプログラムには書かれていませんでした。無しで実行しても正しく動作していました。それでも自分で書く場合には表記することにしますが何故動いたのでしょう。

閉鎖

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