注:この回答は、4時間前にOP(元のポスター)である@hudacが行った説明を反映するように更新されています。尋ねられた質問は有効であり、非常に興味深いものです。そうは言っても、OPのコードを単純化する方法があるかもしれません。元の質問に答えた後、それらを見ていきます。
まず、OPの最近の説明に従って、Aとfuncの定義例を示すことにより、質問のコードをもう少し具体的にしようとします。
typedef struct { int x; int y;} A;
void func(A **ppa)
{
printf("(%d,%d)\n",(*pa)->x,(*pa)->y);
}
次に、質問と回答の両方に適切なコンテキストを提供するコードをいくつか示します。
void main() {
A *pa;
pa = (A*) malloc(2 * sizeof(A));
pa[0].x = 3; pa[0].y = 4;
pa[1].x = 13; pa[1].y = 42;
func(&pa);
//the answer to the question goes here
}
現状の質問は、動的に割り当てられたAの配列の2番目pa
の要素へのポインターへのポインターを渡す方法を求めています。OPは、pa
(func(&pa);
)の最初の要素へのポインターへのポインターの受け渡しに成功し、ポインター演算を使用して2番目の要素へのポインターへのポインターの受け渡しを試みましたが、もちろん期待どおりには機能しませんでした。OPのコードを考えると、与えられた答えはどれも正しいとは言えません。これは非常に興味深い質問であることがわかります。func
A*ではなくA**を期待すると、物事が必要以上に複雑になります。OPが直面した問題を理解せずに物事を単純化することは可能ですが、これでは、ポインター演算を正しく行うことがなぜそれほど困難であったか(実際には不可能であったか)についての洞察は得られません。
基本を再検討するための小さな迂回:代入がある場合は、左辺値(左側の式)と右辺値(右側の式)があります。左辺値はアドレスを持つものであり、右辺値には、割り当てが左辺値のアドレスに格納する値があります。例:x = 5; 式には、左辺値と右辺値の両方(xなど)が含まれる場合と、右辺値のみ(5など)が含まれる場合があります。演算子&は、左辺値、つまりメモリアドレスを持つもの、つまりxなどにのみ適用されますが、5などには適用されません。
質問に戻ります。paはタイプA*ですが、funcはタイプA**の引数を想定しています。OPは、paの最初の要素で機能するようにfuncを呼び出す方法を問題なく理解しました。
func(&pa);
これはpaの最初の要素に取り組んでいるfuncへの呼び出しであるというコメントと一緒に、このコードを少し与えるのは非常に難しいことに注意してください。関数funcはpa[0](別名* pa)、つまりタイプAの何かで動作する必要がありますが、funcが定義されているため、タイプAの&paを渡します** !!! より適切な引数タイプで関数を定義することにより、この問題に後で対処します。今のところ、私は自分が持っているもので作業しようとします。
OPなどがpaの2番目の要素で機能するfuncの呼び出しを行うのが非常に難しいと感じた理由は、必要なのは、値がメモリのビットのアドレスを保持するポインタ式であるためです。 paの2番目の要素。paの2番目の要素のアドレスはどこにも保存されていないため、このようなポインタの使用はありません。(大文字が目を痛めた場合はお詫びします:-)これをできるだけ明確にする必要がありました!)paの2番目の要素へのアクセスは、1番目の要素のアドレス(paが保持するもの)を使用したポインター演算によるものです。 。
それは質問に答えることが不可能であるという意味ではありません。それは、答えが予想外で厄介なハックになることを意味します。したがって、質問でmain()に別の変数を導入できないと仮定すると、funcを呼び出すためにpaを変更し、厄介な驚きなしにpaを操作できるように戻す必要があります。後でmain()で。
pa++; func(&pa); pa--;
これがOPの質問に対する答えです。現在ここに投稿されている他の回答は、さまざまではるかに単純な質問に答えようとしています。しかし、元の質問ははるかに興味深いものであり、今では答えがあります:-)しかし、答えよりも興味深いのは、大文字を使用して強調した観察です。
私たちの正気を取り戻すために、私たちはfuncの定義でなされた選択を再考する必要があります。
配列に格納されているAにアクセスすることだけが必要な場合は、ポインターへの依存を完全に排除することを検討できます。
typedef struct { int x; int y;} A;
void bar(A a)
{
printf("(%d,%d)\n", a.x, a.y);
}
void main() {
A *pa;
pa = (A*) malloc(2 * sizeof(A));
pa[0].x = 3; pa[0].y = 4;
pa[1].x = 13; pa[1].y = 42;
bar(pa[0]);
bar(pa[1]);
}
関数に単純なAではなくA*引数をとらせることを検討する理由は2つあります。1。関数が指すAを変更する必要がある場合、および/または2. Aがたまたま大きな構造体である場合、および関数を呼び出すたびに、大量のメモリがスタックにコピーされることは望ましくありません。
typedef struct { int x; int y;} A;
void foo(A *a)
{
int tmp = a->x;
a->x = a->y;
a->y = tmp;
}
void bar(A a)
{
printf("(%d,%d)\n", a.x, a.y);
}
void main() {
A *pa;
pa = (A*) malloc(2 * sizeof(A));
pa[0].x = 3; pa[0].y = 4;
pa[1].x = 13; pa[1].y = 42;
foo(&pa[0]); //or alternatively: foo(pa);
bar(pa[0]); //or alternatively: bar(*pa);
foo(&pa[1]); //or alternatively: foo(pa + 1);
bar(pa[1]); //or alternatively: bar(*(pa+1));
}
OPに役立つ可能性のあるもう1つの注意点は、ポインター演算を実行する際に、ポインターのタイプが役割を果たすということです。したがって、pa + 1の数値は、paの数値にsizeof(A)を加えたものに等しくなります。(これは、次の呼び出しでpaの2番目の要素でfuncを機能させる試みの失敗に関連しています:func(&pa + 1 * sizeof(A)))
OPへの最後の注意:func内にA **ポインターを取得し、それに別のポインターを割り当てるなどの面白いことを行う計画があったためにfuncの署名を変更したくない場合(たとえば、新しいmalloc( )-割り当てられたメモリのチャンク)、あなたは地雷原に歩いているでしょう!関連性があると思われる別の質問に対する私の回答を確認してください。line2の無料でmemcpyが何もしないのはなぜですか?mallocが大丈夫になる前に無料ですか? 物語の教訓は、ポインターは有用ですが、概念の複雑さとエラーの可能性は、*ごとに指数関数的に増大するということです(そして多くの場合、より良い選択肢があります):-)