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

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

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

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

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

9回答

5613閲覧

char (*p)[10]の使い道

strike1217

総合スコア651

C

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

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

1グッド

2クリップ

投稿2017/01/23 02:31

編集2017/01/24 08:33

char (*p)[10];
>>>char型の要素を10個持つ配列へのポインタ!
これの使い道がわかりません。

C

1int (*arp)[10]; 2 3// アドレスを入れるときは以下のように入れます。 4 5int array_ten[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 6arp = &array_ten;

普通に、int *arp = array_ten; で良いような気がします。
わざわざ要素数が固定のポインタに渡す必要性が見えません。

「あ!ポインタのポインタの初期化に使えるかな?」と思い、以下のようにしてみました。

C

1char buf[3][100] = { 2 "nononono!!", 3 "I love you!", 4 "Thanks" 5 }; 6 7 char **p; 8 // char *q[3] = {buf[0], buf[1], buf[2]}; こっちは成功 9 char (*q)[100] = buf; 10 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型です。

退会済みユーザー👍を押しています

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

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

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

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

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

guest

回答9

0

int型は4バイト、int[10]型は40バイトだから

int型のポインタはポインタ演算で4byte進もうとするけど
int[10]型のポインタは40byte進もうとするんだ

C++

1#include <iostream> 2using namespace std; 3 4int main() { 5 int *singlep = 0; 6 int (*arrayp)[10] = 0; 7 cout << (unsigned int)singlep << endl << (unsigned int)(singlep+1) << endl; 8 cout << (unsigned int)arrayp << endl << (unsigned int)(arrayp+1) << endl; 9 return 0; 10}

この小さなコードを実行してみて欲しい。
こんな感じで結果がでるはずだ。

0 4 0 40

ポインタにも細かい型の違いがあるんだよ
ポインタと配列が違うっていう人もいるけど、この動きは配列の時でも何も変わらない

C++

1#include <iostream> 2using namespace std; 3 4int main() { 5 int array[2][10] = 0; 6 cout << (unsigned int)array << endl << (unsigned int)(array+1) << endl; 7 return 0; 8} 9 10> 4290089120 11> 4290089160

追記


ポインタはアドレスの先頭を指している。一つのint型は4バイト使うので、もしも隣接する要素もint型なら次の4バイトは4バイト先のアドレスから始まる。型の大きさはプログラミングする上で重要な要素なんだ。受け取ったポインタが配列だった場合、次の要素の位置を簡単に指し示すことができるからね。

C++

1#include <iostream> 2using namespace std; 3 4int main() { 5 char str[12]; 6 char *p = str; 7 *(p+0) = 'H'; 8 *(p+1) = 'E'; 9 *(p+2) = 'L'; 10 *(p+3) = 'L'; 11 *(p+4) = 'O'; 12 *(p+5) = ' '; 13 *(p+6) = 'W'; 14 *(p+7) = 'O'; 15 *(p+8) = 'R'; 16 *(p+9) = 'L'; 17 *(p+10) = 'D'; 18 *(p+11) = '\0'; 19 cout << str; 20 return 0; 21} 22 23> HELLO WORLD

C++

1#include <iostream> 2using namespace std; 3 4int main() { 5 char str[3][100] = { "Line1", "Line2", "Line3" }; 6 char *p = (char *)str; 7 cout << p << endl << p+1 << endl; 8 9 p = (char*)((int)p + 100); 10 cout << p << endl << endl; 11 12 char (*arrayp)[100] = str; 13 cout << *arrayp << endl << *(arrayp+1) << endl; 14 15 return 0; 16} 17 18> Line1 19> ine1 20> Line2 21 22> Line1 23> Line2

型情報には、アドレスをどうシフトさせたら次の要素が参照できるのかが得られるだけの差別化がある。
**ポインタのポインタは4バイトシフトするポインタだが、要素数100のchar型の配列のポインタは100バイトシフトするポインタなわけで、全然別ものなんだよね。

C++

