質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.48%
C

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

Q&A

解決済

3回答

16617閲覧

配列で文字列を扱い、その文字列の末尾を取得したいです

gregolio122

総合スコア14

C

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

0グッド

0クリップ

投稿2018/09/08 16:47

編集2018/09/08 16:51

実現したいことと前提

配列を使い、文字列の一番最後の文字を取り扱いたいです。
具体的には、文字列(標準入力で受け付ける)w1とw2の、両方の末尾の文字を取得したいです。
文字列の長さは最大10文字で、何文字入力されるかは不明とします。
###自分で考えてみた

C

1#include <stdio.h> 2int main(void){ 3 char w[10]; 4 int i,matubi[2]; 5//w1に関して 6for(i=0;w[i]!='\0'&&i<10;i++){ 7 scanf("%c",&w[i]); 8} 9i=i-2; 10matubi[0]=w[i]; 11//w2に関して 12for(i=0;w[i]!='\0'&&i<10;i++){ 13 scanf("%c",&w[i]); 14} 15i=i-2; 16matubi[1]=w[i]; 17//出力 18printf("w1の末尾は%cで、w2の末尾は%cです",matubi[0],matubi[1]); 19}

汎用性のないコードで申し訳ないです。
forの継続条件についてですが、単にi<10としただけでは、入力された文字列が10文字未満だった場合、
ループが10回まわることで、値が入っていないorおかしな数値の入っている(ここら辺の理解が不十分です)w1[9]やw2[9]の文字が取られてしまい、最終的に、末尾文字が出力されないor「?」が出力されると思い、継続条件に**w[i]!='\0'**を追加しました。
このように書けば、たとえば、配列変数w1[]に格納されている文字列がkakiであった場合、4文字目のiの次の配列要素は空文字(つまり'\0')であり(自信がないですが)、kakiはそれぞれ1つずつ順番に配列変数w[0]~w[3]に格納されているので、このループの最終回は、初めて空文字が配列に格納されるとき、すなわち、変数iが4の時となる。
そしてループが終了されるとi++により、さらにiが1足され、i=5となる。なので、ループを抜けたあとにiから2を引いてやる。
そうすることで、空文字の前の文字、つまり、その文字列の末尾文字が取得出来る、と考えたのですが…

###期待と実行結果
実際にこのコードを実行してみました。
標準入力にて
w1を__abcdefghij__(10文字)
w2を__aiueokaki__(8文字)
としたのですが、
その結果は

w1の末尾はeで、w2の末尾は です

というものでした。
この結果は不可解です。
自分の考え方に間違いがあるものだと思いますが、どこが間違っているかがわかりません。
どなたかご教示ください。

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

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

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

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

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

guest

回答3

0

ベストアンサー

C

