【问题标题】:The difference between pointer address指针地址的区别
【发布时间】:2013-10-13 15:57:31
【问题描述】:

我是一名 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() 不需要这样做呢?

【问题讨论】:

  • intArray 中的withinArray 是什么?
  • 另外,printf("%d\n", withinArray(a, 10, p)); 不正确,因为withinArray() 返回void。此外,指针减法的结果类型为ptrdiff_t,使用%td打印。
  • @hacks 抱歉打错了,intArray 应该在这里。

标签: c pointers memory-address


【解决方案1】:

两个指针相差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 *)。注意:除非您在选项中包含 -pedantic,否则 GCC 不会反对减去两个 void * 值。


你知道为什么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,包括&lt;inttypes.h&gt;,并使用"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 中的建议:

【讨论】:

  • 不错的答案。只需为 OP 添加一些信息,因为他是 C 的新手:指针运算应该只在指向同一数组中的元素的指针之间执行(或结束后的 1)。两个指针之间的差异是 ptrdiff_t 类型(在 stddef.h 中定义)
  • @FilipeGonçalves;真的。从另一个指针中减去一个指针的效果是undefined,除非两个指针都指向同一个数组的元素。
  • 感谢您让我知道指针运算只能在同一数组中的 btw 指针中执行...这里快速跟进,您知道为什么 doubPtr2-doubPtr1 的值(在我的第二方法)不同于 x = ptr - intArray(在我的第一种方法中)?谢谢!
  • 我已经对你的意思提出了我的最佳猜测……看看这是否有帮助。
  • @JonathanLeffler 您的回答非常清晰且条理清晰。我感谢您为帮助我所做的努力。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-05
  • 2013-04-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-30
相关资源
最近更新 更多