1#include <iostream> 2using namespace std; 3 4int main() { 5 char *p = 0; 6 char **pp = 0; 7 char (*ap)[100] = 0; 8 cout << (unsigned int)(p+1) << endl; 9 cout << (unsigned int)(pp+1) << endl; 10 cout << (unsigned int)(ap+1) << endl; 11 12 return 0; 13} 14 15> 1 16> 4 17> 100

更に追記

どんな時に使うのかが質問だった。

まず、char **pchar (*p)[100]の違いはなんですかって質問を見つめなおそう。
こいつはね、ポインタのポインタ配列のポインタの違いを聞いてる。
これはもっと抽象化すると、ポインタのポインタある型のポインタの違いを聞いてる。
これがわかるかどうかはstrike君次第だけど…
記法よりも、何をしているのかに着目してほしい。
これはプログラミングっていうより国語の問題だ。

ポインタのポインタある型のポインタは根本的に別物だよ。
char **pint *pの違いはなんですかって言ってるのと本質的には大差無い。

ある型のポインタには配列の型も指定できるんだよ。それがchar (*p)[100]って記法だ。
だから、あるポインタをある配列のポインタに変換することもできる。

C++

1char c[100*5]; 2char (*ap)[100] = (char(*)[100])c; 3 4char a[5][100]; 5char *cp = (char*)a;

このコードはコンパイル通るし、お互いの範囲内で使う分には安全だよ。
cもaも変数としては連続した500バイトのメモリアドレスを確保していてどのようにアクセスするかが違うだけだ。

下記は今じゃまず使わないけどこのサンプルで配列型のポインタの使い方を示しているつもりだ。

C++

1#include <iostream> 2using namespace std; 3 4void char100print(char (*str)[100], int length) 5{ 6 for (int i = 0; i < length; i++) 7 { 8 cout << *str++ << endl; 9 } 10} 11 12int main() { 13 char value[][100] = {"test1", "test2", "test3", "test4"}; 14 15 char100print(value, 3); 16 17 return 0; 18} 19 20> test1 21> test2 22> test3

より具体的なサンプルも示そう。
昔は固定長のファイルが多かった。
今でも現役の固定長フォーマットに全銀協制定フォーマットがある。
銀行でお金を振り込んでくださいってことをするファイルだ。
これは120バイト(固定)を1行として使うファイルなんだ。
ファイルの読み書きは行単位ではなく、120バイト単位にしなきゃならない。

C++

1ZENGIN ReadZenginFormat(const char *p, int record_num); 2 3ZENGIN ReadZenginFormat(const char (*p)[120], int record_num);

このサンプルは上下で情報量が全然違うよね。
でも動作原理はまったく一緒だし、読み取るための関数だから参照先アドレスを編集することがないのでconst属性がついてる。
record_num=5の場合、pはとあるアドレスを参照しており、そこから連続した600バイトのメモリが確保されている。

一方、以下は全く意味が異なる。

C++

1ZENGIN ReadZenginFormat(const char **p, int record_num);

pは…ポインタのポインタの配列なんだろう。読み取り専用だしね。
でもそうなると、ポインタそのものは5行であったとしたら、pのアドレスから確保されているバイト数はわずか4*5バイト=20バイトだ。
その20バイトの中に、5つの文字列のポインタが入っているわけだ。

使い方を示すとこうだ。

C++

1char *p[5]; 2p[0] = malloc(120); 3p[1] = malloc(120); 4p[2] = malloc(120); 5p[3] = malloc(120); 6p[4] = malloc(120); 7 8ReadZenginFormat(p, 5);

ポインタそれぞれに別の文字列の参照を持つ。
p[0]からp[4]で参照しているメモリアドレスは別に連続したメモリ空間じゃない。
一旦次のポインタに移動して、そこから別の文字列を参照する。
これは配列のポインタとは構造が全く異なるんだよ。

ポインタのポインタ配列のポインタには互換性が無いんだ。
互換させる場合はこうなる。

C++

1char a[5][120]; 2char *p[5]; 3p[0] = a[0]; 4p[1] = a[1]; 5p[2] = a[2]; 6p[3] = a[3]; 7p[4] = a[4]; 8 9ReadZenginFormat(p, 5);

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

投稿2017/01/24 07:52

編集2017/01/24 11:15
haru666

総合スコア1591

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

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

strike1217

2017/01/24 07:56

ありがとうございます!
strike1217

2017/01/24 08:38 編集

char *p = 0; // p+1 : 1 char **pp = 0; // pp+1 : 4 char (*ap)[100] = 0; // ap+1 : 100 p = &p; 可能! pp = &ap; 不可能! 違いがいまいちわからないのですが・・・
haru666

2017/01/24 11:12

非常に説明しづらいけどなんとか書いてみました。 strike1217君はそもそもとんでもなく勘違いしてます。 もっと確保したメモリがどうなっているか考えてみてください。
haru666

2017/01/24 11:16

どこかで直接通話できれば図とか書いて教えてあげれるから早いんだけどね。
strike1217

2017/01/24 11:20

ポインタのポインタはポインタのアドレスを格納できます。 配列のポインタのアドレスもポインタのポインタに格納できる! ・・・と思っていたんですが・・・
haru666

2017/01/24 11:29

うーん、使い方がぜんっぜん異なるからね。 可能不可能の問題では可能だけど、やっても次のアドレスみれないよ? ある配列char a[2][100]のポインタのアドレスが10000だとしよう。 で、ポインタのポインタpに同じ10000って値を入れることはできそうだよね。 しかも*pってアクセスすればアドレス10000から始まる文字列にはアクセスできそう。 でも、だからってa[1] は10100をさすけど p[1]で10100をさすことはできない、代入しない限りp[1]は不定値だし、アクセスすればsegmentation faultだ。 あれなら、あとで絵にかくよ。
strike1217

2017/01/24 11:36 編集

確かに、以下をテストするとおちます。 char iii[3][100] = { "you will make me happy", "but I dont like you!", "Good Bye!" }; char (*qqq)[100] = iii; // 配列のポインタ char **ppp = &qqq; // warningが出てくる(なんで?) printf("%s\n", *ppp); // ok! printf("%s\n", *(ppp+1)); // 落ちる!
haru666

2017/01/24 11:42

そりゃそうさ、pppはただのアドレスの参照だもの。 あるメモリpppの次のバイトってどうなってるって考えてみよう。 strike1217君のメモリレイアウトがそのまま確保された場合、pppの次のバイトは不定値。 ppp+1で次のバイトを見たら不定値。 printfでそこを見たら当然落ちる。 互換性まったくないんだからね、暗黙的な返還はできないよ。 iii[3][100]は300バイト確保される。 この意味をよく考えてみて。 iii[1]はiii[0]から100ずれたアドレスを指してるだけ。 場所の情報。 ppp[1]はpppから1ずれたアドレスを指してるだけ。 これはpppが指し示すiii[0]のさすアドレスの1バイトずれたアドレスじゃなくて ppp自体のアドレスの1バイトずれたアドレス。 互換性無いのに暗黙的に変換できるわけがないよね。
haru666

2017/01/24 11:45

型が先にあって、アドレスの操作は型に紐づいてるんであって、参照先には紐づかないよ。
strike1217

2017/01/24 11:48

あは~~~!! printf("%s, %s\n", *ppp, *ppp+100); こいつは問題がありませんでした。 you will make me happy, but I dont like you!  ← 目的の結果になりました。 *(ppp+1) にすれば、 *ppp + 100にアクセスできると思い込んでいました。
haru666

2017/01/24 11:53

そう、それが違うわけ。どう見てもポインタのポインタの型情報には100バイトシフトして、なんて情報はいってないよね。どうやっても暗黙的には変換できないんだよ。
strike1217

2017/01/24 12:03

「100バイトシフトして、なんて情報はいってない」 ああ~~ なるほど! できない理由がやっとわかりました。
rubato6809

2017/01/25 10:06 編集

