为什么我们需要把*用于指针

时间:2013-09-22 09:30:44

标签: c++ c pointers

当我突然想到指针只是一个存储变量的内存地址的变量时,我正在阅读有关指针的内容,因此每个整数应该作为指针。 然后我创建了一个小程序,它发出警告,但它有点工作。

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

为什么j-i = 4而不是16?

7 个答案:

答案 0 :(得分:16)

  

为什么我们需要将*用于指针

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

  

那么,为什么要添加额外的*如果正常的int工作呢?

因为“普通”int不起作用。也不是“异常”int

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

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


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

See this question有关指针算法的更多信息。


哦,从技术上讲,你的第一个程序有未定义的行为,因为打印指针是使用%p转换说明符完成的,但你使用%d用于{{1 }}。你的第一个程序是这样的:

int

(另请注意投射到printf("%d\n%d\n%p", i, j, (void *)&i); - 这是少数情况下需要<{1}}强制转换的情况之一,否则你再次拥有UB。)< / p>

答案 1 :(得分:7)

具有类型安全性。即使用一件事时不应该用来做其他事情。

请参阅http://en.wikipedia.org/wiki/Type_safety

答案 2 :(得分:3)

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

在汇编级别,您可以将地址视为int并使用它们执行任何类型的诡计,但C语言比汇编语言高得多。在编程语言环境中它意味着什么是“高级”?它是“高级抽象”的缩写,这意味着它是一种更接近人类写作和思考的语言。

从某种意义上说,这都是关于“抽象”的。比如想一辆车。您不需要知道所有血腥工程细节,只是为了安全驾驶它。与机械工程师相比,您将汽车视为“更高层次的抽象”。为什么这有用?因为你的大脑可以自由地集中精力驾驶你而不会发生车祸,而不是被迫想到,例如,发动机中每个齿轮每分钟转数需要多少转。

这个比喻对于编程语言也是有效的:抽象是有用的,因为它们可以让你不遗余力地思考底层实现的每个微小细节。一个指针 一个抽象(虽然不是一个非常高级别的,与你在更现代的语言中找到的相比):它是间接引用某事物的原型模型。在引擎盖下,它可以实现为地址,句柄或整体不同的东西,但它的语义由标准描述(和强制)。因此,您可以避免许多问题,这些问题是汇编程序员的噩梦,特别是在切换平台或架构时:指针也可以帮助您制作可移植程序。

答案 3 :(得分:1)

指针并不总是简单的整数。它们是绝大多数当前实现的整数,但它们可能更复杂。一个例子是为8086处理器完成的实现。一个简单的整数指针仅限于访问64k地址空间。为了应对这个C编译器,将实现不同的内存模型。一个微小的内存模型将使用简单的整数作为指针,并且对于程序代码,数据和堆栈组合将限制为最大64k。一个小的内存模型也会使用简单的整数,但是将代码和代码分开。数据进入一个段,堆栈进入另一个段。这允许128k节目。其他内存模型将使用由段组成的指针:偏移整数对,允许更大的程序大小。底线是指针从其实现中抽象出内存位置的概念。

答案 4 :(得分:0)

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

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

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

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

答案 5 :(得分:0)

指针和整数具有不同的类型,因为它们是两个不同的东西,即使指针在许多体系结构上实现为整数。但是考虑一下x86_64架构,有些实现的整数是64位宽,指针是32位宽。

答案 6 :(得分: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)

相关问题