🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

3回答

2808閲覧

ポインタを使った配列の入れ替え

Bruno_5239

総合スコア23

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

1クリップ

投稿2019/10/04 11:45

現在プログラミングの学習でポインタを使った配列の入れ替えをしようと思っています。しかしどうやっても文字列がちゃんと出力されません。散々あらゆるサイトで出尽くしていますがその方法を理解して記述したつもりでもうまくいきません。どなたか問題点をご指摘お願いします。

c

1#include <stdio.h> 2 3int main() 4{ 5 char v[11] = "HelloWorld"; 6 char* p; 7 char* q; 8 int v_size; 9 int i = 0; 10 11 v_size = sizeof v / sizeof v[0]; 12 p = v; 13 q = v + v_size - 1; 14 15 16 printf("v = %s\n", v); 17 printf("v_size = %d\n", v_size); 18 19 while(p < q) 20 { 21 char temp; 22 23 temp = *p; 24 *p = *q; 25 *q = temp; 26 27 p++; 28 q--; 29 i++; 30 } 31 printf("v = %s\n", v); 32 33 return 0; 34}

イメージ説明

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

ヌル文字が先頭に移動しているからでしょう。

C

1// q = v + v_size - 1; 2q = v + v_size - 2;

また文字列長を取得する場合は、一般にsizeofよりstrlenの方が適切だと考えます。
これならヌル文字を意識せずに済みます。

C

1v_size = strlen(v); 2p = v; 3q = v + v_size - 1;

投稿2019/10/04 11:49

編集2019/10/04 11:51
LouiS0616

総合スコア35668

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Bruno_5239

2019/10/04 11:57

試しに q = v + v_size - 2; にしてみたところうまくいきました!!ヌル文字が先頭に移動しているのはなぜなのか教えていただけませんか? そしてさきほどstrlen()を入力したところ"Implicit conversion loses integer precision: 'unsigned long' to 'int'"と表示されたのですがstrlen()メソッドはlong型を入れるメソッドなのですか?
LouiS0616

2019/10/04 12:00

> ヌル文字が先頭に移動しているのはなぜなのか教えていただけませんか? 配列は11要素ですが、その [11-1] 番目の要素がヌル文字だからです。 ヌル文字ごとひっくり返っているので、 \0dlroWolleH という文字列ができています。 > strlen()メソッドはlong型を入れるメソッドなのですか? long型の数値を返す関数です。(C言語ではメソッドと呼ばれる機能がありません) v_sizeの型をlongにすれば解消するでしょう。
kazuma-s

2019/10/06 10:56

q = v + strlen(v) - 1; にもひとつ問題があります。 char v[] = "HelloWorld"; なら良いのですが、 char v[] = ""; だった場合、q が v[-1] を指すことになり、これは未定義動作です。
LouiS0616

2019/10/06 11:29

@kazuma-s さん なるほど、確かにそのとおりですね。空文字列の場合別の処理を噛ませてやる必要がありそうです。 大変有益なコメントありがとうございます。
SHOMI

2019/10/07 02:57

>char v[] = ""; だった場合、q が v[-1] を指すことになり その場合ループ内には入らないので境界外アクセスも発生せず、問題ないですよね?
LouiS0616

2019/10/07 06:40 編集

@SHOMI さん コメントありがとうございます。 文字列の長さが0のとき、char *q = v + strlen(v) - 1 の時点で未定義動作を踏みます。 > Note that executing p-1 when p points at the first element of an array is undefined behavior and may fail on some platforms. 引用元:https://en.cppreference.com/w/c/language/operator_arithmetic
SHOMI

2019/10/07 06:50

LouiS0616さん ご指摘ありがとうございます。末尾+1までは問題ないので、先頭-1もポインタ演算だけなら問題ないと思いこんでいました…
LouiS0616

2019/10/07 07:29

私自身うろ覚えだったところ、改めて調べ直す良い機会になりました。 今後も何か気になる点がありましたら、コメントよろしくお願いします。
guest