横槍失礼。 再確認。ppp は qqq という変数をポイントしています。 *ppp とは何か? 答えは qqq の値です。 そして、qqq の値とは "you will ..." という文字列の先頭アドレスです。 要するに、*ppp は "you will make me happy" の先頭アドレスであり、 それに 100 を足せば "but I dont like you!" の先頭アドレスになります。 ですから printf("%s", *ppp + 100); は "but I dont..." を表示します。 これはよいですね。 ところで、一般的に言って、*p とは p[0] と書けます。 従って、printf("%s", *ppp); は printf("%s", ppp[0]); と同じです。 これもよいですね? > *(ppp+1) にすれば、 *ppp + 100にアクセスできると思い込んで では、*(ppp + 1) は何か? 一般的に、*(p + 1) は p[1] と同じですから、*(ppp + 1) は ppp[1] とも書けます。 これも、理解できますよね? 以上が意味する事は、こういう事です。 ppp が指すメモリは qqq である、 そして、その付近のメモリは qqq 自身のアドレスを先頭にして、 「ポインタが配列のように並んでいる」と見做すことができる。 念の為、ppp[0] は qqq それ自身です。 で、 *(ppp + 1)、即ち ppp[1] とは、qqq というポインタ変数の「隣のメモリ」なのです! 「qqq の隣のメモリ」が、何に使われているか、不明です。"無理やり"ポインタだと見做した場合、何番地を指しているかわからない。不定値です。少なくとも、"but I dont like you!" を指してはいません。 よって、落ちても不思議は無い(運が良ければ、何かゴミ文字列が表示される)。 これが理解できると、貴方の勘違いも、落ちる原因も、全て解決するはずです。 当然ですが、単純変数も配列も、ポインタも、全てメモリ上に配置されています。 できれば、ご自分のプログラム中の各変数が何番地に割り当てられているか、 メモリダンプなど、何らかの方法で確認してみる事をお勧めします。 次第に、こうしたことが実感できてくるはずです。 P.S. > char **ppp = &qqq; // warningが出てくる(なんで?) 念の為。これは昨日の私の回答の中で触れた通り、コンパイラは「型が違う」と言っている。ので、キャストすると warningは消えます。 char **ppp = (char**) &qqq;
strike1217

2017/01/25 11:06

ありがとうございます! qqqで、pppを初期化したら、2次元配列のように、列x行で確保できていると勝手に思っていました。 qqq は列x行に伸びているわけではないんですよね? qqqは一次元配列ですよね?
haru666

2017/01/25 11:20

qqqは配列ですらないよ 100バイトのchar型配列のポインタだから、変数としてみるならわずか1個だよ
haru666

2017/01/25 11:22

qqq+1は運が悪ければその時点でsegmentation faultだし、どっちにしろ大体そこに格納されてる不定値のせいでsegmentation faultだ。何度もいってるけど、char (*qqq)[100]はchar(*)[100]型の1個の変数だよ。int a;やint *bを配列って言わないよね?
strike1217

2017/01/25 11:30

あ!qqqはポインタですね
strike1217

2017/01/25 11:36

今やってみたところ、 printf("%s", qqq + 1); これは普通にできるみたいですが・・・
rubato6809

2017/01/25 11:39

qqqは一個の単純なポインタ変数です。配列ではないのに、 *(ppp + 1) とアクセスしたために、その辺りを、あたかも配列であるかのようにアクセスしてしまう・・・一種のバッファオーバーランとも言えるから、その時点で不正なメモリアクセスです。 ことほどさように、自分が今何をしてるのか、明確なイメージを持ってないと危ないのがC言語です。切れ味するどいツールは、扱いが難しいのです。
haru666

2017/01/25 11:44

printfは受け取った変数の数値を、左側の文字列から判定しているだけ。 つまり、printf("%s", some_value);だったらsome_valueの型がなんであろうと文字列(char*)として処理しているだけ。だからqqqの型は関係ないんだよね。printfで表示する時にはすでに失われてて、変数の値だけ受け取ってるんだよ。qqq+1が指し示すアドレスが文字列をきちんと指すから問題ない。 特に不思議な魔法はそこには何もない。
rubato6809

