構造体へのポインター/参照を関数に渡す
質問
これはばかげた質問のように聞こえますが、私はまだCを学んでいるので、どうか我慢してください。 :)
私はK& R(構造体)の第6章に取り組んでいますが、これまでのところ、この本を通して大きな成功を収めています。私は構造体をかなり重く扱うことを決めたので、この章の早い段階でポイントとレクの例を使って多くの仕事をしました。試したかったことの1つは、 canonrect
関数(第2版、p 131)をポインター経由で変更し、 void
を返すことでした。
これは機能していますが、しゃっくりにぶつかりました。皆さんが私を助けてくれることを望んでいました。 canonRect
で一時的な長方形オブジェクトを作成し、その変更を実行してから、渡されたポインターを一時的な長方形に再割り当てして、コードを簡素化したかったのです。
しかし、私がそれをしても、長方形は変わりません。代わりに、渡された四角形のフィールドに手動で再入力しますが、これは動作します。
コードは次のとおりです。
#include <stdio.h>
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
struct point {
int x;
int y;
};
struct rect {
struct point lowerLeft;
struct point upperRight;
};
// canonicalize coordinates of rectangle
void canonRect(struct rect *r);
int main(void) {
struct point p1, p2;
struct rect r;
p1.x = 10;
p1.y = 10;
p2.x = 20;
p2.y = 40;
r.lowerLeft = p2; // note that I'm inverting my points intentionally
r.upperRight = p1;
printf("Rectangle, lower left: %d, %d; upper right: %d %d\n\n",
r.lowerLeft.x, r.lowerLeft.y, r.upperRight.x, r.upperRight.y);
// can't pass a pointer, only a reference.
// (Passing pointers results in illegal indirection compile time errors)
canonRect(&r);
printf("Rectangle, lower left: %d, %d; upper right: %d %d\n\n",
r.lowerLeft.x, r.lowerLeft.y, r.upperRight.x, r.upperRight.y);
}
void canonRect(struct rect *r) {
struct rect temp;
temp.lowerLeft.x = min(r->lowerLeft.x, r->upperRight.x);
temp.lowerLeft.y = min(r->lowerLeft.y, r->upperRight.y);
temp.upperRight.x = max(r->lowerLeft.x, r->upperRight.x);
temp.upperRight.y = max(r->lowerLeft.y, r->upperRight.y);
r = &temp; // doesn't work; my passed-in rect remains the same
// I wind up doing the following instead, to reassign all
// the members of my passed-in rect
//r->lowerLeft = temp.lowerLeft;
//r->upperRight = temp.upperRight;
}
だからここに質問があります:
- なぜ
r =&amp; temp;
が機能しないのですか? (これは、ポインターではなく参照を渡すためです考えます。参照は変更できませんが、ポインターは変更できると思いますか?) -
canonRect
へのポインタを渡そうとすると、不正な間接コンパイル時エラーが発生するのはなぜですか? (IE、canonRect(* r);
がmain()
にあった場合)
#1の答えはすでに知っていると思いますが、#2には困惑します-ポインタを渡すのは合法だと思いました。
とにかく... C newbはご容赦ください。
解決
やりたいことはこれだと思います:
void canonRect(struct rect *r) {
struct rect temp;
temp.lowerLeft.x = min(r->lowerLeft.x, r->upperRight.x);
temp.lowerLeft.y = min(r->lowerLeft.y, r->upperRight.y);
temp.upperRight.x = max(r->lowerLeft.x, r->upperRight.x);
temp.upperRight.y = max(r->lowerLeft.y, r->upperRight.y);
*r = temp;
}
上記のコードでは、rect型の* rをrect型のtempに設定しています。
Re 1:rが指しているものを変更したい場合は、ポインターへのポインターを使用する必要があります。それが本当にあなたが望むものである場合(上記を参照、それは本当にあなたが望むものではありません)、あなたはそれをヒープ上の何かに向けるのを確実にしなければなりません。 「new」またはmallocで作成されていないものをポイントすると、スコープから外れ、その変数に使用されなくなったメモリをポイントします。
なぜコードがr =&amp; tempで動作しないのですか?
rはrect *型であるため。つまり、rは、rectを含むメモリのアドレスを保持する変数です。 rが指しているものを変更した場合、それで問題ありませんが、渡された変数は変更されません。
Re 2:*型宣言で使用されていないときは、単項演算子の逆参照です。これは、ポインターのアドレス内にあるものを検索することを意味します。したがって、* rを渡すことで、ポインターをまったく渡しません。 rはポインターではないため、見た目では無効な構文です。
他のヒント
canonRectメソッドが終了し、無効なメモリを指すようになるとすぐに、変数 'struct rec temp'がスコープから外れることにも注意してください。
'dereference'演算子( *
)と 'address of'演算子(&amp;
)を混同しているようです。
&amp; r
を記述すると、rのアドレスを取得してrへのポインターを返します(ポインターは単なる変数のメモリアドレスです)。したがって、実際には関数にポインターを渡します。
* r
を記述するとき、rを逆参照しようとしています。 rがポインターの場合、rが指している値を返します。しかし、rはポインターではなく、rectであるため、エラーが発生します。
事態をより混乱させるために、ポインター変数を宣言するときに *
文字も使用されます。この関数宣言では:
void canonRect(struct rect *r) {
r
は、 struct rect
へのポインターとして宣言されます。これは、次のように *
を使用することとはまったく異なります。
canonRect(*r);
どちらの場合も、*文字はまったく異なるものを意味します。
まず、K&amp; R cには「参照」という概念はなく、単なるポインターがあります。 &amp;
演算子は、「アドレスを取得」を意味します。
第二に、 cannonRect()
の r
はローカル変数であり、 r
の r
ではありません main()
。ローカルの r
が指す場所を変更しても、呼び出しルーチンの r
には影響しません。
最後に、既に述べたように、ローカルの struct rect
はスタックに割り当てられ、閉じ括弧でスコープ外になります
パラメータを(概念的に)関数に渡すことができるさまざまな方法を調べたい場合があります。 Cは call-by-value であるため、rectにポインターを渡すとポインターのコピーを渡す関数。関数が値rに(間接的ではなく)直接行う変更は、呼び出し元には見えません。
関数が呼び出し側に新しい構造体を提供するようにする場合、2つの方法があります。 1.四角形を返すことができます: 2. rectへのポインターへのポインターを渡すことができます:
最初の方法はより自然です:
struct rect* canonRect(struct rect* r)
{
struct rect* cr = (struct rect*) malloc(sizeof(struct rect));
...
return cr;
}
2番目の方法は次のとおりです。
void canonRect(struct rect** r)
{
*r = (struct rect*) malloc(sizeof(struct rect));
}
そして発信者は次を使用します:
canonRect(&r);
ただし、呼び出し元は元のポインターを失い、構造体をリークしないように注意する必要があります。
どの手法を使用する場合でも、関数はmallocを使用してヒープ上の新しい構造体にメモリを割り当てる必要があります。関数が戻るとメモリが無効になるため、構造体を宣言するだけではスタック上のスペースを割り当てることはできません。
2.canonRectへのポインターを渡そうとすると、不正な間接コンパイル時エラーが発生するのはなぜですか? (IE、canonRect(* r); main()があった場合)
というのは、ポインターの目的ではないからです。