繁体   English   中英

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

[英]Why do we need to put * for pointer

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

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?

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

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

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

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

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

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


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

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


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

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

(还要注意演员表void * - 这是少数需要演员void *情况之一否则你再次获得UB。)

这取决于具有类型安全性。 即使用一件事,它不应该被用来做其他事情。

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

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

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

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

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

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

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

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

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

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

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

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

指针算术:

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)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM