13

これはばかげた質問のように聞こえるかもしれませんが、私はまだ C を学んでいるので、ご容赦ください。:)

私は K&R (構造体) の第 6 章に取り組んでおり、これまでのところ、この本は大きな成功を収めています。私は構造体をかなり扱うことに決めたので、この章の早い段階で point と rect の例を使って多くの作業を行いました。私が試したかったことの 1 つは、canonrectポインターを介して関数 (第 2 版、p 131) の動作を変更することvoidでした。

私はこれを機能させていますが、皆さんが私を助けてくれることを望んでいた問題に遭遇しました。canonRect一時的な四角形オブジェクトを作成し、その変更を実行してから、渡されたポインターを一時的な四角形に再割り当てして、コードを簡素化したいと考えていました。

しかし、そうすると、rectは変わりません。代わりに、渡された rect のフィールドを手動で再設定していることに気付きますが、これはうまくいきます。

コードは次のとおりです。

#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;
}

だからここに質問があります:

  1. なぜ機能しr = &temp;ないのですか?(これは、ポインターではなく参照を渡すためだと思います。参照は変更可能ではなく、ポインターは変更可能であると考えるのは正しいですか?)
  2. へのポインターを渡そうとすると、不正なインダイレクションのコンパイル時エラーが発生するのはなぜcanonRectですか? (IE、私が持っていcanonRect(*r);た場合main()。)

1. の答えはすでにわかっていると思いますが、2. は私を困惑させます。ポインターを渡すことは合法だと思っていました。

とにかく... Cの初心者を許してください。

4

6 に答える 6

18

あなたがやりたいことはこれだと思います:

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 = &temp で機能しないのはなぜですか?

r は rect* 型だからです。つまり、r は、メモリに rect が含まれるメモリ アドレスを保持する変数です。r が指すものを変更しても問題ありませんが、渡された変数は変更されません。

Re 2: * 型宣言で使用されていない場合は、逆参照単項演算子です。これは、ポインターのアドレス内にあるものを検索することを意味します。したがって、*r を渡すことで、ポインターをまったく渡していません。r はポインターではないため、これは無効な構文です。

于 2008-11-01T21:41:50.237 に答える
12

また、そのメソッド canonRect が終了するとすぐに変数「struct rec temp」が範囲外になり、無効なメモリを指していることにも注意してください。

于 2008-11-01T21:43:50.560 に答える
5

「逆参照」演算子 ( *) と「アドレス」演算子 ( ) を混同しているようです&

を書く&rと、それは r のアドレスを取得し、r へのポインターを返します (ポインターは、変数の単なるメモリ アドレスです)。したがって、実際にはポインターを関数に渡しています。

を書くとき*r、あなたは r を逆参照しようとしています。r がポインタの場合、r が指している値が返されます。しかし、r はポインターではなく、rect であるため、エラーが発生します。

さらにややこしいことに、この*文字はポインタ変数を宣言するときにも使用されます。この関数宣言では:

void canonRect(struct rect *r) {

rへのポインタとして宣言されていますstruct rect*これは、次のように使用するのとはまったく異なります。

canonRect(*r); 

どちらの場合も、* 文字はまったく別のものを意味します。

于 2008-11-01T21:53:53.957 に答える
2

パラメータを(概念的に)関数に渡すことができるさまざまな方法を読みたいと思うかもしれません。C はcall-by-valueであるため、rect へのポインターを関数に渡すと、ポインターのコピーが渡されます。関数が値 r に直接 (間接的ではなく) 加えた変更は、呼び出し元には表示されません。

関数が呼び出し元に新しい構造体を提供するようにするには、2 つの方法があります: 1. rect を返す: 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 を使用してヒープ上の新しい構造体にメモリを割り当てる必要があります。関数が戻るとそのメモリが無効になるため、構造体を宣言するだけではスタックにスペースを割り当てることはできません。

于 2008-11-01T21:54:45.220 に答える
2

まず、K&R c には「参照」の概念がなく、ポインタだけです。&演算子は「~のアドレスを取る」という意味です。


次に、rincannonRect()はローカル変数であり、in ではありません。ローカル ポイントの場所を変更しても、呼び出し元のルーチンでは影響を受けません。rmain()rr


最後に、すでに述べたように、ローカルstruct rectはスタックに割り当てられ、右中括弧で範囲外になります。

于 2008-11-01T21:56:22.877 に答える
-1

2.canonRect へのポインターを渡そうとすると、無効な間接参照コンパイル エラーが発生する可能性があるのはなぜですか? (つまり、main() に canonRect(*r); がある場合)

なぜなら、それはpointerの目的ではないからです。

于 2008-11-04T14:13:43.603 に答える