1for(i=0;w[i]!='\0'&&i<10;i++){

これを

C

1for(i=0;i<10;i++){

このように変えると、後ろから二番目の文字が出力されました。
さらに

C

1i=i-2;

これを

C

1i=i-1;

このように変えると、最後の文字が出力されました。

w[i]!='\0' という条件がついていることにより、読み込みバッファ w[] の中に '\0' が含まれていると読み込みがそこでストップします。

###追記

なお、scanf("%c",&w[i]); は改行も読み込みの対象にします。
そして改行コードが入力された時の処理はなにもしていません。
したがって修正済みのコードにて、たとえば "abcd" "efghijklmnopqrstuv" が入力されたとき、w1 の末尾は i で w2 の末尾は s になります。
二行を改行コードで連結した文字列の最初の 10 バイトが "abcd\nefghi" で、次の 10 バイトが "jklmnopqrs" であるためです。

投稿2018/09/08 21:37

編集2018/09/08 21:49
Zuishin

総合スコア28660

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

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

Zuishin

2018/09/09 00:52

最後に \0 が無いので文字列としては使えません。
gregolio122

2018/09/09 01:08 編集

失礼しました。途中送信してしまいました。(削除リクエスト済) ループの継続条件を i<10のみに指定し、ループ抜け後の処理 i=i-2;をi=i-1;にしたところ w1がabcdefghijで w2がaiueokakiとした場合、確かにw1の末尾はjでw2の末尾がiと出力されました。 >読み込みバッファ w[] の中に '\0' が含まれていると ここでいう '\0'が含まれていると、というのは、何の数値も文字も入力されていない配列要素があると、という意味であっていますか? w1="ab" で w2="aiueokaki"であった場合、出力される末尾は w1が'a' で w2も'a'となるのですが、これは何故でしょう? w1の末尾が'a'と出力されるのは、2行を改行コードで連結した文字列の最初の10バイトが"ab\naiueoka"となり、その末尾が'a'だから、ですよね。(\nは\とnで2バイトではなく、\nで1バイトと扱うのですね?) しかし、w2の末尾も'a'となるのが何故なのか、わかりません。 お手数をおかけしますが、このことについて教えていただけませんか?
Zuishin

2018/09/09 01:13

> ここでいう '\0'が含まれている、というのは、何の数値も文字も入力されていない配列要素があると、という意味であっていますか? 何も入力されてない要素であっています。 その場合、その要素の値は不定です。 どんな値でもあり得ます。 たまたま \0 であることもあります。 > w1="ab" で w2="aiueokaki"であった場合、出力される末尾は w1が'a' で w2も'a'となるのですが、これは何故でしょう? うちでは w2 は i になります。(指摘した四カ所修正済み)
gregolio122

2018/09/09 04:56

お返事いただきましてありがとうございます。 話は戻るのですが、w1(標準入力は"ab"とします)に関するforの継続条件がi<10であった場合、ループ内のscanfによってw[0]にaが、w[1]がbが格納されると思いますが、w[2]〜w[9]はどうなるのでしょうか? ここがよくわからなくて…。 出力結果が私とは異なるようですね。 ```c #include <stdio.h> int main(void){ char w[10]; int i,matubi[2]; //w1に関して for(i=0;w[i]!='\0'&&i<10;i++){ scanf("%c",&w[i]); } i=i-2; matubi[0]=w[i]; //w2に関して for(i=0;w[i]!='\0'&&i<10;i++){ scanf("%c",&w[i]); } i=i-2; matubi[1]=w[i]; //出力 printf("w1の末尾は%cで、w2の末尾は%cです",matubi[0],matubi[1]); } ``` 上記コードを https://beta.atcoder.jp/contests/apg4b/custom_test こちらのサイトにて実行しました(言語はCのGCC5.4.1です)
Zuishin

2018/09/09 05:09

w[2] は \n ですね。 それ以降は何も入力されていないので不定です。 それと私の実行したのは指摘箇所を修正済みなのでこれとは違います。
Zuishin

2018/09/09 05:22

修正していない場合、不定の要素が入るのでどうなるかは環境によって違います。 同じパソコンで動かしても同じ結果になるとは限りません。
gregolio122

2018/09/09 05:26

\nというのは改行コードですね。 最初の標準入力と、次の入力(w2の文字列)との間に改行があるから、w1に関しw[2]が\nになるのだと思いつつも、 もし、入力された文字列がw1のみ("ab"の1行のみ)であった場合にもw[2]は\nになるのかが気になり、コードを少し書き換え(w2に関する部分をすべて削除しました)テスト実行、検証してみたのですが、その結果、@が出力されました。 これは、w[2]の値が不定で、たまたま@に該当する値が格納されていた、と考えるのが正しいでしょうか? それと、w[3]以降の出力をしてみたところ、結果は�と出力されました。 これも、値が不定であり、たまたま結果が�となるような値がそこに格納されていたというだけの話なのでしょうか? また、不定='\0'とは一概に言えませんか?
Zuishin

2018/09/09 05:30

たまたまそこに格納されていた値だと思います。 \0 に決まっているなら不定ではありません。 どんな値でも取り得るのが不定です。
gregolio122

2018/09/09 05:34

ありがとうございます。 お手数をおかけしていますが、だんだん解ってきました。 「不定ならば\0」ではなく、「不定の場合\0であることもある もちろんそれ以外の値が格納されている可能性もある」という捉え方が正しいでしょうか?
Zuishin

2018/09/09 05:36

その通りです。
gregolio122

2018/09/09 06:14

そういうことですよね、ありがとうございます。 話は最初に戻って…… strlenなどを用いず、私の書いたコードの中にある関数のみで w1もw2も◯文字だと決めてかかるのではなく、1~10文字以内の文字列として(w1とw2の文字数はユーザーからの入力次第です)、それらの末尾を出力する、というプログラムを作ることは可能でしょうか? ここに質問させていただいた結果、文字数が一つに定まっている場合のプログラムの書き方はわかりました。
Zuishin

2018/09/09 07:07

改行の前の文字を取ればいいだけですが、文字数が定まっていないというのはどういう意味ですか? 決めてかかってなどいないと思うのですが。
Zuishin

2018/09/09 07:09 編集

それと、新しい関数を使わないというのにどういう意味がありますか?
gregolio122

2018/09/09 10:38

失礼致しました。 文字数が定まっていないというのは、ユーザーが入力する文字数が何文字であると決まっていない、ということです。 w1に関して末尾を取るというのは、改行の前の文字を取れば良いので、forの継続条件を i<10かつw[i]!='\n'とすれば良いと思いますが、w2に関してはその文字列に改行コードが含まれないため、どうしたら末尾が取れるのかわかりません。 新しい関数を使わないでできるかどうか尋ねたのは、素朴な疑問からです。
Zuishin

2018/09/09 11:07

ユーザーの入力文字数が十文字以内であれば何文字であろうと次のコードで対応できるはずです。 for(i=0;i<10;i++){ scanf("%c",&w[i]); if (w[i] == '\n') break; } i=i-1; バッファが十文字しかないので、十文字を超える場合にはもちろんこれではできません。 もし最後の文字だけが必要なのであれば、十文字のバッファは不要で、改行が来るまで読み飛ばせばいいと思います。 char d = '\0'; while (1) { char c = getchar(); if (c == '\n') { break; } d = c; } matubi[0] = d; この場合、ユーザーの入力が 0 文字であれば matubi[0] には '\0' が、その他の場合は末尾の文字が入ります。
gregolio122

2018/09/14 05:10

お答えいただきありがとうございます。 お返事が遅くなってしまい申し訳ありません。 頂いた返信の内容については納得することができました。 新たな疑問なのですが //w1に関して for(i=0;w[i]!='\0'&&i<10;i++){ scanf("%c",&w[i]); } printf("i=%d\n",i); i=i-2; matubi[0]=w[i]; というコードを 標準入力でw1を「ab」と入力して実行していると i=6 w1の末尾は�です という結果になりました。 i=6という出力結果になったのはなぜなのでしょうか? a、bのその次は空文字ではないのでしょうか?改行を打ったつもりはないのですが…。 aはi=0のときにw[0]に格納され bはi=1のときにw[1]に格納され ループが完了して、iは2になる だから、i=2と表示されることを期待していたのですが…
Zuishin

2018/09/14 05:27

ちゃんと読んでいませんが。 あの w[i]!='\0' はいつ外すんでしょうか? 何度も説明した通り、これが入っている限りどんな結果でもあり得ます。 これについて了解してもらえないのはなぜでしょう?
gregolio122

2018/09/14 05:32

しっかりと定義していない配列の要素(空文字)に関してはどんな値も取りうるという認識はあったものの、ループの継続条件に関して w[i]!='\0' を外さなければどんな結果にもなりうる という理解はありませんでした。申し訳ありません。
Zuishin

2018/09/14 05:54

どんな値も取りうるものを for の継続条件にしているのでどんな結果もあり得ます。 まず for の文法を確かめてください。 for (初期化; 継続条件; 継続処理) w[i] == '\0' であった場合、この for は終了します。 どんな値でも取りうるということは、'\0' も取りうるということです。 その場合、for はいつ終了するかわかりません。
Zuishin

2018/09/14 06:27

ループがいつ終了するかわからないということは、ループ内でしている計算の結果も変わってくるということです。 ループを使って 2 を 10 回足せば 20 になりますが、ループが途中で終了したらどんな値になるかは不定でしょう?
guest

0

その文字列の末尾を取得したいです

質問欄にあるコードとは大幅に異なりますが、strlenを使うのはどうでしょうか?
入力された「文字列の長さ」を取得するものです。

C

1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4 5#define BUFSIZE 256 6 7int main(void) 8{ 9 char w1[BUFSIZE]; 10 char w2[BUFSIZE]; 11 12 printf("w1:"); 13 fgets(w1,sizeof(w1),stdin); 14 printf("w2:"); 15 fgets(w2,sizeof(w2),stdin); 16 17 printf("w1の末尾 : %c w2の末尾 : %c \n",w1[strlen(w1) - 2],w2[strlen(w2) - 2]); 18 19 return 0; 20}

<注意>
.w1[strlen(w1) - 2]のように配列の番号に -2がある理由ですが、

(i)まず、strlenは文字列の長さを取得するもので配列は0番目から始まるのでそのずれを修正するために-1

(ii)次に、fgetsを使うと末尾が改行となるため、その1つ前を取り出したいから-1
合計で -2 しなければいけません。

投稿2018/09/09 04:18

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

0

Cの仕様がどうなっていたかは確認していませんが、

for(i=0;w[i]!='\0'&&i<10;i++){

の動作は、

C

1 i = 0; 2 while (w[i] != '\0' && i < 10) { 3 // for()文内の処理 4 i++; 5 }

だったかと思います。(昔、デバッガで追った時...)
従って、 w[i] != '\0' の判定は、中身が確定していない w[i] を見ている事になります。

投稿2018/09/09 00:47

pepperleaf

総合スコア6383

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

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

gregolio122

2018/09/09 01:10

'\0'についての理解に自信がないのですが、中身が確定していない、というのも'\0'として扱われると考えていたのですが、ここが間違えていますでしょうか?
pepperleaf

2018/09/09 01:13

C言語の場合、初期化していない限り不定です。 また、初期化済みの場合、最初に (w[i] == '\0') 判定されるので、そこで終了すると思いますが、、、。
gregolio122

2018/09/09 01:22

申し訳ありません 初期化済みの場合に最初に (w[i] == '\0') 判定されるのは何故なのでしょうか? w[i]に関する初期化というのは、w[i]=0だけでなく、w[i]='a'とするのも初期化と言えますよね? やはり'\0'とは何か、について誤解をしているかもしれません w[i] に何らかの文字列が入っていれば w[i]は'\0' ではないという判定をされるという認識ですが…
pepperleaf

2018/09/09 01:37

書き方が悪かったでしょうか。 初期化済みの場合 ... 全てに '\0' が入っているという意味でした。 従って、 i=0 の時、 w[0] == '\0' が成立するので、forループは一回も処理されずに終了するという事です。 実際には、初期化されず、内容は不定なので、w[i] != '\0' となるかは不明ということです。(現実は、処理系でほぼ決まるが) ただし、2回目の forループは、最初の読込みの値が初期値となります。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問