teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

3

バグの修正とassertの追加

2016/05/06 15:03

投稿

raccy
raccy

スコア21782

answer CHANGED
@@ -96,6 +96,7 @@
96
96
  という制限があったのですね。struct data_allの構造を変えずにするとなると、普通に構造体や配列としてアクセスする方法では無理です。メモリを自分で確保して、直接計算してアクセスするしかないと思います。ということで、書き直しました。
97
97
 
98
98
  ```C
99
+ #include <assert.h>
99
100
  #include <stdio.h>
100
101
  #include <stdlib.h>
101
102
 
@@ -111,16 +112,18 @@
111
112
 
112
113
  struct data_all {
113
114
  int size;
114
- int mem_size;
115
+ size_t mem_size;
115
116
  void *mem;
116
117
  };
117
118
 
118
119
  struct data_all data_all_create(int size)
119
120
  {
121
+ assert(0 <= size);
120
122
  struct data_all dd;
121
123
  dd.size = size;
122
124
  dd.mem_size = sizeof(struct head) + sizeof(struct data) * size +
123
- sizeof(int) * 3 * 4 * size;
125
+ sizeof(int) * 3 * 4 * size;
126
+ assert(0 <= dd.mem_size);
124
127
  dd.mem = calloc(1, dd.mem_size);
125
128
  return dd;
126
129
  }
@@ -132,18 +135,27 @@
132
135
 
133
136
  struct head *data_all_head(struct data_all dd)
134
137
  {
138
+ assert(dd.mem != NULL);
135
139
  return (struct head *)dd.mem;
136
140
  }
137
141
  struct data *data_all_data(struct data_all dd, int n)
138
142
  {
143
+ assert(dd.mem != NULL);
144
+ assert(0 <= n && n < dd.size);
139
145
  return (struct data *)(dd.mem + sizeof(struct head) +
140
- sizeof(struct data) * n);
146
+ sizeof(struct data) * n);
141
147
  }
142
148
  // data[y][x][n]のこと
143
149
  int *data_all_value(struct data_all dd, int n, int x, int y)
144
150
  {
151
+ assert(dd.mem != NULL);
152
+ assert(0 <= n && n < dd.size);
153
+ assert(0 <= x && x < 4);
154
+ assert(0 <= y && y < 3);
145
- return (int *)(dd.mem + sizeof(struct data) * dd.size +
155
+ size_t forward = sizeof(struct head) + sizeof(struct data) * dd.size +
146
- sizeof(int) * (n + dd.size * x + dd.size * 4 * y));
156
+ sizeof(int) * (n + dd.size * x + dd.size * 4 * y);
157
+ assert(forward < dd.mem_size);
158
+ return (int *)(dd.mem + forward);
147
159
  }
148
160
  int data_all_get_value(struct data_all dd, int n, int x, int y)
149
161
  {
@@ -160,6 +172,10 @@
160
172
  {
161
173
  int rec_size = 100000;
162
174
  dd = data_all_create(rec_size);
175
+ if (dd.mem == NULL) {
176
+ fprintf(stderr, "Failed to allocate memory.\n");
177
+ return 1;
178
+ }
163
179
  // 既にあるデータ構造を入れる場合は、次のようにする。
164
180
  // memcpy(dd.mem, ○○, dd.mem_size);
165
181
 
@@ -173,18 +189,18 @@
173
189
  for (int x = 0; x < 4; x++) {
174
190
  for (int y = 0; y < 3; y++) {
175
191
  // data[y][x][n] = ...
176
- data_all_set_value(dd, n, x, y,
192
+ data_all_set_value(
177
- n * (y + 1) - x);
193
+ dd, n, x, y, n * (y + 1) - x);
178
194
  }
179
195
  }
180
196
  }
181
197
  printf("%d: %s\n", data_all_data(dd, 3)->no,
182
- data_all_data(dd, 3)->name);
198
+ data_all_data(dd, 3)->name);
183
199
  printf("%d: %s\n", data_all_data(dd, 99999)->no,
184
- data_all_data(dd, 99999)->name);
200
+ data_all_data(dd, 99999)->name);
185
- printf("%d\n", data_all_get_value(dd, 0, 0, 0));
201
+ printf("%d\n", data_all_get_value(dd, 0, 0, 0)); // 最小値
186
202
  printf("%d\n", data_all_get_value(dd, 42, 1, 1));
187
- printf("%d\n", data_all_get_value(dd, 99999, 2, 3));
203
+ printf("%d\n", data_all_get_value(dd, 99999, 3, 2)); // 最大値
188
204
  data_all_destroy(dd);
189
205
  return 0;
190
206
  }

2

2\.の条件が抜けていた。

2016/05/06 15:03

投稿

raccy
raccy

スコア21782

answer CHANGED
@@ -87,4 +87,106 @@
87
87
  Q: 構造体可変長配列(VLAIS)ってこれのこと?
88
88
  A: 違います。これまた別の機能です。
89
89
  Q: これってCの標準仕様?
90
- A: だと思ったけど、調べ切れてないです。わかる人は教えてください。
90
+ A: だと思ったけど、調べ切れてないです。わかる人は教えてください。
91
+
92
+ ---
93
+
94
+ > 2.入れ子の構造体の領域は、使用している独自処理のため、連続したメモリ領域である必要がある。
95
+
96
+ という制限があったのですね。struct data_allの構造を変えずにするとなると、普通に構造体や配列としてアクセスする方法では無理です。メモリを自分で確保して、直接計算してアクセスするしかないと思います。ということで、書き直しました。
97
+
98
+ ```C
99
+ #include <stdio.h>
100
+ #include <stdlib.h>
101
+
102
+ struct head {
103
+ int size;
104
+ char ver[10];
105
+ };
106
+
107
+ struct data {
108
+ int no;
109
+ char name[30];
110
+ };
111
+
112
+ struct data_all {
113
+ int size;
114
+ int mem_size;
115
+ void *mem;
116
+ };
117
+
118
+ struct data_all data_all_create(int size)
119
+ {
120
+ struct data_all dd;
121
+ dd.size = size;
122
+ dd.mem_size = sizeof(struct head) + sizeof(struct data) * size +
123
+ sizeof(int) * 3 * 4 * size;
124
+ dd.mem = calloc(1, dd.mem_size);
125
+ return dd;
126
+ }
127
+ void data_all_destroy(struct data_all dd)
128
+ {
129
+ free(dd.mem);
130
+ dd.mem = NULL;
131
+ }
132
+
133
+ struct head *data_all_head(struct data_all dd)
134
+ {
135
+ return (struct head *)dd.mem;
136
+ }
137
+ struct data *data_all_data(struct data_all dd, int n)
138
+ {
139
+ return (struct data *)(dd.mem + sizeof(struct head) +
140
+ sizeof(struct data) * n);
141
+ }
142
+ // data[y][x][n]のこと
143
+ int *data_all_value(struct data_all dd, int n, int x, int y)
144
+ {
145
+ return (int *)(dd.mem + sizeof(struct data) * dd.size +
146
+ sizeof(int) * (n + dd.size * x + dd.size * 4 * y));
147
+ }
148
+ int data_all_get_value(struct data_all dd, int n, int x, int y)
149
+ {
150
+ return *data_all_value(dd, n, x, y);
151
+ }
152
+ void data_all_set_value(struct data_all dd, int n, int x, int y, int value)
153
+ {
154
+ *data_all_value(dd, n, x, y) = value;
155
+ }
156
+
157
+ struct data_all dd; // 可変部分があるため静的に作ることはできない
158
+
159
+ int main(int argc, char *argv[])
160
+ {
161
+ int rec_size = 100000;
162
+ dd = data_all_create(rec_size);
163
+ // 既にあるデータ構造を入れる場合は、次のようにする。
164
+ // memcpy(dd.mem, ○○, dd.mem_size);
165
+
166
+ // ダミーで値を入れてみる
167
+ for (int n = 0; n < dd.size; n++) {
168
+ struct data *d = data_all_data(dd, n);
169
+ d->no = n;
170
+ sprintf(d->name, "%08d", n);
171
+ }
172
+ for (int n = 0; n < dd.size; n++) {
173
+ for (int x = 0; x < 4; x++) {
174
+ for (int y = 0; y < 3; y++) {
175
+ // data[y][x][n] = ...
176
+ data_all_set_value(dd, n, x, y,
177
+ n * (y + 1) - x);
178
+ }
179
+ }
180
+ }
181
+ printf("%d: %s\n", data_all_data(dd, 3)->no,
182
+ data_all_data(dd, 3)->name);
183
+ printf("%d: %s\n", data_all_data(dd, 99999)->no,
184
+ data_all_data(dd, 99999)->name);
185
+ printf("%d\n", data_all_get_value(dd, 0, 0, 0));
186
+ printf("%d\n", data_all_get_value(dd, 42, 1, 1));
187
+ printf("%d\n", data_all_get_value(dd, 99999, 2, 3));
188
+ data_all_destroy(dd);
189
+ return 0;
190
+ }
191
+ ```
192
+ C++でクラス作っているような感じで実装すればできるかなと思います。

1

Clangで試したことと、Q&AにVLAISを追加

2016/04/29 09:30

投稿

raccy
raccy

スコア21782

answer CHANGED
@@ -1,6 +1,6 @@
1
1
  頑張って、たぶん、こうしたいんだろうってのを実装してみました。
2
2
 
3
- 下記の機能を使っており、GCCでしか試していません。
3
+ 下記の機能を使っており、gcc(GCC 5.3.0)とclang(LLVM 7.3.0)でしか試していません。
4
4
  [Zero Length - Using the GNU Compiler Collection (GCC)](https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html)
5
5
  ただ、下記の情報もあるので、Visual C++でもいけると思います。
6
6
  [構造体の可変長配列](https://msdn.microsoft.com/ja-jp/library/b6fae073(v=vs.120).aspx)
@@ -77,12 +77,14 @@
77
77
 
78
78
  Q&A
79
79
  Q: `[]`でいいの?
80
- A: GCCとVCでは0サイズと見なして大丈夫のようです。ただ、それ一つだけだと構造体全体が0サイズになるため、他に何か必要になります。
80
+ A: GCCとVCでは0サイズと見なして大丈夫のようです。ただ、それ一つだけだと構造体全体が0サイズになるため、他に何か必要になります。`[1]`とする書き方もあるようですが、正式なドキュメントが見つけられませんでした。
81
81
  Q: `int value[3][4][]`とはできないの?
82
82
  A: できません。`int value[3][4][]`は「intが**可変個**ある型が4個ある型が3個ある型」となり、可変個部分が確定しないと最終的にアクセスできません。なので、`int value[][3][4]`として、「intが4個ある型が3個ある型が可変個ある型」にします。int[3][4]が可変個ある形になるので、いくつあるかわからなくても、n番目にアクセスできます。
83
83
  Q: 可変長部分は最後以外には書けないの?
84
84
  A: 書けません。メモリを確保できても、途中に可変長部分があるとがそこがどこで終わるのかわからなくなるからです。
85
85
  Q: 可変長配列(VLA)ってこれのこと?
86
86
  A: 違います。全く別の機能です。
87
+ Q: 構造体可変長配列(VLAIS)ってこれのこと?
88
+ A: 違います。これまた別の機能です。
87
89
  Q: これってCの標準仕様?
88
90
  A: だと思ったけど、調べ切れてないです。わかる人は教えてください。