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

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

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

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

Q&A

解決済

5回答

2083閲覧

英文字列に各文字間にスペースを挿入するプログラム

aiueo12345

総合スコア41

C

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

0グッド

0クリップ

投稿2018/11/01 11:04

前提・実現したいこと

C言語を用いて、英文字列に各文字間にスペースを挿入する関数「char *strspace(const char *s)」を含むプログラムを書きたいと思っています。

#include <stdio.h> char *strspace(const char *s) { ????? } int main(void) { char a[9999]; printf("英文字列を入力してください。\n"); scanf("%s",a); printf("%s\n",strspace(a)); return 0; }

例)abcdefg → a b c d e f g

発生している問題・エラーメッセージ

「const」でポインタsの要素を触ることは禁止されている状態で、関数「char *strspace(const char *s)」をどのように書けばよいのか思いつきません。
どのように考えればよいかご教授ください。

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

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

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

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

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

guest

回答5

0

「const」でポインタsの要素を触ることは禁止されている状態で

sの指す先の文字を変更できないと思ったのであれば、間違いです。
sというポインタ変数に代入できないだけです。

まあ、どっちみち、長さが足りないので、sの指す領域は更新してもしょうがないですが。

引数の文字列長strlen(s)を2倍した値が、結果格納用領域の必要最低限な長さなので、malloc(strlen(s)*2)した領域を使って、そこに、結果文字列を格納してください。

mallocの使い方は分かりますか?

投稿2018/11/01 11:31

編集2018/11/02 03:52
otn

総合スコア84533

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

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

aiueo12345

2018/11/01 12:23

回答ありがとうございます。 「sというポインタ変数に代入できない」というのは、「*s=」という代入ができないということであっていますか? 結果格納用領域としてstrlen(s)*2が必要であることは大丈夫だと思いますが、mallocについては知りません。 せっかくご回答いただいたのに、申し訳ありません。
otn

2018/11/01 12:53

> 「sというポインタ変数に代入できない」というのは、「*s=」という代入ができないということであっていますか? いいえ。それは出来ます。 s = ~; が出来ないと言うことです。
pepperleaf

2018/11/01 13:49

> malloc(strlen(s)*2) 細かい話ですが、 malloc(strlen(s)*2 + 1) じゃないでしょうか?
otn

2018/11/01 14:18

もとが3文字なら、処理後は5文字になるので、末尾ゼロを含めて6文字ですよ。
asm

2018/11/02 02:56

> sというポインタ変数に代入できないだけです。 重箱の隅をつつくようで申し訳ないですが、逆です。 const charとchar constは同じ型で char * constとconst char*が別の型になります。 なので、const char* sはs[0]に対して代入できないが、sに対して代入でる型になります。 https://port70.net/~nsz/c/c11/n1570.html#6.2.5p29
otn

2018/11/02 03:53 編集

大変失礼しました。 長年ちゃんとしたCのプログラムを書いてないのがばれてしまいました。 自戒のために、誤った解答をしたとわかるようにしておきます。
asm

2018/11/02 07:07

私もよくどっちだか忘れてしまいどっちにもconstつける選択をしてしまいます。
guest

0

スペースを入れた結果を格納するバッファのアドレスとそのサイズを引数に取る必要がありますね
元の文字列はいじりません

投稿2018/11/01 11:09

y_waiwai

総合スコア87774

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

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

aiueo12345

2018/11/01 12:18

回答ありがとうございます。 「バッファ」というものを使わないと書けないプログラムなのですか? お恥ずかしいことにまだ「バッファ」について学習していないものでして。。。
y_waiwai

2018/11/01 12:23

スペースを入れた変換結果を格納する場所が必要になりますよね? それをあらかじめ用意しておいて、関数にそれを与えて、それに結果を入れて返すようにします バッファといっても、charの配列でいいです
aiueo12345

2018/11/01 12:28

なるほど、結果を格納することのできる配列を別に準備しておけばいいんですね。 やってみます。
y_waiwai

2018/11/01 12:32

そゆことです。 気をつけなければならないのは、配列のサイズをオーバーしないようにすることです 文字列の最後の'\0'も含めて、オーバーしないように。
guest