2017/01/25 11:51 編集

> printf("%s", qqq + 1); > これは普通にできるみたいですが・・・ ああ、そうですね。 char (*qqq)[100] = iii; // 配列のポインタ 即ち、一要素(qqqが指す先)が100バイトだと宣言してあるから、+1 でアドレスが100バイト進むわけです。 それ、即ちアドレスが1バイトずつ進む char** という型との違いなのですよ。
strike1217

2017/01/25 11:48

char (*qqq)[100] = iii; 2次元配列で初期化しているから、あたかも2次元配列のようにアクセスできるのですね!!
haru666

2017/01/25 11:51

2次元配列も連続したバイト配列を確保しているだけだからね… char a[3][100]はアドレスaから1文字列目100バイト、2文字列目100バイト、3文字列目100バイト確保されて連続した300バイトになってるだけだから、ちゃんと100バイトずつずらせばn文字列目っていうのを参照できるようになってます。だからchar(*)[100]型のポインタは加算すると100バイトずらしてくれる。もしも受け取ったポインタが配列のアドレスだった場合のためにね。
rubato6809

2017/01/25 12:02 編集

>2次元配列で初期化しているから、あたかも2次元配列のようにアクセスできるのですね!! それは違う。まだ分かってないな。残念w char x = 'A'; char *p = &x; printf("%c", p[1]); pが指してる先を配列にしたつもりはないのに、ポインタだから、その辺りをあたかも配列のようにアクセスできてしまう、というだけ
strike1217

2017/01/25 11:54

ガーーーーン! それはちがうんですか!
strike1217

2017/01/25 11:55

「2次元配列も連続したバイト配列を確保しているだけだからね」 ああ~~ そうなんですね! よくある、平面のような図を想像していました。
strike1217

2017/01/25 12:05

char *p = &iii[0][0]; char (*w)[100] = iii; 下の方がアクセスが簡単にできますね。 w + 1 == p + 100 になってました。
guest

0

こんにちは。

普通に、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;と定義した方が読みやすいし、バグりにくいと思います。

投稿2017/01/23 03:47

Chironian

総合スコア23272

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

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

strike1217

2017/01/23 03:52

ありがとうございます。 char (*p)[10]; // 初期化なし printf("p[1] : %p, p[1][0] : %p\n", p[1], &p[1][0]); 初期化なしで、2次元配列のようにできたので・・・あれ?と思ってしまいました。 「ポインタは配列に読み替えることができます。」 確かに、そうでしたね!
strike1217

2017/01/23 03:58

でも、これって、極端な話をすれば、 printf("p[1000] : %p, p[1000]); みたいなこともできるんですよね? 「列の方が可変で、行の方が固定の2次元配列」というイメージを持っていました。
Chironian

2017/01/23 07:18

できる筈ですよ。普通の配列(ポインタ)と同じですから。 そのイメージで正しいです。C言語のポインタ≒可変長配列(領域管理はプログラマの責任)ですから。
guest

0

前の質問

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次元配列をダブルポインタに入れようとしていることではないかと思ってしまいました。
配列とポインタは相互に入れ替えできる部分はあっても別物です。

投稿2017/01/23 03:39

kyunta

総合スコア350

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

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

strike1217

2017/01/23 03:43

ありがとうございます!! これは2次元配列で初期化できるよ。 ということですよね。
strike1217

2017/01/23 03:47

確かに、2次元配列で初期化するときは、 char *q[3] これより、char (*q)[3]の方が簡単にできますね!!
kyunta

2017/01/23 12:38 編集

追記について >arpという1つのポインタが4つの配列の先頭アドレスを指しているということですよね? >たった1つしかないポインタが同時に4つのアドレスを指しているなんて・・・ いいえ違います。4つの要素を持つ配列の先頭アドレスを指しています。 そしてこの場合、その4つの要素は配列です。 arp[0]はint型の要素を10個持つ配列の先頭アドレスを arp[1]は次のint型要素を10個持つ配列の先頭アドレスを指します。。。 (*p)[]の記述が配列へのポインタというのはそういう事です。
strike1217

2017/01/23 07:11

「4つの要素を持つ配列の先頭アドレス」 ほうほう! ふむふむ・・・ なるほど。 分かりやすいです!
kyunta

2017/01/24 10: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; } こんな事を推奨しているわけではなく、勉強のためのコードです。 配列は配列らしく、ポインタはポインタらしく使うのがいいと思っているので。
strike1217

