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

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

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

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

Q&A

解決済

3回答

11628閲覧

構造体の配列の特定のメンバだけの配列を出力したい

sakakendo0321

総合スコア45

C

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

0グッド

0クリップ

投稿2016/12/16 12:37

編集2016/12/17 02:48

###前提・実現したいこと
cで構造体の配列の特定のメンバだけの配列を出力する

###該当のソースコード
話が2転3転して申し訳ないです下の

「簡単に」を明確にしてくださいについて

で話を整理しました

c

1#include <stdio.h> 2 3typedef struct { 4 char c[128]; 5 int val; 6}str; 7 8int main(void){ 9 // Here your code ! 10 str a[10]={ 11 {"a",0}, 12 {"b",1}, 13 {"c",2}, 14 {"d",3}, 15 {"e",4} 16 }; 17} 18

例えば上のコードで言うと

c

1printf("%s",string); 2for(int i=0;i<5;i++){ 3 printf("%d",n[i]); 4}

すればabcde01234が出力されるようにしたいです

c

1for (int i=0;i<5;i++){ 2 sprintf(string,"%s",a[i].c); 3 sprintf(n[i],"%d",a[i].val); 4}

だとメンバが増えた時にそれだけsprintf()を呼ばなければならないのでもっと簡単にする方法が知りたいです

#「簡単に」を明確にしてくださいについて

c

1typedef struct{ 2 char c1[128]; 3 char c2[128]; 4 char c3[128]; 5 char c4[128]; 6 char c5[128]; 7 8}str; 9str a[3]={ 10 {"H","e","l","l","o"}, 11 {"W","o","r","l","d"}, 12 {"A","I","U","E","O"}, 13}; 14for(int i=0;i<4;i++){ 15 sprintf(string1[i],"%s",a[i].c1); 16 sprintf(string2[i],"%s",a[i].c2); 17 sprintf(string3[i],"%s",a[i].c3); 18 sprintf(string4[i],"%s",a[i].c4); 19 sprintf(string5[i],"%s",a[i].c5); 20}

のようにそれぞれのメンバをそれぞれの用意した配列に入れたいのですがこれだと
c1,c2,c3,c4,c5それぞれのためにsprintfを呼びださなければいけなく、またメンバにc6 ,c7,...と増えた時にそれだけ呼び出すのが煩わしいのでそれをまとめて短く記述したいということです。
メンバにint,double等他の型が追加された時にもまとめる方法が知りたいです.
構造体のポインタを用いて記述できるような気がしたのですが、自分では検討もつかず、調べても出てこなかったので
よろしくお願いします

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

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

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

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

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

yohhoy

2016/12/16 13:33 編集

望む出力がどのような要件なのかを明確にしてください。あなたの望む「簡単に」とはどういうニュアンスでしょうか?
guest

回答3

0

こんにちは。

だとメンバが増えた時にそれだけsprintf()を呼ばなければならないのでもっと簡単にする方法が知りたいです

C/C++において、コンパイラは構造体のメンバのリストを把握していますが、残念ながらプログラムがそのリストにアクセスできないため、メンバ全てを自動的に処理することは事実上できません。

C++11ならちょっとだけ楽できます。

C++

1#include <stdio.h> 2 3// ------------------- ここから ----------------------------- 4#include <sstream> 5#include <string> 6 7template<typename tArray, typename tStruct, typename tType> 8char const* output(tArray const& iArray, tType tStruct::*iPtr) 9{ 10 std::stringstream ss; 11 for(auto&& i : iArray) 12 { 13 ss << i.*iPtr; 14 } 15 static std::string ret; 16 ret=ss.str(); 17 return ret.c_str(); 18} 19// ------------------- ここまではC++です -------------------- 20 21typedef struct { 22 char c[128]; 23 int val; 24}str; 25 26int main(void){ 27 // Here your code ! 28 str a[]={ 29 {"a",0}, 30 {"b",1}, 31 {"c",2}, 32 {"d",3}, 33 {"e",4} 34 }; 35 36 printf("%s", output(a, &str::c)); 37 printf("%s", output(a, &str::val)); 38 39 return 0; 40}

これでabcde01234が出力されます。
メンバが増えてもoutput()関数を修正しないで済みますので、ちょっとだけ楽です。

でも、できるだけC言語の範疇で書いたのですが、output関数はC言語では書けません。C++は便利だよって言いたかっただけです。ごめんなさい。

&str::cメンバ変数へのポインタです。私はこれをC言語で使ったことはないですが、たぶんC言語にもあると思います。

投稿2016/12/16 15:20

編集2016/12/16 15:25
Chironian

総合スコア23272

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

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

sakakendo0321

2016/12/17 03:37

c++を書いたことがないのでもoutput関数の中身が理解できません 関数内部の動作を教えてください
Chironian

2016/12/17 15:22

