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

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

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

charは文字データ型を指します。一文字分の文字コードの格納を想定としている型です。

C

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

Q&A

解決済

4回答

6237閲覧

char型の配列変数にchar型の変数を代入したい

start08

総合スコア9

char

charは文字データ型を指します。一文字分の文字コードの格納を想定としている型です。

C

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

0グッド

0クリップ

投稿2017/07/29 04:39

使用言語 C 環境 Visual Studio 2017
初めての質問です。
独学でプログラミングを始めたのですがわからないところがあり困っています。
char型の配列変数の使い方がいまいちわかりません。

キーボードからいくつかの単語を半角スペースで区切って入力し、最後に改行を行うと
それらの単語を単語ごとに入力された順に、単語と入力回数を出力する
というプログラムを書きたいです。
英単語です。

入力例:
cat dog dog bird cat dog
出力例:
cat 2
dog 3
bird 1

・どこが間違っているのか
・違う書き方(便利な手法があれば)
の二点を教えていだたきたいです。

以下は自分で書いてみたプログラムとエラー箇所です。

#include <stdio.h>
#include <string.h>

int main(void) {
char ward[1001]; //単語 字数は 1<=wards<=1000//
int i, j;
int ct = 1; //入力された単語の数
char list_W[1001][1001]; //単語リスト
int ct_Same_W[1001]; //同単語カウント
//半角スペースで区切られて単語(半角英字)が標準入力される(末尾は改行)
int lp;
while(lp == 1) {
scanf("%s", ward); //単語の入力
for (j = 0;j < ct;j++) {
if (list_W[j] == ward) { //同単語の場合、ct_Same_W[j]でカウント
ct_Same_W[j] += 1;
}
else if (j == ct - 1) { //同単語がない場合はlist_W[j]へ代入
list_W[j] = ward;
ct += 1;
}
}
for (i = 0;i <= strlen(ward);i++) {
if (ward[i] == '\n') { //改行があった場合、
lp == 0; //ループを離脱
}
}
}
for (i = 0;i <= j;i++) {
//単語リストとそれぞれの出現回数を表示
printf("%s %d\n", list_W[i], ct_Same_W[i]);
}
}

また、将来、実務で役立つようなプログラムの書き方でアドバイス等頂けると助かります。
例:変数名や変数の宣言の仕方など
イメージ説明

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

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

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

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

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

guest

回答4

0

そもそも文字列操作をCでやるな、という話がありますが、それでもCでやってみましょう。

個人的にscanfが好きじゃないので、fgetsstrtokを組み合わせてやってみましょう。
strtokは非常に癖があり、スレッド安全ではなく、入力文字列を書き換えるので使いにくいですが、今回は利用できそうです。

c

1#include <stdio.h> 2#include <string.h> 3#include <assert.h> 4 5#ifndef countof 6# if defined(_MSC_VER) && !defined(__c2__) 7 //MSVC except Clang CodeGen 8# define countof _countof 9# else 10# define countof( arr ) (sizeof(arr) / sizeof(*arr)) 11# endif 12#endif 13 14///! 単語情報を格納する構造体 15typedef struct tag_word_info { 16 ///! 見つかった数 17 size_t count; 18 ///! 単語 19 char word[1001]; 20} word_info; 21 22int main(void) { 23 char line[1001]; 24 size_t ct = 0; //入力された単語の数 25 word_info word_list[1001];//単語リスト 26 //半角スペースで区切られて単語(半角英字)が標準入力される(末尾は改行) 27 if(NULL == fgets(line, countof(line), stdin)) return 1; 28 for(const char* word = strtok(line, " \n"); NULL != word; word = strtok(NULL, " \n")) { 29 size_t i; 30 for (i = 0; i < ct && ct < countof(word_list); ++i) { 31 if (0 == strcmp(word_list[i].word, word)) { 32 //同単語の場合、ct_Same_W[i]でカウント 33 ++word_list[i].count; 34 break; 35 } 36 } 37 if(i == ct) { 38 //同単語がない場合はlist_W[i]へコピー 39 //strcpy(word_list[i].word, word); 40 memcpy(word_list[i].word, word, strlen(word) + 1); 41 word_list[i].count = 1; 42 ++ct; 43 } 44 } 45 for (size_t i = 0; i < ct; ++i) { 46 //単語リストとそれぞれの出現回数を表示 47 printf("%s %zd\n", word_list[i].word, word_list[i].count); 48 } 49} 50