0

ベストアンサー

修正しておきました。
for(i=0;i<strlen(s);i++) ですが、sを変化させているので
strlen(s)の値が、毎回変わってしまいます。
1)これを固定させます。
2)終端は、'\0'を入れないとだめです。(文字列操作の鉄則です)
3)ついでですが、if (2*strlen(s) > sizeof(buff)) return NULL;でreturnしているので
elseにする必要はありません。
for(・・・)から書いても構いません。そのほうがすっきりします。
ここは、動作上は問題ないので、あえて修正しませんでしたが・・・・

C

1#include <stdio.h> 2#include <string.h> 3 4char *strspace(const char *s) 5{ 6 static char buff[9999*2]; 7 int i,len; 8 if (2*strlen(s) > sizeof(buff)) return NULL; 9 else 10 { 11 len = strlen(s); 12 for(i=0;i<len;i++) 13 { 14 buff[2*i]=*s++; 15 buff[2*i+1]=' '; 16 } 17 buff[2*i-1] = '\0'; 18 return buff; 19 } 20} 21 22int main(void) 23{ 24 char a[9999]; 25 printf("英文字列を入力してください。\n"); 26 scanf("%s",a); 27 printf("%s\n",strspace(a)); 28 return 0; 29} 30 31

投稿2018/11/02 05:22

編集2018/11/02 05:27
tatsu99

総合スコア5438

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

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

aiueo12345

2018/11/02 05:31 編集

なるほど、そういうことだったんですね! 修正ありがとうございました。 追記について、勉強になりました。 わざわざありがとうございます(._.)_
Zuishin

2018/11/02 06:04 編集

次のコードを実行するとどうなるでしょうか? int main(void) { printf("%d\n", strcmp(strspace("abc"), strspace("def"))); return 0; } strspace("abc") と strspace("def") が同じであれば 0 で違えば 1 または -1 と表示されます。 同じだと思いますか? それとも違うと思いますか? 1 または -1 と表示されると思ったなら、慎重に使ってください。正解は 0 つまり同じです。 標準ライブラリの関数はみなバッファを外部から与えているので、この実装法はかなり特殊と言えます。 私の主観で歯に衣着せず言えば、あまり行儀のいい実装法ではありません。 見習うのであれば、今後このような実装をするときには、しっかりコメントを入れて注意を促しましょう。わかりにくいバグの原因となります。
asm

2018/11/02 07:22

> 標準ライブラリの関数はみなバッファを外部から asctimeとposix規格ですがstrdupがあります。
Zuishin

2018/11/02 07:24

malloc() でメモリを確保するものは対象外のつもりでした。
asm

2018/11/02 07:35

asctimeは規格書的にも静的ローカル変数を用いておりこの回答の実装に近いですね https://port70.net/~nsz/c/c11/n1570.html#7.27.3.1 ただまぁ、今回のケースでは最大文字数が予測できないので静的ローカル変数が最善かというと疑問ですが
can110

2018/11/02 07:41 編集

重箱隅ですがstrtokが該当しますね。 https://www.systutorials.com/docs/linux/man/3-strtok/ > The strtok() function uses a static buffer while parsing, so it's not thread safe. Use strtok_r() if this matters to you. もっとも、ちゃんとMANに明記されているので、注意して使うのは利用者の責任ですが。 その意味でも > しっかりコメントを入れて注意を促しましょう。 含め、Zuishinさんのコメントに同意です。
asm

2018/11/02 07:38

strtokはバッファを割り当てるには該当しませんね
Zuishin

2018/11/02 07:38

ああ本当ですね。「みな」というのは撤回します。
Zuishin

2018/11/02 07:46

再度確認します。 この実装は気を付けて使わないとバグのもとになるので行儀がよくない、バッファとバッファサイズを与えた方が読みやすくていいというのは私の主観です。 なぜなら、そのような実装の関数が多く、また他言語から学んだ人は文字列である戻り値が勝手に変わるような仕様にびっくりすると思うからです。 だから、この実装をまねるなら、しっかりとコメントすべきと主張します。
guest

