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

回答編集履歴

3

誤字

2017/01/24 11:15

投稿

haru666
haru666

スコア1593

answer CHANGED
@@ -231,5 +231,5 @@
231
231
  a[0]は先頭アドレスを指している。
232
232
  a[1]はその120バイト先のアドレスを指しているが、a[1]には大きさがあるわけではない。
233
233
  a[1]で指示される場所が120バイト先のアドレスというだけだ。
234
- 一方、p[1]はアドレスを保するための変数として4バイト確保されている。
234
+ 一方、p[1]はアドレスを保するための変数として4バイト確保されている。
235
235
  そこに代入している。

2

更に追記

2017/01/24 11:15

投稿

haru666
haru666

スコア1593

answer CHANGED
@@ -117,4 +117,119 @@
117
117
  > 1
118
118
  > 4
119
119
  > 100
120
- ```
120
+ ```
121
+
122
+ ---
123
+ 更に追記
124
+
125
+ どんな時に使うのかが質問だった。
126
+
127
+ まず、`char **p`と`char (*p)[100]`の違いはなんですかって質問を見つめなおそう。
128
+ こいつはね、`ポインタのポインタ`と`配列のポインタ`の違いを聞いてる。
129
+ これはもっと抽象化すると、`ポインタのポインタ`と`ある型のポインタ`の違いを聞いてる。
130
+ これがわかるかどうかはstrike君次第だけど…
131
+ 記法よりも、何をしているのかに着目してほしい。
132
+ これはプログラミングっていうより国語の問題だ。
133
+
134
+ `ポインタのポインタ`と`ある型のポインタ`は根本的に別物だよ。
135
+ `char **p`と`int *p`の違いはなんですかって言ってるのと本質的には大差無い。
136
+
137
+ `ある型のポインタ`の`型`には`配列の型`も指定できるんだよ。それが`char (*p)[100]`って記法だ。
138
+ だから、あるポインタをある配列のポインタに変換することもできる。
139
+ ```C++
140
+ char c[100*5];
141
+ char (*ap)[100] = (char(*)[100])c;
142
+
143
+ char a[5][100];
144
+ char *cp = (char*)a;
145
+ ```
146
+ このコードはコンパイル通るし、お互いの範囲内で使う分には安全だよ。
147
+ cもaも変数としては連続した500バイトのメモリアドレスを確保していてどのようにアクセスするかが違うだけだ。
148
+
149
+ 下記は今じゃまず使わないけどこのサンプルで配列型のポインタの使い方を示しているつもりだ。
150
+ ```C++
151
+ #include <iostream>
152
+ using namespace std;
153
+
154
+ void char100print(char (*str)[100], int length)
155
+ {
156
+ for (int i = 0; i < length; i++)
157
+ {
158
+ cout << *str++ << endl;
159
+ }
160
+ }
161
+
162
+ int main() {
163
+ char value[][100] = {"test1", "test2", "test3", "test4"};
164
+
165
+ char100print(value, 3);
166
+
167
+ return 0;
168
+ }
169
+
170
+ > test1
171
+ > test2
172
+ > test3
173
+ ```
174
+
175
+ より具体的なサンプルも示そう。
176
+ 昔は固定長のファイルが多かった。
177
+ 今でも現役の固定長フォーマットに全銀協制定フォーマットがある。
178
+ 銀行でお金を振り込んでくださいってことをするファイルだ。
179
+ これは120バイト(固定)を1行として使うファイルなんだ。
180
+ ファイルの読み書きは行単位ではなく、120バイト単位にしなきゃならない。
181
+
182
+ ```C++
183
+ ZENGIN ReadZenginFormat(const char *p, int record_num);
184
+
185
+ ZENGIN ReadZenginFormat(const char (*p)[120], int record_num);
186
+ ```
187
+
188
+ このサンプルは上下で情報量が全然違うよね。
189
+ でも動作原理はまったく一緒だし、読み取るための関数だから参照先アドレスを編集することがないので`const`属性がついてる。
190
+ record_num=5の場合、pはとあるアドレスを参照しており、そこから連続した600バイトのメモリが確保されている。
191
+
192
+ 一方、以下は全く意味が異なる。
193
+ ```C++
194
+ ZENGIN ReadZenginFormat(const char **p, int record_num);
195
+ ```
196
+ pは…ポインタのポインタの配列なんだろう。読み取り専用だしね。
197
+ でもそうなると、ポインタそのものは5行であったとしたら、pのアドレスから確保されているバイト数はわずか4*5バイト=20バイトだ。
198
+ その20バイトの中に、5つの文字列のポインタが入っているわけだ。
199
+
200
+ 使い方を示すとこうだ。
201
+ ```C++
202
+ char *p[5];
203
+ p[0] = malloc(120);
204
+ p[1] = malloc(120);
205
+ p[2] = malloc(120);
206
+ p[3] = malloc(120);
207
+ p[4] = malloc(120);
208
+
209
+ ReadZenginFormat(p, 5);
210
+ ```
211
+
212
+ ポインタそれぞれに別の文字列の参照を持つ。
213
+ p[0]からp[4]で参照しているメモリアドレスは別に連続したメモリ空間じゃない。
214
+ 一旦次のポインタに移動して、そこから別の文字列を参照する。
215
+ これは`配列のポインタ`とは構造が全く異なるんだよ。
216
+
217
+ `ポインタのポインタ`と`配列のポインタ`には互換性が無いんだ。
218
+ 互換させる場合はこうなる。
219
+ ```C++
220
+ char a[5][120];
221
+ char *p[5];
222
+ p[0] = a[0];
223
+ p[1] = a[1];
224
+ p[2] = a[2];
225
+ p[3] = a[3];
226
+ p[4] = a[4];
227
+
228
+ ReadZenginFormat(p, 5);
229
+ ```
230
+ わかるかな。
231
+ a[0]は先頭アドレスを指している。
232
+ a[1]はその120バイト先のアドレスを指しているが、a[1]には大きさがあるわけではない。
233
+ a[1]で指示される場所が120バイト先のアドレスというだけだ。
234
+ 一方、p[1]はアドレスを確保するための変数として4バイト確保されている。
235
+ そこに代入している。