https://wandbox.org/permlink/AdnCtvSQJx5d32MM

それではいくつか。

  • 変数の命名、もうすこしどうにかなりませんかね・・・
  • 配列の添字として使う変数は原則size_t型にしましょう
  • 文字列中から文字検索は自分でループを書かずに、strchr関数を利用しましょう(今回はstrtokでやってしまいましたが)
  • 構造体をつかうと可読性が上がることがあります。

追記

もうすこし関数わけするべきだったという反省を活かし、C言語でオブジェクト指向してみました。C++書きたい・・・

C

1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4#include <assert.h> 5 6#ifndef countof 7# if defined(_MSC_VER) && !defined(__c2__) 8 //MSVC except Clang CodeGen 9# define countof _countof 10# else 11# define countof( arr ) (sizeof(arr) / sizeof(*arr)) 12# endif 13#endif 14 15///! 単語情報を格納する構造体 16typedef struct tag_word_info { 17 ///! 見つかった数 18 size_t count; 19 ///! 単語 20 char word[1001]; 21} word_info; 22 23typedef struct tag_word_list word_list; 24size_t word_list_find_word(const word_list* words, const char* key_str); 25void word_list_print(const word_list* words); 26void word_list_push_back(word_list* words, const char* word); 27struct tag_word_list { 28 size_t (*find_word)(const word_list* words, const char* key_str); 29 void (*print)(const word_list* words); 30 void (*push_back)(word_list* words, const char* word); 31 ///! 現在の大きさ(1001以下) 32 size_t size; 33 ///! リスト 34 word_info list[1001]; 35}; 36 37/** 38 * @brief find word from list 39 * @params words[in] read-only pointer to word list 40 * @params key_str[in] read-only pointer to key string to find. 41 * @return Found index. If not found, this function will return `words->size`. 42 */ 43size_t word_list_find_word(const word_list* words, const char* key_str){ 44 size_t i; 45 for (i = 0; i < words->size; ++i) { 46 if (0 == strcmp(words->list[i].word, key_str)) return i; 47 } 48 return i; 49} 50/** 51 * @brief print-out word list 52 * @params words[in] read-only pointer to word list 53 */ 54void word_list_print(const word_list* words){ 55 for (size_t i = 0; i < words->size; ++i) { 56 //単語リストとそれぞれの出現回数を表示 57 printf("%s %zd\n", words->list[i].word, words->list[i].count); 58 } 59} 60/** 61 * @brief append new word to word list 62 * @params words[in,out] pointer to word list 63 * @params word[in] Read-only pointer to string to append. 64 */ 65void word_list_push_back(word_list* words, const char* word){ 66 if(countof(words->list) <= words->size) return; 67 memcpy(words->list[words->size].word, word, strlen(word) + 1); 68 words->list[words->size].count = 1; 69 ++words->size; 70} 71/** 72 * @brief create word list 73 */ 74word_list* new_word_list(){ 75 word_list* re = (word_list*)malloc(sizeof(word_list)); 76 if(NULL == re) return NULL; 77 //set function pointer 78 re->find_word = word_list_find_word; 79 re->print = word_list_print; 80 re->push_back = word_list_push_back; 81 //init size is zero 82 re->size = 0; 83 return re; 84} 85/** 86 * @brief delete word list 87 * @params words[in,out] pointer to word list 88 */ 89void delete_word_list(word_list* words){ 90 free(words); 91} 92int main(void) { 93 char line[1001]; 94 word_list* words = new_word_list();//単語リスト 95 if(NULL == words) return 1; 96 //半角スペースで区切られて単語(半角英字)が標準入力される(末尾は改行) 97 if(NULL == fgets(line, countof(line), stdin)){ 98 delete_word_list(words); 99 return 1; 100 } 101 for(const char* word = strtok(line, " \n"); NULL != word; word = strtok(NULL, " \n")) { 102 const size_t found_index = words->find_word(words, word); 103 if(words->size == found_index) { 104 //同単語がない場合は追加 105 words->push_back(words, word); 106 } 107 else { 108 //同単語の場合、カウントアップ 109 ++words->list[found_index].count; 110 } 111 } 112 words->print(words); 113 delete_word_list(words); 114}