0

質問への回答でなくて申し訳ありませんが、
ポインタを使った文字列の反転です。

C

1#include <stdio.h> 2 3char *revstr(char *s, char *p) 4{ 5 char c = *p; 6 return c ? p = revstr(s, p + 1), *p = c, p + 1 : s; 7} 8 9int main(void) 10{ 11 char v[11] = "HelloWorld"; 12 printf("v = %s\n", v); 13 revstr(v, v); 14 printf("v = %s\n", v); 15}

追記

3項演算子 ?: を使わずに書くと次のようになります。

C

1#include <stdio.h> 2 3char *revstr(char *s, char *p) 4{ 5 char c = *p; 6 if (c != '\0') { 7 p = revstr(s, p + 1); 8 *p = c; 9 return p + 1; 10 } 11 else 12 return s; 13} 14 15int main(void) 16{ 17 char v[11] = "HelloWorld"; 18 printf("v = %s\n", v); 19 revstr(v, v); 20 printf("v = %s\n", v); 21}

s は変化せず、常に main の v[0] を指すポインタです。

p は、関数 revstr の再帰呼出しにより、配列 v の要素を順番に指していきます。

最初に呼び出された revstr の c には 'H' が入ります。
2番目に呼び出された revstr の c には 'e' が入ります。
...
9番目に呼び出された revstr の c には 'l' が入ります。
10番目に呼び出された revstr の c には 'd' が入ります。
11番目に呼び出された revstr の c には '\0' が入ります。

ここで c が '\0' なので、再帰呼出しはせずに、s を返し、
10番目に呼び出された revstr に戻ります。
そこで p には s の値、すなわち v[0] のアドレスが入るので、
*p = c; により、v[0] に 'd' が入ります。
そして、p + 1、すなわち v[1] のアドレスを返します。

9番目に呼び出された revstr に戻ると、p に v[1] のアドレスが入ります。
*p = c; により、v[1] に 'l' が入ります。
そして、p + 1、すなわち v[1] へのアドレスを返します。

これを繰り返して、最初に呼び出された revstr に戻った時
p には v[9] のアドレスが入ります。
*p = c; により、v[9] に 'H' が入ります。

そして、main に戻ります。

追記その2
char v[] = ""; でも未定義動作ではないプログラム

C

1#include <stdio.h> // printf 2#include <string.h> // strlen 3 4int main(void) 5{ 6 char v[] = "HelloWorld"; 7 char *p = v; 8 char *q = v + strlen(v); 9 printf("v = %s\n", v); 10 while (p + 1 < q) { 11 char temp = *p; 12 *p++ = *--q; 13 *q = temp; 14 } 15 printf("v = %s\n", v); 16}

投稿2019/10/04 21:46

編集2019/10/06 11:21
kazuma-s

総合スコア8224

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Bruno_5239

2019/10/05 02:50 編集

丁寧にコードありがとうございます! ですが参考演算やらメソッドの再帰呼び出しやら初心者の自分はまだちょっと慣れてない書き方が多くてなんでこれで正常に動くのかちょっと理解できないです!笑 お手数でなければ解説もお願いしたいです ありがとうございます!まだおそらく完全には理解できていませんが、解説のおかげで少し動きはわかった気がします笑 再帰呼び出しが使えるとコードがスマートになりますね!こちらも参考にして勉強していきます。ありがとうございました????
rubato6809

2019/10/05 13:49 編集

良いコードとは思えない。ひとりよがりな感じ。 まず再帰が分かりにくい。再帰らしくない、というか。三項演算子に加えてこんなところにコンマ演算子まで使いますか。
guest

0

v[10]は、'\0'ですよ

投稿2019/10/04 11:55

y_waiwai

総合スコア88040

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Bruno_5239

2019/10/04 11:58

本当ですね!!!! 今の今まで気がつきませんでした!!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問