1

追記

2017/01/24 11:10

投稿

haru666
haru666

スコア1593

answer CHANGED
@@ -38,4 +38,83 @@
38
38
 
39
39
  > 4290089120
40
40
  > 4290089160
41
+ ```
42
+
43
+ 追記
44
+
45
+ ---
46
+
47
+ ポインタはアドレスの先頭を指している。一つのint型は4バイト使うので、もしも隣接する要素もint型なら次の4バイトは4バイト先のアドレスから始まる。型の大きさはプログラミングする上で重要な要素なんだ。受け取ったポインタが配列だった場合、次の要素の位置を簡単に指し示すことができるからね。
48
+
49
+ ```C++
50
+ #include <iostream>
51
+ using namespace std;
52
+
53
+ int main() {
54
+ char str[12];
55
+ char *p = str;
56
+ *(p+0) = 'H';
57
+ *(p+1) = 'E';
58
+ *(p+2) = 'L';
59
+ *(p+3) = 'L';
60
+ *(p+4) = 'O';
61
+ *(p+5) = ' ';
62
+ *(p+6) = 'W';
63
+ *(p+7) = 'O';
64
+ *(p+8) = 'R';
65
+ *(p+9) = 'L';
66
+ *(p+10) = 'D';
67
+ *(p+11) = '\0';
68
+ cout << str;
69
+ return 0;
70
+ }
71
+
72
+ > HELLO WORLD
73
+ ```
74
+ ```C++
75
+ #include <iostream>
76
+ using namespace std;
77
+
78
+ int main() {
79
+ char str[3][100] = { "Line1", "Line2", "Line3" };
80
+ char *p = (char *)str;
81
+ cout << p << endl << p+1 << endl;
82
+
83
+ p = (char*)((int)p + 100);
84
+ cout << p << endl << endl;
85
+
86
+ char (*arrayp)[100] = str;
87
+ cout << *arrayp << endl << *(arrayp+1) << endl;
88
+
89
+ return 0;
90
+ }
91
+
92
+ > Line1
93
+ > ine1
94
+ > Line2
95
+
96
+ > Line1
97
+ > Line2
98
+ ```
99
+ 型情報には、アドレスをどうシフトさせたら次の要素が参照できるのかが得られるだけの差別化がある。
100
+ `**`ポインタのポインタは4バイトシフトするポインタだが、要素数100のchar型の配列のポインタは100バイトシフトするポインタなわけで、全然別ものなんだよね。
101
+
102
+ ```C++
103
+ #include <iostream>
104
+ using namespace std;
105
+
106
+ int main() {
107
+ char *p = 0;
108
+ char **pp = 0;
109
+ char (*ap)[100] = 0;
110
+ cout << (unsigned int)(p+1) << endl;
111
+ cout << (unsigned int)(pp+1) << endl;
112
+ cout << (unsigned int)(ap+1) << endl;
113
+
114
+ return 0;
115
+ }
116
+
117
+ > 1
118
+ > 4
119
+ > 100
41
120
  ```