https://wandbox.org/permlink/qo52P1eMw7KXXnAQ

投稿2017/07/29 06:33

編集2017/07/29 16:24
yumetodo

総合スコア5850

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

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

start08

2017/07/29 10:44

実際にこのプログラムに取り組んでいると文字列操作をC言語であまりやらない方がいいという事に気づきました。 書いてくださったコードもひとつひとつ意味を調べながら確認したいと思います。 質問してよかったです! ありがとうございました!
guest

0

幾つか前提変更しましたのでご参考のPGです。
scanf("%s")では改行文字はスペース文字と同じで入力の区切りとみなされて、それ自身を取り込むことはできないのでEOFで終了としました。

c

1#include <stdio.h> 2#include <string.h> 3 4int main(void) { 5 char ward[1001]; //単語 字数は 1<=wards<=1000// 6 int i, j; 7 int ct = 1; //入力された単語の数 8 char list_W[1001][1001]={'\0'}; //単語リスト クリア 9 int ct_Same_W[1001]; //同単語カウント 10 //半角スペースで区切られて単語(半角英字)が標準入力される(末尾は改行) 11 // int lp=1; 12 while(scanf("%s", ward)!=EOF) { // 単語入力 13 for (j = 0; j<ct; j++) { 14 if (strcmp(ward,list_W[j])==0) { //同単語の場合、ct_Same_W[j]でカウント 15 ct_Same_W[j] += 1; 16 break; 17 } 18 else if (j == ct - 1) { //同単語がない場合はlist_W[j]へ代入 19 strcpy(list_W[j], ward); 20 ct_Same_W[j] = 1; 21 ct += 1; 22 break; 23 } 24 } 25 // for (i = 0;i <= strlen(ward);i++) { // \nは入力されないのでEOF(ctl+d)としました。 26 // if (ward[i] == '\n') { //改行があった場合、 27 // lp == 0; //ループを離脱 28 // } 29 // } 30 } 31 for (i = 0; i<ct-1 ;i++) { 32 //単語リストとそれぞれの出現回数を表示 33 printf("%s %d\n", list_W[i], ct_Same_W[i]); 34 } 35}

投稿2017/07/29 06:35

A.Ichi

総合スコア4070

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

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

start08

2017/07/29 10:46

scanfがとても不便だったり便利だったり、色々調べてるうちになんとなく理解できました。 書いてくださったプログラムも参考にして改めて自分で書いてみたいと思います。 質問してよかったです、ありがとうございました!
guest

0

ベストアンサー

文字列のコピーには、代入ではなくstrcpyを使います。
list_W[j] = ward;を、strcpy(list_W[j], ward);に置き換えてください。

どうしても=で代入したい場合は、一文字ずつ処理するか、ポインタを用いる必要があります。

...あと、wardじゃなくてwordじゃないですかね。


私も書いてみました。せっかくなので載せます。
純粋なC言語はしばらく触ってませんので、よくない点も多々あると思いますが...

C

