-2

我是一名 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?但是对于另一种方法,我们必须先转换doubPtrint,然后才能得到指针之间的差异int

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

4

1 回答 1

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在他的评论中正确指出的那样,两个指针之间的区别仅在指针具有相同类型并指向同一数组的两个元素或指向数组末尾之外的一个元素时才正式定义. 请注意,标准 C 要求给出:

int a[100];

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

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


你知道为什么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;
}

编译时会出现我通常会修复的警告(将p1and的类型更改p2uintptr_t、 include<inttypes.h>和 format using"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

(区别在于p1和打印的十六进制数字的数量p2。)

我假设您的困惑是为什么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中的建议:

于 2013-10-13T16:15:20.013 回答