-2

私は Java プログラマーで、最近 C で遊んでいます。今、私は少し混乱しているアドレスとポインターを学んでいます。これが私の質問です。以下のコードの 2 つのブロックを参照してください。

void withinArray(int * a, int size, int * ptr) {
    int x;
    printf("ptr is %d\n", ptr);
    printf("a is %d\n", a);
    printf("difference in pointers is: %d\n", ptr - a);
    x = ptr - intArray;
    printf("x is %d\n", x);
}

void doubleSize() {
  double doubArray[10];
  double * doubPtr1;
  double * doubPtr2;

  doubPtr1 = doubArray;
  doubPtr2= doubArray+1;
  int p2 = doubPtr2;
  int p1 = doubPtr1;

  printf("p2-p1 is %d\n", p2-p1);
  printf("doubPtr2-doubPtr1 is %d\n", doubPtr2-doubPtr1);
}

int main(void)
{
  int a[10];
  int *intarray = a;
  int *p = intarray + 9;

  printf(withinArray(a, 10, p));
  return 0;
}

function について疑問に思っているのですがwithinArray()、なぜx値を直接取得できるのでしょうか。これは 9 ですか? しかし、他の方法では、最初に変換する必要がdoubPtrあり、次に?intのポインター間の違いを取得できます。int

私の理解では、 in はdoubleSize()doubPtr2-doubPtr1 = 1メモリ内のポインタ アドレスの差が であることを意味します1。しかし、なぜそうwithinArray()する必要がないのでしょうか?

4

1 に答える 1

3

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

私が通常修正する警告付きでコンパイルし (フォーマット文字列としてp1andのタイプp2uintptr_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 のアドバイスを思い出してください。

于 2013-10-13T16:15:20.013 に答える