「Teach Yourself C in 21 Days」という本を読んでいます(すでにJavaとC#を習得しているので、ずっと速いペースで動いています)。ポインタの章を読んでいたところ、->
(矢印)演算子が説明なしに表示されました。メンバーと関数を呼び出すために使用されていると思います.
((ドット)演算子と同等ですが、メンバーではなくポインター用です)。しかし、私は完全にはわかりません。
説明とコードサンプルを入手できますか?
「Teach Yourself C in 21 Days」という本を読んでいます(すでにJavaとC#を習得しているので、ずっと速いペースで動いています)。ポインタの章を読んでいたところ、->
(矢印)演算子が説明なしに表示されました。メンバーと関数を呼び出すために使用されていると思います.
((ドット)演算子と同等ですが、メンバーではなくポインター用です)。しかし、私は完全にはわかりません。
説明とコードサンプルを入手できますか?
回答:
foo->bar
はと同じです。つまり、指す構造体から(*foo).bar
呼び出されるメンバーを取得します。bar
foo
->
ていた場合は、はるかに読みやすいと同等であるため、演算子はまったく必要ありませんでしたfoo*.bar
。余分な括弧をすべて含むtypedef-ing関数の全体的な混乱も回避されます。
foo*.bar
、(*foo).bar
両方とも同等foo->bar
ですか?どうFoo myFoo = *foo; myFoo.bar
ですか?
はい、それだけです。
参照ではなくポインタである構造体/クラスの要素にアクセスする場合、それは単なるドットバージョンです。
struct foo
{
int x;
float y;
};
struct foo var;
struct foo* pvar;
pvar = malloc(sizeof(pvar));
var.x = 5;
(&var)->y = 14.3;
pvar->y = 22.4;
(*pvar).x = 6;
それでおしまい!
pvar = &var
か?
答えに「なぜ?」を追加します。
.
は、*
ポインター演算子よりも優先順位が高い標準のメンバーアクセス演算子です。
構造体の内部にアクセスしようとしていて*foo.bar
、それを書き込んだとき、コンパイラは「foo」(メモリ内のアドレス)の「bar」要素が必要だと考え、明らかに、単なるアドレスにはメンバーがありません。
したがって、まずwhith (*foo)
を逆参照し、次にメンバー要素にアクセスするようコンパイラーに要求する必要があります(*foo).bar
。これは書くのが少し扱いにくいので、優れた人々は簡略版を考え出しました。foo->bar
これはポインター演算子によるメンバーアクセスの一種です。
foo->bar
はの省略形です(*foo).bar
。これですべてです。
struct Node {
int i;
int j;
};
struct Node a, *p = &a;
ここでの値にアクセスするi
とj
、私たちは、変数を使用することができますa
し、ポインタをp
次のようにa.i
、(*p).i
とp->i
すべて同じです。
これ.
が「直接セレクター」と->
「間接セレクター」です。
まあ私も何かを追加する必要があります。配列はポインタであり、構造はそうではないため、構造は配列とは少し異なります。ので注意してください!
私がこの役に立たないコードを書いたとしましょう:
#include <stdio.h>
typedef struct{
int km;
int kph;
int kg;
} car;
int main(void){
car audi = {12000, 230, 760};
car *ptr = &audi;
}
ここで、ポインターは構造体変数ptr
のアドレス(!)を指していますaudi
が、アドレス構造体の横にもデータのチャンク(!)があります。データのチャンクの最初のメンバーは構造体自体と同じアドレスを持ち、このようなポインターを逆参照するだけでデータを取得できます*ptr
(中括弧は不要)。
あなたが最初のものよりも他のメンバーをアセスしたい場合しかし、あなたは次のように指示を追加する必要があり.km
、.kph
、.kg
より多くのベースアドレスにオフセットよりも何もないそのデータのチャンク ...
ただし、優先順位が高いため、*ptr.kg
アクセス演算子.
が逆参照演算子よりも先に評価されるため、書き込むことはできません。ポインタにはメンバーがない*
ため、*(ptr.kg)
これは不可能です。そしてコンパイラはこれを知っているため、エラーを発行します。例:
error: ‘ptr’ is a pointer; did you mean to use ‘->’?
printf("%d\n", *ptr.km);
代わりに、これを使用する(*ptr).kg
と、あなたはにコンパイラを強制する第一のポインタデリファレンスとにアセスを可能にするデータのチャンクと第二メンバーを選択するためにあなたがオフセット(指示)を追加します。
私が作ったこの画像を確認してください:
ただし、メンバーをネストすると、この構文は判読できなくなり、->
導入されました。私は読みやすさは、このようにそれを使用するための唯一の正当な理由だと思うptr->kg
よりも書き込みにはるかに簡単です(*ptr).kg
。
接続をより明確に理解できるように、これを別の方法で記述しましょう。(*ptr).kg
⟹ (*&audi).kg
⟹ audi.kg
。ここで私は最初、実際に使用ptr
される「のアドレスaudi
」、すなわち&audi
と事実「参照」 &
と「間接参照」 *
事業者がお互いアウトをキャンセルします。
実行するには、ジャックのプログラムを少し変更する必要がありました。構造体ポインタpvarを宣言した後、それをvarのアドレスにポイントします。この解決策は、Stephen Kochanのプログラミングin Cの242ページで見つかりました。
#include <stdio.h>
int main()
{
struct foo
{
int x;
float y;
};
struct foo var;
struct foo* pvar;
pvar = &var;
var.x = 5;
(&var)->y = 14.3;
printf("%i - %.02f\n", var.x, (&var)->y);
pvar->x = 6;
pvar->y = 22.4;
printf("%i - %.02f\n", pvar->x, pvar->y);
return 0;
}
これをvimで次のコマンドを使用して実行します。
:!gcc -o var var.c && ./var
出力されます:
5 - 14.30
6 - 22.40
%
現在のファイル名を表すために使用します。このように:!gcc % && ./a.out
#include<stdio.h>
int main()
{
struct foo
{
int x;
float y;
} var1;
struct foo var;
struct foo* pvar;
pvar = &var1;
/* if pvar = &var; it directly
takes values stored in var, and if give
new > values like pvar->x = 6; pvar->y = 22.4;
it modifies the values of var
object..so better to give new reference. */
var.x = 5;
(&var)->y = 14.3;
printf("%i - %.02f\n", var.x, (&var)->y);
pvar->x = 6;
pvar->y = 22.4;
printf("%i - %.02f\n", pvar->x, pvar->y);
return 0;
}
->
オペレータは、よりコードが読みやすくなり*
、いくつかの状況でオペレータ。
例:(EDK IIプロジェクトから引用)
typedef
EFI_STATUS
(EFIAPI *EFI_BLOCK_READ)(
IN EFI_BLOCK_IO_PROTOCOL *This,
IN UINT32 MediaId,
IN EFI_LBA Lba,
IN UINTN BufferSize,
OUT VOID *Buffer
);
struct _EFI_BLOCK_IO_PROTOCOL {
///
/// The revision to which the block IO interface adheres. All future
/// revisions must be backwards compatible. If a future version is not
/// back wards compatible, it is not the same GUID.
///
UINT64 Revision;
///
/// Pointer to the EFI_BLOCK_IO_MEDIA data for this device.
///
EFI_BLOCK_IO_MEDIA *Media;
EFI_BLOCK_RESET Reset;
EFI_BLOCK_READ ReadBlocks;
EFI_BLOCK_WRITE WriteBlocks;
EFI_BLOCK_FLUSH FlushBlocks;
};
_EFI_BLOCK_IO_PROTOCOL
構造体は、4人の関数ポインタメンバーが含まれています。
変数がありstruct _EFI_BLOCK_IO_PROTOCOL * pStruct
、古き良き*
演算子を使用してそのメンバー関数のポインターを呼び出すとします。次のようなコードになります。
(*pStruct).ReadBlocks(...arguments...)
しかし、->
演算子を使用すると、次のように書くことができます。
pStruct->ReadBlocks(...arguments...)
。
どちらが良く見えますか?
ドットは間接参照演算子であり、構造の特定のレコードの構造変数を接続するために使用されます。例:
struct student
{
int s.no;
Char name [];
int age;
} s1,s2;
main()
{
s1.name;
s2.name;
}
このようにして、ドット演算子を使用して構造体変数にアクセスできます
->
ます。また、この質問は4.5年前から回答されています。