outputはパラメータを2つ持ってます。iArrayとiPtrです。 output(a, &str::c)にて、iArrayには配列a、iPtrには構造体strのメンバcへのメンバ・ポインタを渡してます。 先頭にあるtemplate<>は型を事前に決めないで済ますための書き方です。テンプレート(雛形)と呼ばれています。 iArrayはtArray型ですが、tArray型がどんな型なのか事前に決めていないわけです。 output(a, &str::c);などのように実際に呼び出した時に、aの型であるint[5]型であると決まります。 tStructやtTypeも同じです。outputの第二引数に渡す変数が、tStruct構造体のメンバ・ポインタでその型がtTypeであると指定しています。 output(a, &str::c);などのように実際に呼び出したときに、tStructがstrであると決まり、tTypeがstr::cの型であるint型であると決まります。 これらはコンパイラがよしなに決めてくれます。この仕組は型推論と呼ばれてます。 for(auto&& i : iArray)は、for(size_t j=0; j < sizeof(iArray)/sizeof(iArray[0]); ++j) {str* i=&iArray[j]; ... }とほぼ同様です。ただし、iはポインタではなくある種の参照になってます。この場合の参照はポインタとほぼ同じ意味で捉えて良いです。メンバ・アクセスを->ではなく、.(ドット)で行う点が違うだけです。 std::stringstreamは、一種のsprintf()のようなものです。<<演算子を使って変数の値を文字列へ変換します。送り先の文字列の記憶領域はstd::stringstreamがよしなに管理してくれるので気にしなくてよいです。また、文字列へ変換したい変数の型に応じてよしなに文字列へ変換してくれるので書式指定する必要がありません。 std::stringは記憶領域をよしなに管理してくれる文字列です。char*では自分でメモリ管理しないといけないので面倒ですが、その面倒を引き受けてくれます。 std::string ret=ss.str();にて、std::stringstreamであるssが保持する文字列を取り出して、retへ代入してます。 このretが保持する文字列をc_str()メンバ関数で取り出すとchar*型になります。(本当はchar const*なのですが、constについては今は気にしないで下さい。) std::stringをoutput関数内のローカル変数として定義するとoutput関数が終了するこ時にstd::stringがなくなるため、同時にそれが確保していた文字列を記録する領域も開放されます。 それでは呼び出し元で返却された文字列をアクセスできませんから、static変数としてoutput関数終了後も開放されないようにしています。
guest

0

ベストアンサー

Chironianさんが言うとおり、構造体のメンバーを列挙する方法はありません。ということで、構造体のメンバーを指定すること自体は避けられません。

ということでマクロを駆使して頑張ってみました。

C

1#include <stdio.h> 2#include <string.h> 3 4#define printf_struct(type, st, format, ...) \ 5 do { \ 6 type *_ = &(st); \ 7 printf(format, ##__VA_ARGS__); \ 8 } while (0); 9 10#define printf_struct_array(type, arr, len, fmt, ...) \ 11 for (int i = 0; i < (len); i++) { \ 12 printf_struct(type, arr[i], fmt, ##__VA_ARGS__); \ 13 } 14 15typedef struct { 16 char c[128]; 17 int val; 18} str; 19 20int main(void) 21{ 22 str a[5] = {{"a", 0}, {"b", 1}, {"c", 2}, {"d", 3}, {"e", 4}}; 23 size_t a_len = sizeof(a) / sizeof(str); 24 printf_struct_array(str, a, a_len, "%s", _->c); 25 printf("\n"); 26 printf_struct_array(str, a, a_len, "%d", _->val); 27 printf("\n"); 28 printf_struct_array(str, a, a_len, "%s, %d\n", _->c, _->val); 29 return 0; 30}

_マジック変数(いま、私がそう名付けました)です。実際に何か宣言しておく変数では無く、アクセス用の変数と言うことです。Scalaの_とかPerlやRubyの$_みたいななんかすごそうな特殊変数だと思ってください。_.cみたいなものも考えたんですが、C++みたいに参照とかないので、構造体を毎回全部コピーするのは遅すぎるだろと言うことでポインタからのアクセスにしました。

マクロ内で_変数を作成するため、構造体の型指定が必須です。しかし、GNU拡張のtypeofを使えば、型指定が不要になります。

C

1// GNU拡張を使用しているため、GCCやClangで-std=gnu11でコンパイルしてください。 2#include <stdio.h> 3#include <string.h> 4 5#define printf_struct(st, format, ...) \ 6 do { \ 7 typeof(st) *_ = &(st); \ 8 printf(format, ##__VA_ARGS__); \ 9 } while (0); 10 11#define printf_struct_array(arr, len, fmt, ...) \ 12 for (int i = 0; i < (len); i++) { \ 13 printf_struct(arr[i], fmt, ##__VA_ARGS__); \ 14 } 15 16typedef struct { 17 char c[128]; 18 int val; 19} str; 20 21int main(void) 22{ 23 str a[5] = {{"a", 0}, {"b", 1}, {"c", 2}, {"d", 3}, {"e", 4}}; 24 size_t a_len = sizeof(a) / sizeof(str); 25 printf_struct_array(a, a_len, "%s", _->c); 26 printf("\n"); 27 printf_struct_array(a, a_len, "%d", _->val); 28 printf("\n"); 29 printf_struct_array(a, a_len, "%s, %d\n", _->c, _->val); 30 return 0; 31}

これを標準仕様のみで実現するにはテンプレートやdecltype()、型推論等C++にしか無い機能でしか実現できないと思います。

投稿2016/12/17 04:39

raccy

総合スコア21735

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

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

0

質問が高頻度で更新されるため回答不可能です。一旦取り下げます。


(質問者の要件がよくわかりません)

c

1for (int i=0; i<5; i++) { 2 printf("%s%d", a[i].c, a[i].val); 3}

投稿2016/12/16 13:03

編集2016/12/16 13:36
yohhoy

総合スコア6191

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問