在关于C的一个有信誉的来源中,在讨论&操作符后给出了以下信息:

... 有点不幸的是,术语[地址的]仍然存在,因为它混淆了那些不知道地址是关于什么的人,并误导了那些知道地址的人:将指针视为地址通常会导致悲伤……

我读过的其他材料(来自同样有名望的来源,我想说)总是毫不掩饰地将指针和&操作符作为内存地址。我很愿意继续寻找事情的真相,但当有信誉的消息来源不同意时,这有点困难。

现在我有点困惑了——如果指针不是内存地址,那么它到底是什么?

P.S.

作者后来说:……不过,我将继续使用“地址”这个术语,因为发明一个不同的(术语)会更糟糕。


当前回答

A pointer value is an address. A pointer variable is an object that can store an address. This is true because that's what the standard defines a pointer to be. It's important to tell it to C novices because C novices are often unclear on the difference between a pointer and the thing it points to (that is to say, they don't know the difference between an envelope and a building). The notion of an address (every object has an address and that's what a pointer stores) is important because it sorts that out.

然而,标准在特定的抽象层次上进行讨论。作者所说的那些“知道地址是关于什么的”,但对C不熟悉的人,必须在不同的抽象级别上学习地址——也许是通过编写汇编语言。不能保证C实现使用与cpu操作码相同的地址表示(在本文中称为“存储地址”),这些人已经知道。

He goes on to talk about "perfectly reasonable address manipulation". As far as the C standard is concerned there's basically no such thing as "perfectly reasonable address manipulation". Addition is defined on pointers and that is basically it. Sure, you can convert a pointer to integer, do some bitwise or arithmetic ops, and then convert it back. This is not guaranteed to work by the standard, so before writing that code you'd better know how your particular C implementation represents pointers and performs that conversion. It probably uses the address representation you expect, but it it doesn't that's your fault because you didn't read the manual. That's not confusion, it's incorrect programming procedure ;-)

简而言之,C使用了比作者更抽象的地址概念。

The author's concept of an address of course is also not the lowest-level word on the matter. What with virtual memory maps and physical RAM addressing across multiple chips, the number that you tell the CPU is "the store address" you want to access has basically nothing to do with where the data you want is actually located in hardware. It's all layers of indirection and representation, but the author has chosen one to privilege. If you're going to do that when talking about C, choose the C level to privilege!

Personally I don't think the author's remarks are all that helpful, except in the context of introducing C to assembly programmers. It's certainly not helpful to those coming from higher level languages to say that pointer values aren't addresses. It would be far better to acknowledge the complexity than it is to say that the CPU has the monopoly on saying what an address is and thus that C pointer values "are not" addresses. They are addresses, but they may be written in a different language from the addresses he means. Distinguishing the two things in the context of C as "address" and "store address" would be adequate, I think.

其他回答

很难确切地说出这些书的作者到底是什么意思。指针是否包含地址取决于如何定义地址和如何定义指针。

从所有的回答来看,有些人认为(1)地址必须是整数,(2)指针不需要是虚的,因为规范中没有这么说。根据这些假设,显然指针不一定包含地址。

然而,我们看到,虽然(2)可能是真的,(1)可能不一定是真的。根据@ corn秸秆的答案,&被称为操作符的地址,这是怎么回事?这是否意味着规范的作者希望指针包含地址?

我们可以说,指针包含一个地址,但地址不一定是整数?也许吧。

我认为所有这些都是废话连篇的迂腐语义学。实际上,这是毫无价值的。你能想到一个编译器以这样的方式生成代码,指针的值不是一个地址吗?如果有,是什么?我也是这么想的……

我认为这本书的作者(第一个摘录声称指针不一定只是地址)可能指的是指针自带固有类型信息这一事实。

例如,

 int x;
 int* y = &x;
 char* z = &x;

y和z都是指针,但y+1和z+1是不同的。如果它们是内存地址,难道这些表达式不会给你相同的值吗?

在这里,把指针当作地址的想法通常会导致悲伤。之所以会出现bug,是因为人们将指针视为地址,而这通常会导致不幸。

55555可能不是指针,尽管它可能是一个地址,但(int*)55555是一个指针。55555+1 = 55556,但是(int*)55555+1是55559(在sizeof(int)方面的+/-差异)。

在这幅图中,

Pointer_p是一个位于0x12345的指针,它指向0x34567的变量variable_v。

指针是一个保存内存地址的变量,而不是地址本身。但是,您可以解除对指针的引用-并访问内存位置。

例如:

int q = 10; /*say q is at address 0x10203040*/
int *p = &q; /*means let p contain the address of q, which is 0x10203040*/
*p = 20; /*set whatever is at the address pointed by "p" as 20*/

就是这样。就是这么简单。

一个演示我所说内容的程序,其输出如下:

http://ideone.com/rcSUsb

程序:

#include <stdio.h>

int main(int argc, char *argv[])
{
  /* POINTER AS AN ADDRESS */
  int q = 10;
  int *p = &q;

  printf("address of q is %p\n", (void *)&q);
  printf("p contains %p\n", (void *)p);

  p = NULL;
  printf("NULL p now contains %p\n", (void *)p);
  return 0;
}

C指针非常类似于内存地址,但是抽象了与机器相关的细节,以及一些在低级指令集中找不到的特性。

例如,C指针是相对丰富的类型。如果在一个结构数组中增加一个指针,它会很好地从一个结构跳到另一个结构。

指针服从转换规则,并提供编译时类型检查。

有一个特殊的“空指针”值,它在源代码级别是可移植的,但其表示可能不同。如果将值为0的整型常量赋给指针,则该指针的值为空指针。同样,如果你用这种方式初始化一个指针。

指针可以用作布尔变量:如果指针不是null,则为true;如果指针为null,则为false。

在机器语言中,如果空指针是一个有趣的地址,如0xFFFFFFFF,那么您可能必须对该值进行显式测试。C把它藏起来了。即使空指针是0xFFFFFFFF,你也可以使用if (ptr != 0) {/* not null!* /}。

Uses of pointers which subvert the type system lead to undefined behavior, whereas similar code in machine language might be well defined. Assemblers will assemble the instructions you have written, but C compilers will optimize based on the assumption that you haven't done anything wrong. If a float *p pointer points to a long n variable, and *p = 0.0 is executed, the compiler is not required to handle this. A subsequent use of n will not necessary read the bit pattern of the float value, but perhaps, it will be an optimized access which is based on the "strict aliasing" assumption that n has not been touched! That is, the assumption that the program is well-behaved, and so p should not be pointing at n.

在C语言中,指向代码的指针和指向数据的指针是不同的,但在许多体系结构中,它们的地址是相同的。可以开发具有“胖”指针的C编译器,即使目标体系结构没有。胖指针意味着指针不仅仅是机器地址,还包含其他信息,例如用于边界检查的被指向对象的大小信息。可移植编写的程序将很容易移植到这样的编译器。

所以你可以看到,在机器地址和C指针之间有很多语义上的区别。

地址用于标识一个固定大小的存储空间,通常为每个字节,作为一个整数。这被精确地称为字节地址,它也被ISO c使用。可以有一些其他方法来构造地址,例如为每一位。然而,只有字节地址是如此经常使用,我们通常省略“字节”。

从技术上讲,一个地址在C中从来都不是一个值,因为在(ISO) C中术语“值”的定义是:

对象的内容在解释为具有特定类型时的精确含义

(我强调了一下。)然而,在C语言中没有这样的“地址类型”。

指针不一样。指针是C语言中的一种类型。有几种不同的指针类型。它们不一定遵守相同的语言规则集,例如++对int*类型值和char*类型值的影响。

C语言中的值可以是指针类型。这叫做指针值。需要明确的是,指针值在C语言中不是指针。但是我们习惯把它们混在一起,因为在C语言中,它不太可能是模棱两可的:如果我们把表达式p称为“指针”,它只是一个指针值,而不是一个类型,因为C语言中的命名类型不是由表达式表示,而是由type-name或typedef-name表示。

其他一些事情是微妙的。作为C语言的使用者,首先要知道object是什么意思:

数据存储在执行环境中的区域,其中的内容可以表示 值

对象是表示特定类型的值的实体。指针是一种对象类型。因此,如果我们声明int* p;,则p表示“指针类型的对象”,或“指针对象”。

Note there is no "variable" normatively defined by the standard (in fact it is never being used as a noun by ISO C in normative text). However, informally, we call an object a variable, as some other language does. (But still not so exactly, e.g. in C++ a variable can be of reference type normatively, which is not an object.) The phrases "pointer object" or "pointer variable" are sometimes treated like "pointer value" as above, with a probable slight difference. (One more set of examples is "array".)

由于指针是一种类型,而地址在C语言中实际上是“无类型的”,因此指针值大致“包含”一个地址。指针类型的表达式可以产生一个地址,例如。

Iso c11 6.5.2.3

一元&操作符产生其操作数的地址。

请注意,这个措辞是由WG14/N1256引入的,即ISO C99:TC3。在C99中有

一元&操作符返回其操作数的地址。

它反映了委员会的观点:地址不是由一元操作符&返回的指针值。

尽管有上述措辞,但即使在标准上也存在一些混乱。

Iso c11 6.6

地址常量是一个空指针,一个指向左值的指针,该左值指定一个static对象 存储持续时间,或指向函数指示符的指针

Iso c++ 11 5.19

3.一个地址 常量表达式是指针类型的prvalue核心常量表达式,计算结果为对象的地址 具有静态存储持续时间的对象,转换为函数的地址、空指针值或prvalue核心 类型std::nullptr_t. ...的常量表达式

(最近的c++标准草案使用了另一种措辞,所以不存在这个问题。)

实际上,C中的“地址常量”和c++中的“地址常量表达式”都是指针类型的常量表达式(或者至少从c++ 11开始是“类指针”类型)。

内置的一元&运算符在C和c++中被称为“address-of”;类似地,std::addressof是在c++ 11中引入的。

这些命名可能会带来误解。结果表达式是指针类型的,所以它们被解释为:结果包含/产生一个地址,而不是一个地址。