2017/01/24 11:07

ほえ!! char **qq = (char**)buf; これってできるんですね!! ありがとうございます!
strike1217

2017/01/24 11:25

printf("%s\n", qq); これは、warningが出現します。 実行はできていますね!
guest

0

これをやると、warningが出てきて、さらに落ちます・・・・
なぜかわかりませんが・・・

手元のGCCでコンパイルするとp = q;の行で、
"warning: assignment from incompatible pointer type ..." となりました。「pとqでは互換性が無い(?)のに、代入して大丈夫なのか?」という警告です。要するに「型が違う」のだから、warningを消す手立ては、まずキャストでしょう。即ち、

C

1 char **p; 2 3 p = (char**)q; // キャストする

これでwarningが消えますが、手立ては他にもあります。ポインタpの宣言を変更すれば良い。即ち、

C

1 char (*p)[100]; // p を、こう宣言する 2 3 p = q; // キャスト不要

以上は、char **char (*)[100]を、コンパイラは型として扱っている事、しかも明確に異なる型だと区別している事を示しています。

char **は、ポイントしてる先のcharが、1個なのか10個なのか、それとも100個なのか、サイズの指定はありません。
それに対して、char (*)[100]は、個々の要素は同じくcharだけど、「100個のcharをひとかたまり(100バイトを一つのデータ型)と見るポインタ」だという宣言です。こちらにはサイズの指定があるので、区別がつきます。

一般的に言えば、「固定長の配列」を一つの型として扱える…そう考えれば、そのようなデータ構造は、決して珍しくないはずですから、そうした管理がふさわしければ使う価値があると思います。ただ実際は、通常の(サイズ指定をしない)ポインタでアクセスも操作も可能なので、いまいち出番が少ないかもしれませんね。

投稿2017/01/23 11:57

rubato6809

総合スコア1380

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

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

strike1217

2017/01/23 12:07 編集

ありがとうございます! サイズの指定があると区別されるんですね!! p = (char **)q; 確かに、キャストするとwarningはでなくなります。 しかし、 printf("m: %s, %s\n\n", *m, *(m + 1)); このようにアクセスすると落ちます。 *m の方は大丈夫のようですが、*(m+1)にするとダメでした。 なぜでしょうか? アクセスの仕方が混乱していますが、間違っていますかね?
rubato6809

2017/01/23 12:14

その「m」、唐突に出てきましたが、どう宣言し、どう初期化しましたか?
strike1217

2017/01/23 12:15

あ! ごめんなさい。 mは名前を変更しただけです。 質問に出てくるpと全く同じです。 すいません。
rubato6809

2017/01/23 12:26

ああ、私もうっかりしていました。 char **p; ではなく char *p; とすべきで、従って p = (char*) q; として printf("p: %s, %s\n\n", p, p + 1); ではありませんか?
rubato6809

2017/01/23 12:31 編集

もしくは char (*p)[100]; と宣言して p = q; とし、 printf("p: %s, %s\n\n", (char*)p, (char*)(p + 1)); これで p: nononono!!, I love you! と出力しました。この出力を期待してるんじゃないかな?
strike1217

2017/01/23 12:36

ああ~~ 確かに、できますね。 この方法も1つありと言うことですよね・・・ 私は、ポインタのポインタの初期化の仕方を char (*q)[100]; でやれば、char *q[3]; これより簡単にできるのではないか・・・・と考えていたんです。 なので、ポインタのポインタを使わないと、お勉強にならないのです! char **p; こいつを char (*q)[100]; での初期化方法はわかりますか?
rubato6809

2017/01/23 12:41

さあ、よくわかりません。 やりたいことを整理して、新たな質問にしたほうが良いのでは?
strike1217

2017/01/23 12:44

んん~~ やはり、これはできなんですかね・・・・・ ポインタのポインタの初期化の仕方が・・・わかりにくいですね。
rubato6809

2017/01/23 12:56

「ポインタのポインタの初期化」で、思ったんですが、 main(int argc, char **argv) は、ご存知? char **argv は char *argv[] でもありますが、それはさておき、 char **argv と宣言するポインタが、どんなデータ構造をポイントしているか、絵に描いてみてください。それが身近なポインタのポインタです。そんなデータ構造を考えてみればよいのでは?
rubato6809

2017/01/23 13:00

char **argv は char buf[3][100] のような配列をポイントしているのではありません。何が違うか、その違いを図に描いて体得するのが大事だと思います。
rubato6809

2017/01/23 13:10 編集

ああ、まさに char *q[3] = { buf[0], buf[1], buf[2] }; // こっちは成功 char **p; // ←これがポインタのポインタ として p = q;   // これで初期化ができる 「char **argv」も、この char **p と同じ(よく似た)構造をポイントしている
strike1217

2017/01/24 05:12

ふむふむ ありがとうございます!
strike1217

2017/01/24 07:57 編集

ポインタのポインタの初期化なので p = &q; こちらが正しかったです。 修正しました。 これでもできませんでしたが・・・
guest

0

intと同じでよろしければ

c

