5

当我突然想到指针只是一个存储变量内存地址的变量时,我正在阅读指针,所以每个整数都应该作为指针工作。然后我创建了一个小程序,它发出警告,但它以某种方式工作。

int main()
{
    int i,j;
    i=3;
    j=&i;
    printf("%d\n%d\n%d",i,j,&i);
    return 0;
}

输出是

3
1606416600
1606416600

那么,如果正常的 int 起作用,为什么要多放一个 * 呢?

另一个问题是关于以下程序的输出

int main()
{
    int a[] = {1,2,3,4,5,6,7};
    int *i,*j;
    i=&a[1];
    j=&a[5];
    printf("%d\n%d\n%d",j,i,j-i);
    return 0;
}

输出 :

1606416580
1606416564
4

为什么 ji = 4 而不是 16?

4

7 回答 7

16

为什么我们需要把*作为指针

因为语言规范是这么说的。

那么,如果正常的 int 起作用,为什么要多放一个 * 呢?

因为“正常”int不做这项工作。“异常”也没有int

指针是一种单独的类型。难怪人脑可以很容易地将它们想象成一个称为“内存”的字节数组的索引,但这不一定是计算机和编译器所做的。C标准说指针之间的转换int是一个实现定义的操作。

如果您使用内置的 int 类型或,则可以存储指针而不会丢失数据- 但这些都不能保证是 an (或 an ,就此而言)。intptr_tuintptr_tintunsigned int


至于你的第二个问题:因为这就是指针算术的定义方式。它是这样定义的,因为这就是它的逻辑和直观。如果p2 = p1 + 4p2 - p1则为 4 而不是 16。

有关指针算术的更多信息,请参阅此问题


哦,从技术上讲,您的第一个程序具有未定义的行为,因为打印指针是使用%p转换说明符完成的,但您使用%d的是 for int。您的第一个程序将是正确的,如下所示:

printf("%d\n%d\n%p", i, j, (void *)&i);

(还要注意演员表——这是void *少数需要演员表的情况之一,否则你又会有 UB。)void *

于 2013-09-22T09:35:14.677 回答
7

这取决于类型安全。即在不应该用来做其他事情的时候使用一件事。

http://en.wikipedia.org/wiki/Type_safety

于 2013-09-22T09:34:53.357 回答
3

(添加到 @H2CO3 和 @EdHeal 已经很好的答案。)

在汇编级别,您可以将地址视为 int 并对它们进行任何类型的恶作剧,但 C 是一种比汇编高级得多的语言。在编程语言的上下文中,“高级”是什么意思?它是“high level of abstraction”的缩写,意思是它是一种更接近人类书写和思考方式的语言。

从某种意义上说,这都是关于“抽象”的。以汽车为例。您无需了解所有血腥的工程细节即可安全驾驶。与机械工程师所必须的相比,您将汽车视为“更高层次的抽象”。为什么这很有用?因为你的大脑可以自由地专注于开车回家而不发生车祸,而不是被迫去想,比如说,发动机中的每个齿轮每分钟要转多少转。

这个比喻也适用于编程语言:抽象很有用,因为它们让您不必费力去思考底层实现的每一个微小细节。指针一种抽象(虽然不是一个非常高级的抽象,与您在更现代的语言中发现的相比):它是对某物的间接引用的原型模型。在底层,它可以作为地址、句柄或完全不同的东西来实现,但它的语义是由标准描述(和强制)的。因此,您可以避免许多汇编程序员的噩梦,尤其是在切换平台或架构时:指针还可以帮助您制作可移植的程序。

于 2013-09-22T09:52:30.400 回答
1

指针并不总是简单的整数。它们在绝大多数当前实现中都是整数,但它们可能更复杂。一个例子是为 8086 处理器完成的实现。一个简单的整数指针将仅限于访问 64k 地址空间。为了应对这种情况,C 编译器将实现不同的内存模型。一个微型内存模型将使用简单的整数作为指针,并将程序代码、数据和堆栈组合的最大值限制为 64k。小型内存模型也将使用简单的整数,但将代码和数据拆分为一个段,将堆栈拆分为另一个段。这允许 128k 程序。其他内存模型将使用由段组成的指针:允许更大程序大小的整数对。

于 2013-09-22T10:22:04.380 回答
0

指针确实通常被实现为内存地址,在这种情况下它们可以被认为是整数。正如您所经历的,甚至可以在两者之间进行转换,尽管您必须注意整数类型的大小与内存地址的大小(指针大小)一样大。

使用 * 的原因与类型安全有关。类型的东西int*是“整数的地址”,而类型的东西float*是“浮点数的地址”。如果您以相同的方式处理它们,您将丢失有关地址处值类型的信息。

至于您的第二个问题,这称为指针算术。地址差异将报告为元素大小的乘数,而不是实际字节。因为sizeof(int)在你的情况下是 4,并且地址之间有 16 个字节的差异,所以运算的结果是 16/4 = 4。结果是元素的数量差异,即 5 - 1 = 4。

编辑:虽然 H2CO3 的答案在技术上是正确的,但我认为这种解释更直观。

于 2013-09-22T09:44:18.950 回答
0

指针和整数具有不同的类型,因为它们是两种不同的东西,即使指针在许多体系结构上都实现为整数。但是以 x86_64 架构为例,在某些实现中,整数是 64 位宽,指针是 32 位宽。

于 2013-09-22T10:53:31.250 回答
0

除了地址表示和类型“安全”问题外,指针算术和赋值还需要特定的指针类型(与单个通用指针类型相反)。(您的示例中未使用这些。)

指针算法:

int intArr[2] = {1, 2};
int* pInt0 = &intArr[0];     // points to intArr[0]
int* pInt1 = pInt0 + 1;      // points to intArr[1]

char* pChar0 = pInt0;       // points to the first  byte of intArr[0]
char* pChar1 = pChar0 + 1;  // points to the second byte of intArr[0]

(见 6.3.2.3/7)

通过指针赋值:

int obj = 42;
unsigned char buf[sizeof(obj)];
for(unsigned i = 0; i < sizeof(obj); ++i) {  // like memcpy
    unsigned char* source = i + (unsigned char*)&obj;
    unsigned char* dest = i + buf;
    *dest = *source;    // copies one byte
}

int obj2 = 0;
int* pObj2 = &obj2;

*pObj2 = obj;           // copies sizeof(int) bytes

(见 6.2.6.1/4)

于 2013-09-22T12:08:46.930 回答