char (*p)[10]の使い道
- 評価
- クリップ 2
- VIEW 3,630
char (*p)[10];
>>>char型の要素を10個持つ配列へのポインタ!
これの使い道がわかりません。
int (*arp)[10];
// アドレスを入れるときは以下のように入れます。
int array_ten[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
arp = &array_ten;
普通に、int *arp = array_ten; で良いような気がします。
わざわざ要素数が固定のポインタに渡す必要性が見えません。
「あ!ポインタのポインタの初期化に使えるかな?」と思い、以下のようにしてみました。
char buf[3][100] = {
"nononono!!",
"I love you!",
"Thanks"
};
char **p;
// char *q[3] = {buf[0], buf[1], buf[2]}; こっちは成功
char (*q)[100] = buf;
p = &q;
これをやると、warningが出てきて、さらに落ちます・・・・
なぜかわかりませんが・・・
char (*p)[10];
関数に2次元配列を渡す時と、列を動的確保したい時ぐらいしか、出番がないような気がするんですが・・・・
char (*p)[10];
この子の他の使い方はあります??
プログラムの例を見ているとよく出てきます。
printf("p[1] : %p, p[1][0] : %p\n", p[1], &p[1][0]);
おまけに2次元配列のようにアクセスもできています・・・・!?
正直、よくわかってないです。
どなたか教えてください!
[追記]
char array[4][10]
char (*arp)[10] = array;
たとえば、こうなった場合・・・
arpという1つのポインタが4つの配列の先頭アドレスを指しているということですよね?
・・・・??
なんかおかしくないですかね?
たった1つしかないポインタが同時に4つのアドレスを指しているなんて・・・
どういうことですか??
[追記2]
ポインタのポインタの初期化について、考察してもらっている方もいらっしゃるのですが、
char array[4][10]
char (*arp)[10] = array; ← arpはポインタで要素数4つの配列の先頭アドレスを持っていると思います。
つまり・・・・・一次元配列・・・ということになりますよね??
arp[0], arp[1] ・・というのが、要素数10個ある配列の先頭を表すことで、あたかも2次元配列のようになる
と解釈しました。
しかし、
char ii[4] = "GHYU";
char *qq = ii;
char **pp = &qq; // char *pp = &qq; これは無理!
これは、問題がありません。
ポインタのポインタを初期化できています。
char (*arp)[10]; はこれに似ていと思います。
あたかも「char ii[4] = "GHYU";」+「char *qq = ii;」これを1つにしたような感じです。
G, H, Y, U が要素数10個配列の先頭の文字だと想像してください。
なのに、最初の質問のように・・・
char (*q)[100] = buf;
p = &q
これができない理由が・・・見当たらないです。
「データ構造が違うから初期化できない」
上記のプログラムができているので、むしろ初期化できないとおかしいような気がします。
[修正]
大変申し訳ありません。
int型とchar型を混合してしまいました。
修正しまいした。
すべてchar型です。
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
+2
前の質問で
char buf[3][100] = {
"you will make me happy",
"I love you!",
"Thanks"
};
char **p;
char *q[3] = {buf[0], buf[1], buf[2]};
p = q;
char **p = buf; こんな事をしたいというのがありましたが
char (*p)[100]=buf; こんな風に使います。
int main() {
char buf[3][100] = {
"you will make me happy",
"I love you!",
"Thanks"
};
char(*p)[100] = buf;
printf("%s\n", p[0]);
printf("%s\n", p[1]);
printf("%s\n", p[2]);
return 0;
}
ネットなどの多くの解説で「配列とポインタはよく似ている」「配列でもポインタでも同じ処理を書ける」などの文章から感覚が同じものの様になっているのではないでしょうか。
その結果が
char **p = buf;
2次元配列をダブルポインタに入れようとしていることではないかと思ってしまいました。
配列とポインタは相互に入れ替えできる部分はあっても別物です。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
+2
こんにちは。
普通に、int *arp = array_ten; で良いような気がします。
私もそう思います。
折角頑張って要素数を付けても、C/C++では直ぐに要素数が抜け落ちて単なる要素へのポインタに成り下がってしまいますので、コンパイル・エラーの元になりがちです。
C++で特殊なケースで使ったことありますが、上記問題で頭が痛かったことがありました。
printf("p[1] : %p, p[1][0] : %p\n", p[1], &p[1][0]);
おまけに2次元配列のようにアクセスもできています・・・・!?
pは「char型10個」へのポインタです。ポインタは配列に読み替えることができますね。なので、p[1]はp[0]の「次」の「char型10個」の要素です。
「char型10個」は10バイトですからp[0]のアドレスに+10したらp[1]のアドレスになります。
まじで解りにくいですね。可能な場合はstruct element { char data[10]; };
などとして、element* p;
と定義した方が読みやすいし、バグりにくいと思います。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
+2
int型は4バイト、int[10]型は40バイトだから
int型のポインタはポインタ演算で4byte進もうとするけど
int[10]型のポインタは40byte進もうとするんだ
#include <iostream>
using namespace std;
int main() {
int *singlep = 0;
int (*arrayp)[10] = 0;
cout << (unsigned int)singlep << endl << (unsigned int)(singlep+1) << endl;
cout << (unsigned int)arrayp << endl << (unsigned int)(arrayp+1) << endl;
return 0;
}
この小さなコードを実行してみて欲しい。
こんな感じで結果がでるはずだ。
0
4
0
40
ポインタにも細かい型の違いがあるんだよ
ポインタと配列が違うっていう人もいるけど、この動きは配列の時でも何も変わらない
#include <iostream>
using namespace std;
int main() {
int array[2][10] = 0;
cout << (unsigned int)array << endl << (unsigned int)(array+1) << endl;
return 0;
}
> 4290089120
> 4290089160
追記
ポインタはアドレスの先頭を指している。一つのint型は4バイト使うので、もしも隣接する要素もint型なら次の4バイトは4バイト先のアドレスから始まる。型の大きさはプログラミングする上で重要な要素なんだ。受け取ったポインタが配列だった場合、次の要素の位置を簡単に指し示すことができるからね。
#include <iostream>
using namespace std;
int main() {
char str[12];
char *p = str;
*(p+0) = 'H';
*(p+1) = 'E';
*(p+2) = 'L';
*(p+3) = 'L';
*(p+4) = 'O';
*(p+5) = ' ';
*(p+6) = 'W';
*(p+7) = 'O';
*(p+8) = 'R';
*(p+9) = 'L';
*(p+10) = 'D';
*(p+11) = '\0';
cout << str;
return 0;
}
> HELLO WORLD
#include <iostream>
using namespace std;
int main() {
char str[3][100] = { "Line1", "Line2", "Line3" };
char *p = (char *)str;
cout << p << endl << p+1 << endl;
p = (char*)((int)p + 100);
cout << p << endl << endl;
char (*arrayp)[100] = str;
cout << *arrayp << endl << *(arrayp+1) << endl;
return 0;
}
> Line1
> ine1
> Line2
> Line1
> Line2
型情報には、アドレスをどうシフトさせたら次の要素が参照できるのかが得られるだけの差別化がある。
**
ポインタのポインタは4バイトシフトするポインタだが、要素数100のchar型の配列のポインタは100バイトシフトするポインタなわけで、全然別ものなんだよね。
#include <iostream>
using namespace std;
int main() {
char *p = 0;
char **pp = 0;
char (*ap)[100] = 0;
cout << (unsigned int)(p+1) << endl;
cout << (unsigned int)(pp+1) << endl;
cout << (unsigned int)(ap+1) << endl;
return 0;
}
> 1
> 4
> 100
更に追記
どんな時に使うのかが質問だった。
まず、char **p
とchar (*p)[100]
の違いはなんですかって質問を見つめなおそう。
こいつはね、ポインタのポインタ
と配列のポインタ
の違いを聞いてる。
これはもっと抽象化すると、ポインタのポインタ
とある型のポインタ
の違いを聞いてる。
これがわかるかどうかはstrike君次第だけど…
記法よりも、何をしているのかに着目してほしい。
これはプログラミングっていうより国語の問題だ。
ポインタのポインタ
とある型のポインタ
は根本的に別物だよ。
char **p
とint *p
の違いはなんですかって言ってるのと本質的には大差無い。
ある型のポインタ
の型
には配列の型
も指定できるんだよ。それがchar (*p)[100]
って記法だ。
だから、あるポインタをある配列のポインタに変換することもできる。
char c[100*5];
char (*ap)[100] = (char(*)[100])c;
char a[5][100];
char *cp = (char*)a;
このコードはコンパイル通るし、お互いの範囲内で使う分には安全だよ。
cもaも変数としては連続した500バイトのメモリアドレスを確保していてどのようにアクセスするかが違うだけだ。
下記は今じゃまず使わないけどこのサンプルで配列型のポインタの使い方を示しているつもりだ。
#include <iostream>
using namespace std;
void char100print(char (*str)[100], int length)
{
for (int i = 0; i < length; i++)
{
cout << *str++ << endl;
}
}
int main() {
char value[][100] = {"test1", "test2", "test3", "test4"};
char100print(value, 3);
return 0;
}
> test1
> test2
> test3
より具体的なサンプルも示そう。
昔は固定長のファイルが多かった。
今でも現役の固定長フォーマットに全銀協制定フォーマットがある。
銀行でお金を振り込んでくださいってことをするファイルだ。
これは120バイト(固定)を1行として使うファイルなんだ。
ファイルの読み書きは行単位ではなく、120バイト単位にしなきゃならない。
ZENGIN ReadZenginFormat(const char *p, int record_num);
ZENGIN ReadZenginFormat(const char (*p)[120], int record_num);
このサンプルは上下で情報量が全然違うよね。
でも動作原理はまったく一緒だし、読み取るための関数だから参照先アドレスを編集することがないのでconst
属性がついてる。
record_num=5の場合、pはとあるアドレスを参照しており、そこから連続した600バイトのメモリが確保されている。
一方、以下は全く意味が異なる。
ZENGIN ReadZenginFormat(const char **p, int record_num);
pは…ポインタのポインタの配列なんだろう。読み取り専用だしね。
でもそうなると、ポインタそのものは5行であったとしたら、pのアドレスから確保されているバイト数はわずか4*5バイト=20バイトだ。
その20バイトの中に、5つの文字列のポインタが入っているわけだ。
使い方を示すとこうだ。
char *p[5];
p[0] = malloc(120);
p[1] = malloc(120);
p[2] = malloc(120);
p[3] = malloc(120);
p[4] = malloc(120);
ReadZenginFormat(p, 5);
ポインタそれぞれに別の文字列の参照を持つ。
p[0]からp[4]で参照しているメモリアドレスは別に連続したメモリ空間じゃない。
一旦次のポインタに移動して、そこから別の文字列を参照する。
これは配列のポインタ
とは構造が全く異なるんだよ。
ポインタのポインタ
と配列のポインタ
には互換性が無いんだ。
互換させる場合はこうなる。
char a[5][120];
char *p[5];
p[0] = a[0];
p[1] = a[1];
p[2] = a[2];
p[3] = a[3];
p[4] = a[4];
ReadZenginFormat(p, 5);
わかるかな。
a[0]は先頭アドレスを指している。
a[1]はその120バイト先のアドレスを指しているが、a[1]には大きさがあるわけではない。
a[1]で指示される場所が120バイト先のアドレスというだけだ。
一方、p[1]はアドレスを保存するための変数として4バイト確保されている。
そこに代入している。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
+1
intと同じでよろしければ
int (*arp)[10];
int array_ten[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
arp = &array_ten;
printf("%d \n",(*arp+1)[2]);
char buf[3][100] = {
"nononono!!",
"I love you!",
"Thanks"
};
char (*q)[3][100];
q = &buf;
printf("%s \n",(*q)[1]);
char **p = (char **)(*q)[1];
printf("%s \n",p);
int (*arp)[10];arp1は、intの配列(要素10)へのポインタなので、代入の場合キャスト(int (*)[10]) intの配列(要素数10)でポインタに変換する。
int main() {
char ii[4] = "GHYU";
char *qq = ii;
char **pp = &qq;
int array[4][10]={{1,2,3,4,5,6,7,8,9,0},{10,11,12,13,14,15,16,17,18,19}};
int (*arp)[4][10] = (int (*)[4][10])array;
printf("%d \n", (*arp)[0][5]);
}
実はこの方のページ を参考とさせて頂いています。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
+1
これをやると、warningが出てきて、さらに落ちます・・・・
なぜかわかりませんが・・・
手元のGCCでコンパイルするとp = q;
の行で、
"warning: assignment from incompatible pointer type ..." となりました。「pとqでは互換性が無い(?)のに、代入して大丈夫なのか?」という警告です。要するに「型が違う」のだから、warningを消す手立ては、まずキャストでしょう。即ち、
char **p;
p = (char**)q; // キャストする
これでwarningが消えますが、手立ては他にもあります。ポインタpの宣言を変更すれば良い。即ち、
char (*p)[100]; // p を、こう宣言する
p = q; // キャスト不要
以上は、char **
とchar (*)[100]
を、コンパイラは型として扱っている事、しかも明確に異なる型だと区別している事を示しています。
char **
は、ポイントしてる先のcharが、1個なのか10個なのか、それとも100個なのか、サイズの指定はありません。
それに対して、char (*)[100]
は、個々の要素は同じくcharだけど、「100個のcharをひとかたまり(100バイトを一つのデータ型)と見るポインタ」だという宣言です。こちらにはサイズの指定があるので、区別がつきます。
一般的に言えば、「固定長の配列」を一つの型として扱える…そう考えれば、そのようなデータ構造は、決して珍しくないはずですから、そうした管理がふさわしければ使う価値があると思います。ただ実際は、通常の(サイズ指定をしない)ポインタでアクセスも操作も可能なので、いまいち出番が少ないかもしれませんね。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
具体的にどんな時に使うかは人それぞれだと思いますが、
charポインタを10個ほしい時に使います。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
ポインターのポインターでありさえすればいいのであれば
int **argp;
でよいわけですが、「10要素の配列へのポインター」であることを明確にしたいときに有用です。
int (*argp)[10];
と宣言することで10要素でない配列へのポインターを代入しようとしたとき(つまりバグ)コンパラーが警告を発してくれます。
int b[9];
argp = &b; // <= 警告: 互換性のないポインタ型からの代入です
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
たとえば列数固定の行列を確保したいとき。
// N行10列の行列を確保する
float (*p)[10];
p = (float (*)[10])malloc(N * 10 * sizeof(float));
... N行10列の行列 p[i][j] として使う
free(p);
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
やはり、char (*p)[10];
1、列の動的確保
2、2次元配列で初期化できる → 関数に2次元配列を渡す!
この2つくらいしか、使い道が思いつきません・・・です。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.13%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2017/01/23 12:43
これは2次元配列で初期化できるよ。
ということですよね。
2017/01/23 12:47
char *q[3] これより、char (*q)[3]の方が簡単にできますね!!
2017/01/23 15:55 編集
>arpという1つのポインタが4つの配列の先頭アドレスを指しているということですよね?
>たった1つしかないポインタが同時に4つのアドレスを指しているなんて・・・
いいえ違います。4つの要素を持つ配列の先頭アドレスを指しています。
そしてこの場合、その4つの要素は配列です。
arp[0]はint型の要素を10個持つ配列の先頭アドレスを
arp[1]は次のint型要素を10個持つ配列の先頭アドレスを指します。。。
(*p)[]の記述が配列へのポインタというのはそういう事です。
2017/01/23 16:11
ほうほう!
ふむふむ・・・
なるほど。
分かりやすいです!
2017/01/24 19:52
Cのコードによっては同じものを吐き出すでしょう。
だからポインタと配列が同じなら[][]を**に置き換えて何が悪いのという質問者の感覚は理解できます。
しかし、Cのコード上でポインタと配列は少し扱いが違います。
Cはプログラマがやりたい事を邪魔しない思想の言語ですから、こんなこともできるのですが。。。
#define PSIZE 4
int main() {
char buf[3][100] = {
"you will make me happy",
"I love you!",
"Thanks"
};
char(*p)[100] = buf;
printf("%s\n", p[0]);
printf("%s\n", p[1]);
printf("%s\n", p[2]);
char **qq = (char**)buf;//サイズが違うが。。。
printf("%s\n", qq);
printf("%s\n", qq+((100/PSIZE)*1));//分かっているなら。。。
printf("%s\n", qq+((100/PSIZE)*2));
return 0;
}
こんな事を推奨しているわけではなく、勉強のためのコードです。
配列は配列らしく、ポインタはポインタらしく使うのがいいと思っているので。
2017/01/24 20:07
char **qq = (char**)buf;
これってできるんですね!!
ありがとうございます!
2017/01/24 20:25
これは、warningが出現します。
実行はできていますね!