1#include <ctype.h> 2#include <stdio.h> 3#include <stdlib.h> 4#include <string.h> 5 6#define BUF_LEN 1000 7#define WORD_LEN 20 8#define WORD_TYPE 500 9 10typedef struct NL_WORD { 11 char word[WORD_LEN+1]; 12 int count = 0; 13} NL_WORD; 14 15char *read_token(char **, char *); 16int find_name(NL_WORD *, int, char *); 17 18int main(void) { 19 char buffer[BUF_LEN+1]; 20 char *buf_ptr = buffer; 21 22 char input_word[WORD_LEN+1]; 23 24 NL_WORD word_info[WORD_TYPE] = {}; 25 int read_word_num = 0; 26 27 int i; 28 29 fgets(buffer, BUF_LEN, stdin); 30 while(read_token(&buf_ptr, input_word) != NULL) { 31 int index; 32 if( (index = find_name(word_info, read_word_num, input_word)) != -1 ) { 33 word_info[index].count++; 34 } 35 else { 36 strcpy(word_info[read_word_num].word, input_word); 37 word_info[read_word_num].count = 1; 38 read_word_num++; 39 } 40 } 41 42 for(i = 0; i < read_word_num; i++) { 43 printf("word: %s\n", word_info[i].word); 44 printf("num: %d\n", word_info[i].count); 45 } 46 47 system("PAUSE"); 48 return EXIT_SUCCESS; 49} 50 51char *read_token(char **buffer, char *dst) { 52 int start, end; 53 for(start = 0; isspace((*buffer)[start]); start++) { 54 if( (*buffer)[start] == '\0' || 55 (*buffer)[start] == '\n' ) return NULL; 56 } 57 *buffer += start; 58 59 for(end = 0; !isspace((*buffer)[end]); end++) { 60 dst[end] = (*buffer)[end]; 61 } 62 dst[end] = '\0'; 63 *buffer += end; 64 65 return *buffer; 66} 67 68int find_name(NL_WORD *word_list, int word_list_len, char *input_word) { 69 int i; 70 for(i = 0; i < word_list_len; i++) { 71 if( !strcmp( word_list[i].word, input_word ) ) return i; 72 } 73 return -1; 74} 75

このコード、yumetodoさんのコードの劣化版みたいになってますね...
上げてから気付きました。


命名というか、変数名の書き方で、次のような分類があります。

名前(例示)別名説明
UpperCamelCasePascalCase単語の頭文字を大文字にして繋げる
lowerCamelCasecamelCase単語の頭文字は大文字、でも最初だけ小文字
SNAKE_CASESNAIL_CASE全部大文字、単語の繋ぎはアンダーバー
snake_casesnail_case全部小文字、単語の繋ぎはアンダーバー

言語によって、どのケースを用いるかが大体決まっています。
名前を見ただけで、『あ、これは関数だ』『変数だ』『マクロだ』と判断できるので重要です。

また、明白でない場合、略語はあまり用いない方が良いかもしれません。
コードが横に長くなりますが、Cは自由構文なので、適宜改行で調整できます。

投稿2017/07/29 04:49

編集2017/07/29 07:54
LouiS0616

総合スコア35660

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

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

start08

2017/07/29 04:53

早い解答ありがとうございます。 早速試してみます。 確かに ward ではなく word ですね! 徹夜と英語の勉強不足が響いて恥ずかしいミスをしていましたorz
start08

2017/07/29 10:41

変数の宣言についての解説ありがとうございます。 書いてくださったコードも、にらめっこしつつ、ひとつひとつ調べてみたいと思います。 とても参考になりました、ありがとうございます。 質問してよかったです!
yumetodo

2017/07/29 15:32

>このコード、yumetodoさんのコードの劣化版みたいになってますね... find_nameはたしかに関数として処理をくくりだすべきだったな・・・、反省。 >read_token strtokよりこれのほうが本当はいいよなぁ、本当にstrtokの設計考えたやつは頭おかC・・・
guest

0

素早い回答、およびプログラムまで書いてくださってありがとうございます。
回答者様方の書いたものに比べたら大したものではありませんが、
ヒントを頼りに自分なりの答えを書いてみました。

イメージ説明

