2

私はこのコードを持っています:


     char *name[] = { "a1", "b2", "c3", "d4" };
     printf("%s\n", *name); //the critical line

関連critical line

この形式では、出力は単純です: a1. を次のように置き換えるcritical lineと:

printf("%s\n", ++*name);

出力は1です。今まではすべて良かったと思います。

nameそれぞれ最初の文字列へのポインタであることを考慮して、次の"a1"ように置き換えcritical lineます。

printf("%s\n", ++name);

出力として結果が得られることを願っています"b2"。しかし、私はこのエラーが発生します:

../src/test.c:32: error: lvalue required as increment operand.

質問++*name:が正当である理由が理解できません。name最初の文字列へのポインターであり、そうで++nameはありません。私の意見では、は次の文字列に++name移動する必要があります。name私の理解力のどこが欠けているのか、誰か説明してもらえますか?

4

4 に答える 4

3

を記述する++nameと、配列 は配列の最初の要素へのポインターnameに変換されます。この変換の結果は左辺値ではなく、またはそれ以外の方法で変更することはできません。代わりに、正しいものを出力する と書くことができます。が配列の場合、その配列 [*] 以外を参照するように変更する方法はありません。++name+1name

以下も考慮してください。

char **p = name;   // OK, `name' converts to pointer
++p;               // OK, `p' is an lvalue
++(p+1);           // not OK, `p+1' is not an lvalue
++((char**)p);     // not OK, `(char**)p' is not an lvalue

++*name;           // OK, `*name' is an lvalue

大まかに言えば、「左辺値」はオブジェクトを参照する式であり、「左辺値ではない」は値を持つ式です。オブジェクトと値の違いは、オブジェクトは値 (一度に 1 つの値) を格納する場所であるということです。値は決して変更できませんが、オブジェクトは変更できる場合があります。

左辺値であるが現在の値が必要な部分式がある場合はいつでも、オブジェクトは読み取り/ロード/呼び出したいものです。C ++では、これは「左辺値から右辺値への変換」と呼ばれます.Cで「部分式の評価」以外に呼び出されたかどうかは思い出せません.

name[*]他の何かを参照する、内部スコープ内の別の変数でそれを隠すことができます。しかし、それはまだアウターnameを変更しておらず、一時的に隠しているだけです。

于 2012-08-31T11:59:24.400 に答える
2

namesizeofは配列であるため、 or演算子のオペランドとして使用する場合を除き&、配列 object の初期メンバーへのポインターとして評価され、lvalue ではありません

nameしたがって、次のような演算子を使用して直接変更することはできません++(後置インクリメント演算子には、オペランドとして変更可能な左辺値が必要であることを思い出してください)。それ以外の場合は、一時ポインターを使用できます (p次の例)。

#include <stdio.h>

const char *name[] = { "a1", "b2", "c3", "d4" };
const char **p = name;
printf("%s\n", *p); /* Output: a1 */
*++p; /* or *p++ */
printf("%s\n", *p); /* Output: b2 */
于 2012-08-31T11:53:09.260 に答える
1

まず、次の事実に完全に満足し、自信を持っていることを確認してください。配列はポインターではありません。

第二に、 には何がありnameますか?配列は、最初の要素へのポインターに崩壊します。減衰後、式nameは型char **( の配列の最初の要素へのポインタ) を持ちますchar*。しかし、減衰した式は右辺値です。変更することはできません!そして当然のことながら、固定値へのポインタであるポインタを変更するのは意味がないためです。配列。

したがって、減衰の結果であるポインターを直接インクリメントすることはできません。 or (プリミティブ型を値で返す場所) [おっと、これは C++ 向けの譲歩でした]nameとは言えません。++5++foo() foo

あなた言えることはこれです:

char ** np = name;
++np;
printf("%s", *np);

これは print と同じ効果name[1]がありますが、2 番目の配列要素へのポインターを保持する変数もあります。

于 2012-08-31T11:59:53.527 に答える
1

name は最初の要素のアドレスを指しますが、 name はtype char *ではなくchar *[4]です。したがって、sizof(name) == sizeof(char *)*4

ポインターをインクリメントするということは、常にポインターが指すデータのサイズを追加することを意味します。したがって、インクリメントした後、配列全体の後ろを指します。あなたが持っているかのように

    int i, a;
    int *p = &i;
    p++;

p はiの後ろを指します。コンパイラが i の後ろに a を置くことを決定した場合、それは a を指します。

また、配列には4つの文字列ではなく、4つのポインターのみが含まれていることに注意してください。上記のように、これらの文字列が実際にどこにあるかはコンパイラの選択です。したがって、最初の文字列の末尾が必ずしも 2 番目の文字列の先頭の隣にあるとは限りません。特に、後で name[1] に他の値 (文字列アドレス) を割り当てる場合。したがって、 name をchar **にキャストすることはできますが、 にキャストしないでくださいchar *。前者をインクリメントすると、2 番目の要素 (2 番目の char* ポインター) がポイントされます。

于 2012-08-31T12:15:09.003 に答える