2

私はCで書いていて、GCCでコンパイルしています。

ポイントを宣言するより良い方法はありますか?ポイントが配列であることに驚きました。配列のように見えるようにポイントを宣言する方法はありますか?

typedef struct Span
{
    unsigned long lo;
    unsigned long hi;
} Span;

typedef struct Series
{
    unsigned long *points;
    unsigned long count;
    unsigned long limit;
} Series;

void SetSpanSeries(Series *self, const Span *src)
{
    unsigned long *points;

    if (src->lo < src->hi )
    {

        // Overlays second item in series.
        points = self->points;  // a pointer in self structure
        points[0] = src->lo;
        points[1] = src->hi;
        self->count = 1;
    }
}

ここで、ポイントが配列である構造を指しているとしましょう。

typedef struct Span
{
    unsigned long lo;
    unsigned long hi;
} Span;


span *points[4];

これらのコード行をどのように記述すればよいですか?私はこれを正しく理解しましたか?

points = self->points;  // a pointer in self structure
points[0].lo = src->lo;
points[0].hi = src->hi;
4

3 に答える 3

3

宣言unsigned long *pointsとともに、pointsはポインタです。配列の先頭を指します。arr[x]はと同じ*(arr + x)であるためarr、配列(この場合、配列のアドレスをx取得し、を追加し、「ポインター」を逆参照します)またはポインター(この場合、ポインター値を取得し、を追加xし、逆参照します)のどちらであるかポインタ)、arr[0]それでも同じ配列アクセスを取得します。

この場合、配列として使用していないため、配列として宣言することはできませんpoints配列を指すポインターとして使用しています。ポインタは浅いコピーです。ポインタが指すデータを変更すると、元のデータが変更されます。通常の配列を作成するには、ディープコピーを実行する必要があります。これにより、での変更が配列に影響を与えるのを防ぐことができます。これは、最終的には必要なことです。pointerself

実際、次のことなしに全体を書き直すことができますpoints

void SetSpanSeries(Series *self, const Span *src)
{
    if (src->lo < src->hi )
    {
        self->points[0] = src->lo;
        self->points[1] = src->hi;
        self->count = 1;
    }
}

2番目の例に関しては、はい、points[0].lo正しいです。points->loにアクセスしているだけであれば、これも正しいでしょうpoints[0]。(または完全self->points[0].loに取り出す場合。)points

于 2012-07-17T02:50:44.387 に答える
1

ポインタを配列として扱う機能は、ほとんどのC初心者を間違いなく混乱させます。配列は、関数に引数として渡されるとポインターに減衰することさえあり、配列とポインターは完全に交換可能であるという印象を与えます-それらはそうではありません。優れた説明は、エキスパートCプログラミング:ディープCシークレットにあります。(これは私のお気に入りの本の1つです。Cを理解するつもりなら、強くお勧めします。)

とにかく、書き込みpointer[2]はと同じです*(pointer+2)-配列構文は、ほとんどの人が読み取り(および書き込み)するのがはるかに簡単です。

この変数を使用して別のメモリブロック(のポインタ)に簡単*pointsにアクセスできるため、配列のベースを他の変数に再割り当てできないため、ローカル変数に配列を使用することはできません。次の違法なコードを検討してください。pointsstruct Series

int foo[10];
int *bar;
int wrong[10];

bar = foo; /* fine */
wrong = foo; /* compile error -- cannot assign to the array 'wrong' */

このコードを書き直すための別のオプションは、一時変数を削除することです。

if (src->lo < src->hi) {
    self->points[0] = src->lo;
    self->points[1] = src->hi;
    self->count = 1;
}

一時変数が読みやすさに役立つかどうかはわかりません。多くの文字を追加する代わりに、数文字を入力する手間が省けました。(そして、紛らわしい変数も。)

于 2012-07-17T02:51:42.737 に答える
0

真ん中のセクションでは、ポイントは構造体スパンへのポインターの配列4であると言います。3番目のセクションでは、self-> pointsからポイントを割り当てています(つまり、ポイントの以前の値、つまりその配列が失われています)。次に、それがstruct Spanの配列であり、struct Spanへのポインタの配列ではないかのように、ポイントを逆参照します。

他の作品では、タイプを混合しているため、これはコンパイルできません。そうでない場合でも、points変数の定義によって割り当てられたメモリを上書きしています。

シリーズの定義を提供すると、何が起こっているのかを説明するのに役立つ場合があります。

しかし、確かに最初の例では、ポイントはおそらくあるはずですが、Span *pointsシリーズを見なければ、確実に言うことはできません。

于 2012-07-17T02:51:19.220 に答える