不完全ながらもほぼ半日かけてしまいましたがひとまずはスッキリしました。
とても楽しかったです!
学習不足を痛感したのと同時に、違う言語も学習し、使い分ける事が重要にも感じました。

どの回答者様も興味深い回答をしてくださったのですが、変数の宣言の仕方についても教授いただいた方の回答をベストアンサーにしました。
まだ全て読んでいないので、休憩しながらでもにらめっこしたいと思います。

余談ですが、現在大学4年の就活中で、就活を始めた時期にプログラマーへと志望を変え、文系ながらもコツコツ取り組んでおります。
いまだに内定を取れていませんがどうしてもプログラマーになりたく、頑張っていたのですが、
難しいプログラミングの概念に挫折しそうになっていたところ、たまたまこのサイトに辿り着きました。
今日は本当に質問してよかったと感じています。
また、わからないことがあったら質問させていただきたいと思います。
本当にありがとうございました。

投稿2017/07/29 11:08

start08

総合スコア9

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

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

yumetodo

2017/07/29 15:29

まあなんか回答の殆どが有効活用されなかったなという思い。 つーかなんでもかんでも自分でループ書くの本当に良くない、保守性もテスト可能性もかけらもない 。fgetsの戻り値見ないとかありえないし、構造体使わないのも本当にありえないし、 そもそもここは回答欄で質問者が書き込む場所ではないし、なんだかなという。
start08

2017/07/29 17:25 編集

まだまだ曖昧な知識で、今の自分が取り組める課題ではなかったと実感しております。 なんでもかんでもループを書かず、strtokなどを使うべきでした。 特に、戻り値、ポインタ、アドレスなどと言った欠かせない基礎概念をしっかり理解することをさけていたところが致命的でした。 最優先で学習します。 まずはすべての回答のわからないところひとつひとつを調べてみます。 保守性、テストの可能性についても意識していきたいと思います。 プログラマとしての常識がまったく身についてないことを痛感しましたし、このサイトの使い方自体間違えていたので、恥ずかしい次第です。 またプログラミングの基礎を1から学びなおします。
start08

2017/07/29 17:37

確認のような質問になってしまいますが、お時間があればお答えいただけると助かります。 まず、「構造体」というのは、変数の宣言の仕方を工夫して、関連のある変数をひとくくりにしてわかりやすくするという認識でだいたいあってますでしょうか? (イメージとしては、配列変数を一列に並んだロッカーに例えて、ロッカールームを用意する、、というような感じ?、3次配列変数と違って色々な型の配列変数をひとくくりにできる) 書き方としては、int main(void)の上に宣言して、後にその内容を記述する定義(単純ですいません)、といった、 structを用いて、宣言→プログラム→定義 であってますでしょうか 初歩的な質問で煩わせてしまい申し訳ありません。
yumetodo

2017/07/30 10:07

>まず、「構造体」というのは、変数の宣言の仕方を工夫して、関連のある変数をひとくくりにしてわかりやすくするという認識でだいたいあってますでしょうか? なんか違う。構造体とは任意の型のデータの集合です。整数型(int, unsigned long, etc...)や浮動小数点型(flaot, double)やその配列型やポインタ型、構造体型などを内包できます。 struct { //member variable declarations } までが型名です。ただこんな長い型名をいちいち書いていられないので struct /*tag name*/ { //member variable declarations } tag nameをつけます。するとたとえば struct tag_Hoge { int a; float b; long c[2]; double* d; }; という構造体があったとき struct tag_Hoge を型名として利用できます。しかしstructと書くのすら億劫です。 typedef struct tag_Hoge { int a; float b; long c[2]; double* d; } Hoge; や typedef struct tag_Hoge Hoge; struct tag_Hoge { int a; float b; long c[2]; double* d; }; のようにふつうします。
start08

2017/07/31 02:40

詳しい解説ありがとうございました! 実際に構造体を用いたプログラムをいくつか書いて、使えるようにしたいと思います。 オブジェクト指向にも興味がわいたのでC++にも触れてみたいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問