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

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

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

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

Q&A

解決済

2回答

1141閲覧

文字列についての問題

Merrifield

総合スコア31

C

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

0グッド

0クリップ

投稿2020/09/02 12:20

編集2020/09/02 14:17
"Bangkok" --> "b:*,a:*,n:*,g:*,k:**,o:*" "Las Vegas" --> "l:*,a:**,s:**,v:*,e:*,g:*"

上記の例は、文字列を受け取り、それぞれの文字の出現数を*の数で表しています。このような文字列を戻り値として返す関数を作成する問題をcodewars(https://www.codewars.com/kata/5b358a1e228d316283001892/train/c)というサイト内で解いています。

自分で作成した関数をテストしてみると
("Chicago") の場合は、
"c:**,h:,i:,a:,g:,o:",となるべきところが "C:h:i:c:a:g:o:"のようになってしまいます。
コードを示しますので、どこがおかしいのか教えていただけますか。また、コード内で
string++ = と何度か書いてあるのですが、いまいちこの*(変数名)++の意味が分かっていないので、こちらのほうも教えていただきたいです。特に、一番最初の*string++はstring[0]と同じことなのでしょうか?

c

1#include <string.h> 2#include <stdlib.h> //malloc 3 4char* get_strings(const char *city) 5{ 6 int i, j, n; 7 n = strlen(city); 8 char *string; 9 string = malloc( n * 4 + 1); 10 char *base = string; 11 12 for(i = 0; i < n; i++ ) 13 { 14 15 int count = 0; 16 int count2 = 0; 17 18 for(j = 0; j < n; j++) 19 { 20 if(city[i] == city[j])count++; 21 22 } 23 24 *string++ = city[i]; 25 *string++ = ':'; 26 27 if(i > 0) 28 { 29 for(int b = i - 1; b == 0; b--) 30 { 31 if(city[i] == city[b])count2++; 32 } 33 } 34 35 36 if(count2 != 0) 37 { 38 39 40 for(int a = 1; a == count; a++) 41 { 42 *string++ ='*'; 43 } 44 45 if(i != (n -1)) 46 { 47 *string++ = ','; 48 } 49 else if(i == (n - 1)) 50 { 51 *string++ = '\0'; 52 } 53 54 55 56 57 } 58 59 60 61 62 } 63 64 return base; 65}

回答いただいたものを参考にして、少し修正してみました。

c

1#include <string.h> 2#include <stdlib.h> //malloc 3#include <ctype.h> // tolower 4 5char* get_strings(const char *city) 6{ 7 int i, j, n; 8 n = strlen(city); 9 char *string; 10 string = malloc( n * 4 + 1); 11 char *base = string; 12 13 for(i = 0; i < n; i++ ) 14 { 15 int c = tolower((unsigned char)city[i]); 16 if (!isalpha(c)) continue; 17 18 int count = 0; 19 int count2 = 0; 20 21 for(j = 0; j < n; j++) 22 { 23 if(tolower(city[i]) == tolower(city[j]))count++; 24 25 } 26 27 28 if(i > 0) 29 { 30 for(int b = i - 1; b >= 0; b--) 31 { 32 if(tolower(city[i]) == tolower(city[b]))count2++; 33 } 34 } 35 if(count2 != 0)continue; 36 37 *string++ = tolower(city[i]); 38 *string++ = ':'; 39 40 41 for(int a = 1; a <= count; a++) 42 { 43 *string++ ='*'; 44 } 45 46 if(i != (n -1)) //この部分が問題? 47 { 48 *string++ = ','; 49 } 50 else 51 { 52 *string = '\0'; 53 } 54 55 56 } 57 58 return base; 59}

文字列によって成功するようになりましたが、まだ問題があるようです。
メッセージは

string_literal("Bangkok") should return "b:*,a:*,n:*,g:*,k:**,o:*", actual: "b:*,a:*,n:*,g:*,k:**,o:*,\"" string_literal("Dhaka") should return "d:*,h:*,a:**,k:*", actual: "d:*,h:*,a:**,k:*,"

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

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

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

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

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

guest

回答2

0

("Chicago") の場合は、

"c:**,h:,i:,a:,g:,o:*",となるべきところが "C:h:i:c:a:g:o:"のようになってしまいます。
コードを示しますので、どこがおかしいのか教えていただけますか。

大文字を小文字に変換していないので C が大文字のままです。

if (count2 != 0) { だと、今の文字より前に同じ文字があったということだから、
今の文字が最初に出てきたのに '*' を string に追加しません。

さらに

C

1 for(int a = 1; a == count; a++) 2 { 3 *string++ ='*'; 4 }

これだと count が 1 の時だけ '' が 1個追加され、
count が 1 でない時は '
' は追加されません。

私なら次のように書きます。

C

1#include <string.h> // strlen 2#include <stdlib.h> // malloc, free 3#include <ctype.h> // tolower 4#include <stdio.h> // puts 5 6char *get_strings(const char *city) 7{ 8 int n = strlen(city); 9 char *string = malloc(n * 4), *base = string; 10 11 for (int i = 0; i < n; i++) { 12 int c = tolower((unsigned char)city[i]); 13 if (!isalpha(c)) continue; 14 int j = i; 15 while (--j >= 0 && c != tolower((unsigned char)city[j])) ; 16 if (j >= 0) continue; 17 if (string > base) *string++ = ','; 18 *string++ = c; 19 *string++ = ':'; 20 for (j = i; j < n; j++) 21 if (c == tolower((unsigned char)city[j])) 22 *string++ = '*'; 23 } 24 *string = '\0'; 25 return base; 26} 27 28int main(void) 29{ 30 char *p; 31 p = get_strings("Chicago"); puts(p); free(p); 32 p = get_strings("bangkok"); puts(p); free(p); 33 p = get_strings("Las Vegas"); puts(p); free(p); 34}

投稿2020/09/02 13:23

編集2020/09/02 13:43
kazuma-s

総合スコア8224

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

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

0

ベストアンサー

*string++ は、演算子の優先度の関係で、*(string++)こうなります。
まず最初に、string++ が解決されます。
char *string;stringはポインタ型なので、これはポインタ演算になります。
ポインタ変数に対して、++すると、ポインタ変数の型に応じたサイズだけ、ポインタが進みます。
charは1Byteなので、string++とすると、stringが保持しているポインタ(アドレス)が
1Byte分進んだ値に更新されます。

しかし、string++と、後置インクリメントなので、stringの値は1つ進みますが、
string++という式自体の値は、進む前の元のstringの値になります。
式の値としてだけ見れば、string++は、stringと同じになります。

その次に、*の部分が解決されます。
*はポインタ変数に対して、それが保持しているアドレスに書かれているデータへアクセスする、
間接参照演算子です。

先に書いたように、*string++のうち、string++の部分は、stringと等価ですので、
*stringと書いたのと同じになります。普通のポインタの先へのアクセスです。

*string = 'A' とすると、それに対する代入なので、ポインタ示すアドレスに、'A'が書き込まれます。
それに加えて、
*string++ = 'A' の場合は、stringの値が加算されますので、ポインタのアドレスは先に進みます。

つまり、ポインタの指すアドレスに、データを書き込み、さらに、ポインタを次に進めるという内容となります。

内容としては、
*string = 'A';に続いてstring++;をしたのと同じになります。


全体の流れが判りやすいようにコメントを付けます。

C

1#include <string.h> 2#include <stdlib.h> //malloc 3 4char* get_strings(const char *city) 5{ 6 int i, j, n; 7 n = strlen(city); 8 char *string; 9 string = malloc( n * 4 + 1); 10 char *base = string; 11 12 for(i = 0; i < n; i++ ) //元の文字列から一つずつ文字をとる 13 { 14 15 int count = 0; 16 int count2 = 0; 17 18 for(j = 0; j < n; j++) //元の文字列の中に同じ文字があるかカウント 19 { 20 if(city[i] == city[j])count++; //自分があるので最低1以上 21 } 22 23 *string++ = city[i]; //全ての文字を無条件で提示用に登録 24 *string++ = ':'; 25 26 if(i > 0) //iが1以上なら=1文字目だけを除いて以下を処理 27 { 28 for(int b = i - 1; b == 0; b--) //今の文字より前の文字を調べる 29 { 30 if(city[i] == city[b])count2++; //同じ文字があればカウント 31 } 32 } 33 34 35 if(count2 != 0) //自分より前に同じ文字があれば 36 { 37 for(int a = 1; a == count; a++) //カウントした数だけ 38 { 39 *string++ ='*'; //*を付ける 40 } 41 42 if(i != (n -1)) //最後の文字以外なら 43 { 44 *string++ = ','; //,を付ける 45 } 46 else if(i == (n - 1))       //最後の文字以外ではなく最後の文字なら(if不要) 47 { 48 *string++ = '\0'; //末端文字を付ける 49 } 50 } 51 } 52 return base; 53}

まずは、大文字と小文字を区別しないという処理がありません。
次に、表示用文字列に対して、文字を登録する時に、問答無用で登録してしまっているので、
C:h:i:c:a:g:o:の様に、二つ目のcが登場してしまっています。
2つ以上ある文字の2度目の登場はスルーする必要があります。

*が出ないのは、*を出すかを決める条件が、「自分よりも前に同じ文字が1つ以上あれば」となっています。
Chicagoの文字列の中で、大文字小文字を区別した場合、「自分よりも前に同じ文字が1つ以上」あるものは存在しないので、*が表示されません。
「自分よりも前に同じ文字が1つ以上あれば、その数の*を表示する」ではなく、
「文字列の中に自分と同じ文字があった数の*を表示する」とする必要があります。
(自分自身を含むので必ず1以上の*は付きます)

投稿2020/09/02 12:49

編集2020/09/02 13:18
amiya

総合スコア1218

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

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

Merrifield

2020/09/02 14:30

回答ありがとうございます。参考にしてコードを直しました。与えられた文字列によって、成功したり失敗したりするようになりました。 string_literal("Ahmedabad") should return "a:***,h:*,m:*,e:*,d:**,b:*", actual: "a:***,h:*,m:*,e:*,d:**,b:*," このようなエラーが出るのですが、これは最後に,がついているということになるのでしょうか?コードの最後のほうの,や終端文字の処理に問題あるのでしょうか?
amiya

2020/09/02 14:56

文字列の最後の文字は、d です。しかし、d は既に出ているので、実際には出力文字には登録されません。 一方、実際に最後に出力する文字である b を出した時には、文字列にはまだ残りがあるから、 続きがあるだろう、として、, を付けています。 という感じに、元の文字列の最後の文字かどうかと、出力文字の最後の文字かは一致しません。 なので、「最後の文字なら~」という条件判断は使えません。 ある文字を登録する段階でその文字が最後かどうかはわからないので、 発想を変えて、2文字目以降を登録する時に、, を付けてから後に続けるか、 もしくは、全部終わった最後に、ヌル文字で上書きすることで、最後の , を削ってしまうという方が楽かもしれません。
hana_yama_san

2020/09/02 15:00

あのですね、コードを直すのは良いのですが、 修正前と修正後の比較が出来るように 記述していただけると有難いです。 後から来て、見た人には極めて分かり辛いです。
Merrifield

2020/09/03 00:06

>>amiyaさん 最後の部分をヌル文字で上書きすることで上手く出来ました。ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問