2 つのポインターの差が 1 であることは、それらのポインターが、 が指しているオブジェクトのサイズの隣接するメモリ単位を指していることを意味します。
したがって、与えられた:
int i[2];
int *ip0 = &i[0];
int *ip1 = &i[1];
double d[2];
double *dp0 = &d[0];
double *dp1 = &d[1];
安全に書くことができます:
assert((ip1 - ip0) == (dp1 - dp0));
assert(ip1 - ip0 == 1);
assert(dp1 - dp0 == 1);
ただし、安全に次のように書くこともできます。
assert((char *)ip1 - (char *)ip0 == sizeof(int));
assert((char *)dp1 - (char *)dp0 == sizeof(double));
通常、次のように書いても安全であることがわかります。
assert(sizeof(double) != sizeof(int));
ただし、それは標準では保証されていません。
また、Filipe Gonçalvesが彼のコメントで正しく指摘しているように、2 つのポインターの違いは、ポインターが同じ型であり、同じ配列の 2 つの要素を指している場合、または配列の末尾を超えた 1 つの要素を指している場合にのみ、正式に定義されます。 . 標準 C では、次のことが要求されることに注意してください。
int a[100];
int *ip = &array[100];
が指す場所からの読み取りまたは書き込みは安全ではありませんが、アドレスを生成することは安全ip
です。に格納された値ip
は比較に使用できます。
void *
また、型のサイズがないため、2 つの値を正式に減算することもできませんvoid
(これが、私の例char *
で ではなくへのキャストを使用した理由void *
です)。void *
注意: オプションに含めない限り、GCC は 2 つの値の減算に反対しません-pedantic
。
(2 番目の方法で) の値が (最初の方法でdoubPtr2 - doubPtr1
) と異なる理由を知っていますか?x = ptr - a
intArray
それがであると仮定するとa
、このコードは次のようになります。
#include <stdio.h>
static void withinArray(int *a, int *ptr)
{
int x;
printf("ptr is %p\n", (void *)ptr);
printf("a is %p\n", (void *)a);
printf("difference in pointers is: %td\n", ptr - a);
x = ptr - a;
printf("x is %d\n", x);
}
static void doubleSize(void)
{
double doubArray[10];
double *doubPtr1 = doubArray;
double *doubPtr2 = doubArray+1;
int p2 = doubPtr2;
int p1 = doubPtr1;
printf("p1 = 0x%.8X\n", p1);
printf("p2 = 0x%.8X\n", p2);
printf("p2-p1 is %d\n", p2-p1);
printf("doubPtr1 = %p\n", (void *)doubPtr1);
printf("doubPtr1 = %p\n", (void *)doubPtr2);
printf("doubPtr2-doubPtr1 is %td\n", doubPtr2-doubPtr1);
}
int main(void)
{
int a[10];
int *intarray = a;
int *p = intarray + 9;
withinArray(a, p);
doubleSize();
return 0;
}
私が通常修正する警告付きでコンパイルし (フォーマット文字列としてp1
andのタイプp2
をuintptr_t
、 include <inttypes.h>
、および formatに変更し"p1 = 0x%.8" PRIXPTR "\n"
ます)、出力を生成します。
ptr is 0x7fff5c5684a4
a is 0x7fff5c568480
difference in pointers is: 9
x is 9
p1 = 0x5C5684B0
p2 = 0x5C5684B8
p2-p1 is 8
doubPtr1 = 0x7fff5c5684b0
doubPtr1 = 0x7fff5c5684b8
doubPtr2-doubPtr1 is 1
固定コード生成:
ptr is 0x7fff5594f4a4
a is 0x7fff5594f480
difference in pointers is: 9
x is 9
p1 = 0x7FFF5594F4B0
p2 = 0x7FFF5594F4B8
p2-p1 is 8
doubPtr1 = 0x7fff5594f4b0
doubPtr1 = 0x7fff5594f4b8
doubPtr2-doubPtr1 is 1
(違いは、 と で出力される 16 進数の桁数にp1
ありp2
ます。)
あなたの困惑は、コードが 1 ではなく 8 を出力int
するのに、たとえば 36 ではなく 9 を出力する理由にあると思いますdouble
。
答えは、2 つのポインターを減算すると、結果は、ポイントされたオブジェクトのサイズの単位で得られるということです (冒頭の文で言ったことを覚えているようです)。
を実行するdoubPtr2-doubPtr1
と、返される距離は、2 つの住所間の値の数の単位になりdouble
ます。
ただし、整数への変換では型情報が失われるため、整数内の 2 つのポインターのchar *
(またはvoid *
) アドレスが効果的に得られ、バイト アドレスは実際には 8 離れています。
2 つの対称的なルーチンを作成すると、情報がより明確になります。
#include <stdio.h>
#include <inttypes.h>
static void intSize(void)
{
int intArray[10];
int *intPtr1 = intArray;
int *intPtr2 = intArray+1;
uintptr_t p2 = (uintptr_t)intPtr2;
uintptr_t p1 = (uintptr_t)intPtr1;
printf("p1 = 0x%.8" PRIXPTR "\n", p1);
printf("p2 = 0x%.8" PRIXPTR "\n", p2);
printf("p2-p1 is %" PRIdPTR "\n", p2-p1);
printf("intPtr1 = %p\n", (void *)intPtr1);
printf("intPtr1 = %p\n", (void *)intPtr2);
printf("intPtr2-intPtr1 is %td\n", intPtr2-intPtr1);
}
static void doubleSize(void)
{
double doubArray[10];
double *doubPtr1 = doubArray;
double *doubPtr2 = doubArray+1;
uintptr_t p2 = (uintptr_t)doubPtr2;
uintptr_t p1 = (uintptr_t)doubPtr1;
printf("p1 = 0x%.8" PRIXPTR "\n", p1);
printf("p2 = 0x%.8" PRIXPTR "\n", p2);
printf("p2-p1 is %" PRIdPTR "\n", p2-p1);
printf("doubPtr1 = %p\n", (void *)doubPtr1);
printf("doubPtr1 = %p\n", (void *)doubPtr2);
printf("doubPtr2-doubPtr1 is %td\n", doubPtr2-doubPtr1);
}
int main(void)
{
doubleSize();
intSize();
return 0;
}
出力:
p1 = 0x7FFF5C93D4B0
p2 = 0x7FFF5C93D4B8
p2-p1 is 8
doubPtr1 = 0x7fff5c93d4b0
doubPtr1 = 0x7fff5c93d4b8
doubPtr2-doubPtr1 is 1
p1 = 0x7FFF5C93D4B0
p2 = 0x7FFF5C93D4B4
p2-p1 is 4
intPtr1 = 0x7fff5c93d4b0
intPtr1 = 0x7fff5c93d4b4
intPtr2-intPtr1 is 1
How to Solve Itでの Polya のアドバイスを思い出してください。