1 int (*arp)[10]; 2 3 int array_ten[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 4 arp = &array_ten; 5 printf("%d \n",(*arp+1)[2]); 6 7 char buf[3][100] = { 8 "nononono!!", 9 "I love you!", 10 "Thanks" 11 }; 12 13 char (*q)[3][100]; 14 q = &buf; 15 printf("%s \n",(*q)[1]); 16 17  char **p = (char **)(*q)[1]; 18 printf("%s \n",p); 19

int (arp)[10];arp1は、intの配列(要素10)へのポインタなので、代入の場合キャスト(int ()[10]) intの配列(要素数10)でポインタに変換する。

c

1int main() { 2 3 char ii[4] = "GHYU"; 4 char *qq = ii; 5 char **pp = &qq; 6 int array[4][10]={{1,2,3,4,5,6,7,8,9,0},{10,11,12,13,14,15,16,17,18,19}}; 7 int (*arp)[4][10] = (int (*)[4][10])array; 8 printf("%d \n", (*arp)[0][5]); 9} 10

実はこの方のページ を参考とさせて頂いています。

投稿2017/01/23 03:31

編集2017/01/24 08:13
A.Ichi

総合スコア4070

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

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

strike1217

2017/01/23 03:38

ありがとうございます。 ポインタのポインタの初期化については char (*q)[100]; q = &buf; これで終わりなら、2次元配列で初期化して終わり!って感じですが・・・・ 問題は、 char **p = q; こいつができないということです。 char *q; char **p = q; こっちはおkでした。
strike1217

2017/01/23 03:44 編集

あ! 失礼しました。 「char (*q)[100]; の初期化に2次元配列を使用できますよ!」 ってことですね!
strike1217

2017/01/24 05:20

char **p = (char **)(*q)[1]; 私の環境で、できませんでした。 コンパイラがエラーを吐き出します。 char **p = (char **)(*q); こちらは、大丈夫でしたが、実行すると落ちます・・・
A.Ichi

2017/01/24 05:41

Centos7(gcc 4.4.7) ,Centos6(gcc 4.1.2 )、Centos5(32)ではコンパイルも実行もでき”I love you!” が表示されます。
strike1217

2017/01/24 05:44

ほえ! 本当ですか! もう一度やってみます!!
strike1217

2017/01/24 05:52

warning C4477: 'printf' : format string '%s' requires an argument of type 'char *', but variadic argument 1 has type 'char **' 同じようにやると上記のようなwarningが出現し、今度は文字化けします。
strike1217

2017/01/24 05:54

printf("%s \n", p); これだとwarningが出ますが、うまくいきます。 printf("%s \n", *p); こっちにしたら、 warningは消えて、落ちます・・・・
A.Ichi

2017/01/24 06:53

ubuntu(gcc v5.3.1)だとワーニングがでました。 warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘char **’ [-Wformat=] printf("%s \n",p); なのでcastする事にしました。 printf("%s \n",(char *)p);
strike1217

2017/01/24 07:06

ほうほう! char * でキャストということは、結果としてはポインタのポインタではなくなるということですかね?
A.Ichi

2017/01/24 07:13

ポインタのポインタで有る事には、変りわないがコンパイラが型が異なる事をワーニングしてくれているのでは、ないかと思もっています。printの%sにcharのアドレスを渡す事ができれば良いのかと考えています。
strike1217

2017/01/24 07:21

ちょっと、追記してみました。 どうして、できないのかが、わからないです・・・
strike1217

2017/01/24 08:40 編集

追加の char ii[4] = "GHYU"; char *qq = ii; char **pp = &qq; これは、できましたか?? warning が出てきて、printf();などでアクセスすると落ちるんですよ。
strike1217

2017/01/24 08:44

char (*q)[3][100]; これが唯一違うんです。 char (*q)[100]; 私は、こっちでやってました。 char (*q)[3][100]; は さらに次元が上がって3次元になるんではないのでしょうか??
A.Ichi

2017/01/24 08:54

配列値を指すアドレス全てを配列に入れるので同じ形が必要の様です。[40]とするのは駄目の様です。
A.Ichi

2017/01/24 09:06

これはできる様です。 int (*arp)[10]; arp = (int (*)[10])array[1];
strike1217

2017/01/24 09:25

ああ~~ そうなんですか! なんか、すごく難しいんですが・・・
guest

0

やはり、char (*p)[10];

1、列の動的確保
2、2次元配列で初期化できる → 関数に2次元配列を渡す!

この2つくらいしか、使い道が思いつきません・・・です。

投稿2017/01/25 19:40

strike1217

総合スコア651

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

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

0

たとえば列数固定の行列を確保したいとき。

C++

1 // N行10列の行列を確保する 2 float (*p)[10]; 3 p = (float (*)[10])malloc(N * 10 * sizeof(float)); 4 ... N行10列の行列 p[i][j] として使う 5 free(p);

投稿2017/01/23 04:45

episteme

総合スコア16614

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

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

strike1217

2017/01/23 04:54

ふむふむ・・・ ありがとうございます。 動的確保の時の使用例ですね!!
guest

0

ポインターのポインターでありさえすればいいのであれば
int **argp;
でよいわけですが、「10要素の配列へのポインター」であることを明確にしたいときに有用です。
int (*argp)[10];
と宣言することで10要素でない配列へのポインターを代入しようとしたとき(つまりバグ)コンパラーが警告を発してくれます。

C

1int b[9]; 2argp = &b; // <= 警告: 互換性のないポインタ型からの代入です

投稿2017/01/23 03:26

KSwordOfHaste

総合スコア18394

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

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

strike1217

2017/01/23 03:32

ありがとうございます。 要素数が9のbと要素数10のargpでは、たしかに互換性がないと思いますが・・・ 要素数が同じなのに、warningがでてきて、落ちるんですよ。 おそらく、 char (*q)[100] = buf; こいつを ポインタのポインタに入れるときがダメみたいです。 なんでダメなのかわかりませんが・・・・
guest

0

具体的にどんな時に使うかは人それぞれだと思いますが、
charポインタを10個ほしい時に使います。

投稿2017/01/23 03:18

Take-y

総合スコア91

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

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

strike1217

2017/01/23 03:20

それは、おそらくこちらではないでしょうか? char *p[10];
Take-y

2017/01/23 03:36

ご指摘、ありがとうございます。 勘違いしてました。申し訳ないです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問