0

内部に十分大きなバッファを確保し、そこへ格納します。
そのバッファに格納できない場合は、NULLを返します。

C

1#include <stdio.h> 2#include <string.h> 3 4char *strspace(const char *s) 5{ 6 static char buff[9999*2]; 7 if (2*strlen(s) > sizeof(buff)) return NULL; 8 //以降 sに空白を付加しながらbuffに文字を格納 9 return buff; 10} 11 12int main(void) 13{ 14 char a[9999]; 15 printf("英文字列を入力してください。\n"); 16 scanf("%s",a); 17 printf("%s\n",strspace(a)); 18 return 0; 19} 20

投稿2018/11/01 11:36

編集2018/11/01 11:38
tatsu99

総合スコア5438

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

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

aiueo12345

2018/11/01 12:27

回答ありがとうございます。 このバッファについてよく知らなかったのでこのプログラムを参考に書いてみたいと思います。
aiueo12345

2018/11/02 05:07

#include <stdio.h> #include <string.h> char *strspace(const char *s) { static char buff[9999*2]; int i; if (2*strlen(s) > sizeof(buff)) return NULL; else { for(i=0;i<strlen(s);i++) { buff[2*i]=*s++; buff[2*i+1]=' '; } return buff; } } int main(void) { char a[9999]; printf("英文字列を入力してください。\n"); scanf("%s",a); printf("%s\n",strspace(a)); return 0; } これで実行してみたところ、入力した文字のおよそ半分しか出力されませんでした。 およそというのは、  1文字入力すると1文字出力され、  2文字入力すると1文字出力され、  3文字入力すると2文字出力され、  4文字入力すると2文字出力され、  5文字入力すると3文字出力され、  6文字入力すると3文字出力され、  7文字入力すると4文字出力されるといった具合です。 どこを修正すればよいでしょうか?
guest

0

strspace 関数中で static な配列を用意した場合、マルチスレッドで問題がでる気がします。
static な配列を使わない、 strspec の引数に結果を格納するエリアとサイズを渡すようにしたものを書いてみました。
扱える文字の長さを 3 にして、境界での動作を試すことを mai() 中で行っています。

a.c

c

1#include <stdio.h> 2#include <string.h> 3 4#define SRC_BUFF_SIZE (4) // (9999) 5 6const char *strspace(const char *src, char * dest, size_t dest_size) { 7 char w[BUFSIZ]; 8 char *wp = w; 9 for (; *src; ) { 10 *wp++ = *src++; 11 *wp++ = ' '; 12 } 13 // 最後の ' ' は削除する 14 if (w < wp) {wp--;} 15 *wp = '\0'; 16 17 strncpy(dest, w, dest_size); 18 dest[dest_size - 1] = '\0'; // strncpy で '\0' が設定されなかった場合への対処 19 return dest; 20} 21 22int main(void) { 23 char a[SRC_BUFF_SIZE]; 24 char b[SRC_BUFF_SIZE * 2 - 1]; 25 26 strcpy(a, ""); 27 printf("'%s' -> '%s'\n", a, strspace(a, b, sizeof(b))); 28 strcpy(a, "1"); 29 printf("'%s' -> '%s'\n", a, strspace(a, b, sizeof(b))); 30 strcpy(a, "12"); 31 printf("'%s' -> '%s'\n", a, strspace(a, b, sizeof(b))); 32 strcpy(a, "123"); 33 printf("'%s' -> '%s'\n", a, strspace(a, b, sizeof(b))); 34 printf("'%s' -> '%s'\n", a, strspace(a, b, sizeof(b) - 1)); 35 printf("'%s' -> '%s'\n", a, strspace(a, b, sizeof(b) - 2)); 36 37 printf("英文字列を入力してください。(%lu 文字以降は無視されます)\n", sizeof(a) - 1); 38 fgets(a, sizeof(a) - 1, stdin); 39 printf("'%s' -> '%s'\n", a, strspace(a, b, sizeof(b))); 40 return 0; 41}

実行例
イメージ説明

投稿2018/11/02 15:09

katoy

総合スコア22324

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問