指针地址之间的区别

时间:2013-10-13 15:57:31

标签: c pointers memory-address

我是一名Java程序员,最近和C一起玩游戏很有趣。现在我正在学习地址和指针,这对我来说有点混乱。这是我的问题。请参阅以下两段代码。

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

我想知道函数withinArray(),为什么我们可以直接得到x值,即9?但是对于其他方法,我们必须首先将doubPtr转换为int,然后我们才能获得int中指针之间的差异?

根据我的理解,在doubleSize()中,doubPtr2-doubPtr1 = 1表示内存中指针地址的差异为1。但为什么withinArray()不需要这样做?

1 个答案:

答案 0 :(得分:3)

两个指针之间的差异为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正确指出他的comment时,两个指针之间的差异只是在指针属于同一类型并且指向同一个数组的两个元素时正式定义,或者指向超出数组末尾的一个元素。请注意,标准C要求给出:

int a[100];

生成地址int *ip = &array[100];是安全的,即使读取或写入ip指向的位置是不安全的。存储在ip中的值可用于比较。

您还正式无法减去两个void *值,因为类型void没有大小(这就是为什么我的示例使用强制转换为char *,而不是void *) 。注意:除非您在选项中包含void *,否则GCC不会反对减去两个-pedantic值。


  

你知道为什么doubPtr2 - doubPtr1(在我的第二种方法中)的值与x = ptr - a不同(在我的第一种方法中)?

假设intArraya,那么这段代码:

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

编译我通常会修复的警告(将p1p2的类型更改为uintptr_t,包括<inttypes.h>,并使用"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

(区别在于为p1p2打印的十六进制数字。)

我认为你的困惑是为什么int代码打印9而不是36,而double代码打印8而不是1。

答案是,当你减去两个指针时,结果是以指向的对象的大小为单位给出的(我似乎记得在我的开头句中说过)。

执行doubPtr2-doubPtr1时,返回的距离以两个地址之间的double值的数量为单位。

但是,转换为整数会丢失类型信息,因此您实际上在整数中有两个指针的char *(或void *)地址,并且字节地址确实相隔8。

如果我们制作两个对称的例程,信息就更清晰了:

#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

请记住Polya在How to Solve It中的建议: