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

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

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

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

Q&A

解決済

3回答

7311閲覧

スペース区切りの文字列と整数の読み取り

thoifon

総合スコア12

C

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

0グッド

0クリップ

投稿2017/04/01 21:48

-入力-
N
都市名1 数
都市名2 数

一行目のNがその後何行要素を読み込むか決めています。

-入力例-
2
tokyo 12
honoruru 11

-出力例-
tokyo
12
honoruru
11

stockにstrで読み取った要素の「都市名」を、minにnumで読み取った要素の「数」をコピーしています。
for文の後、読み取った都市名と同じものを表示するようにしています。

配列を「都市名」と「数」で2つ宣言しているのは冗長であるとおもいます。
何か他に書き方があれば教えていただきたいです。

以下私の書いたコードです。

C

1#include <stdio.h> 2int main(void){ 3 int N; 4 scanf("%d", &N); 5 char str[N+1], stock[N]; 6 int num[N], min[N], tmp; 7 for (int i=0; i<N; i++) { 8 scanf("%s", &str[i]); 9 stock[i] = str[i]; 10 scanf("%d", &num[i]); 11 min[i] = num[i]; 12 13 printf("%s\n", &str[i]); 14 printf("%d\n", num[i]); 15 } 16 return 0; 17}

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

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

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

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

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

raccy

2017/04/01 22:35

都市名は何文字以下という制限はないのですか?無制限の文字列受け取りはC言語だととても面倒似なるのですが…。あと、VC++でも動くようにする(VLAが使えない)と言う縛りも無しで良いでしょうか?
thoifon

2017/04/02 02:23

今回は実在する都市名を対象としていますのでchar型で扱えるサイズの文字列を想定しています。VC++を存じ上げませんが、無しで良いかと思います
guest

回答3

0

ベストアンサー

配列を「都市名」と「数」で2つ宣言しているのは冗長

「数」についてはそうですね。またそれぞれを配列として宣言するより構造体としてまとめた方がよいという観点もあります。しかし元のコードでは「都市名」については、冗長云々より先に正しくない実装になっていますのでその点をコメントします。

  • 配列の宣言(固定サイズ、可変サイズ)

もし配列の要素数Nが固定(定数)なら

C

1#define N 10 2 3int min[N]; 4 5int main() { 6 for (int i = 0; i < N; i++) { 7 min[i] = ...; // 配列の要素のアクセス 8 } 9}

のように書けます。固定サイズの配列はどのバージョンのCでも書けますので基本的な書き方と言えるでしょう。

しかし配列の要素数Nが可変(プログラムを実行する度に値が違うものとなり得る)なら、基本は「mallocにより動的にメモリーを確保する」方法をまず学ぶことをお勧めします。それを意識しているかどうかはっきりしませんが、VLA(variable length array)を使っておられます。しかしコードの完成度からいって自分は質問者さんにまずmallocを使う方法を学ぶことをお勧めします。Nが可変のとき配列を動的に確保するにはCではポインターを使って次のように書きます。

C

1#include <malloc.h> 2... 3int N; 4scanf("%d", &N); // プログラムを実行する度にNは変わる。つまりNは定数ではなく変数 5int *min = (int*)malloc(sizeof(int) * N); // mallocで任意の大きさの領域を確保 6for (int i = 0; i < N; i++) { 7 min[i] = ...; // ポインターminを使ってもあたかも配列のように要素をアクセスできます 8}
  • 文字列の長さと個数

文字列は例えば"abc"であれば最小限、3文字+末尾のNUL文字の合計4個のchar要素を格納できる領域が必要です。また"abc","def"という2つの文字列を格納するには少なくとも4個のchar要素の領域が2セット必要です。しかしあなたのコード上では単に文字列の個数Nに対して要素数をN+1にしています。Nは文字列の個数であって長さではありませんしそもそも長さ×個数の領域が必要という点を配慮できていません。

長さがLの文字列をN個分格納するためには最小でも(L+1)xN個のchar要素が必要です。このような領域は2次元配列として表現したりポインターのポインターとして宣言したりといくつかのバリエーションがありますが、そのような領域を配列やポインターでどう書くべきかは、ある程度の学習が必要です。ここでは詳細は述べず例のみ挙げてみます。(いくつかの箇所ではintでなくsize_tを使うほうがよりよいコードと言えるかも知れませんが以下の例ではintとしています。)

文字列の最大長も文字列の個数も固定とし2次元配列にする場合

c

1#define MAXL 20 2#define N 10 3char town_names[N][MAXL + 1];

文字列の最大長は固定、文字列の個数は可変の場合

c

1#include <stdio.h> 2#include <malloc.h> 3 4#define MAXL 20 5 6typedef char TOWN_NAME[MAXL + 1]; 7 8int main() { 9 int N; 10 TOWN_NAME *town_names; 11 12 scanf("%d", &N); 13 town_names = (TOWN_NAME*)malloc(sizeof(TOWN_NAME) * N); 14 for (int i = 0; i < N; i++) { 15 scanf("%s", town_names[i]); 16 } 17 ... 18}

文字列の最大長も文字列の個数も可変としたい場合

c

1#include <stdio.h> 2#include <malloc.h> 3#include <string.h> 4 5// 個々の文字列の長さは可変とするがこの例では読み込みの際のバッファー長は固定としておく 6#define MAXL 20 7 8int main() { 9 int N; 10 char **town_names; 11 char buffer[MAXL + 1]; 12 13 scanf("%d", &N); 14 town_names = (char**)malloc(sizeof(char*) * N); 15 16 for (int i = 0; i < N; i++) { 17 scanf("%s", buffer); 18 int town_name_len = strlen(buffer); 19 town_names[i] = (char*)malloc(town_name_len + 1); 20 strcpy(town_names[i], buffer); 21 } 22 ... 23}

なお、C言語の仕様は時代の変遷によって変化しているので「ずっと昔から使える機能」と「現在でも一部のコンパイラーでしか使えない機能」が入り混じっています。こうしたことは他の言語でも同様なので回答の際には「何を前提にするか」が回答者の知識や主観に左右されることがあります。自分はVLAをこのサイトで教えていただくまで知らなかった(w;)のですが、知った後でも「多くのCコンパイラーでは使えるとしても基本としてまず知っておくべきことは何か」との観点からVLAでなくmallocを使う方法を述べ、「新しい仕様ではあるが推奨したい書き方」という考えからc99以上で利用できる「変数をブロックの先頭で宣言しなくても使う場所で宣言できる」仕様を前提に回答しています。(それ以外に自分でも気づかない前提を置いている箇所があるかも知れません。)

投稿2017/04/02 01:17

KSwordOfHaste

総合スコア18392

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

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

thoifon

2017/04/08 03:22

KSwordOfHaste様 ご回答ありがとうございます。 「mallocにより動的にメモリーを確保する」という方法を理解していませんでした。 配列の動的メモリの確保について勉強しなおしてみます。 回答の冒頭にもありましたように、今回の場合構造体を使った方が綺麗にまとまる気がします。 構造体を使った方法も考えてみたいと思います。
guest

0

char str[N+1]はたぶん間違いではないかと思われます。

ご参考まで

c

1#include <stdio.h> 2int main(void){ 3 int N; 4 scanf("%d", &N); 5 char str[N][256]; 6 int num[N]; 7 int i; 8 for (i=0; i<N; i++) { 9 scanf("%s %d", &str[i], &num[i]); 10 11 } 12 for (i=0; i<N; i++) { 13 printf("%s\n%d\n", str[i], num[i]); 14 } 15 return 0; 16}

投稿2017/04/02 01:15

A.Ichi

総合スコア4070

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

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

0

C

1#include <stdio.h> 2 3typedef struct _item item; 4struct _item { 5 char name[ 100 ]; 6 int num; 7}; 8 9int main( void ) { 10 int N; 11 char buf[ 1000 ] = { 0 }; 12 item* items; 13 14 fgets( buf, sizeof( buf ), stdin ); 15 sscanf( buf, "%d", &N ); 16 17 items = malloc( sizeof( item ) * N ); 18 if( !items ) { 19 memset( items, 0x00, sizeof( item ) * N ); 20 printf( "malloc err.\n" ); 21 return -1; 22 } 23 24 /* 取得 */ 25 for( int i = 0; i < N; i++ ) { 26 memset( buf, 0x00, sizeof( buf ) ); 27 fgets( buf, sizeof( buf ), stdin ); 28 sscanf( buf, "%s %d", items[ i ].name, &items[ i ].num ); 29 } 30 31 /* 表示 */ 32 for( int i = 0; i < N; i++ ) { 33 printf( "%s\n%d\n", items[ i ].name, items[ i ].num ); 34 } 35 36 free( items ); 37 return 0; 38}

投稿2017/04/02 00:34

otaks

総合スコア223

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問