1

我最近尝试真正掌握 C++ 中的引用和指针,但我有点困惑。我理解*and&运算符可以分别获取地址中的值并获取值的地址,但是为什么不能将它们简单地用于像ints 这样的基本类型?

我不明白为什么你不能,例如,做如下的事情,而不是使用任何奇怪的指针变量创建:

string x = "Hello";
int y = &x; //Set 'y' to the memory address of 'x'
cout << *y; //Output the value at the address 'y' (which is the memory address of 'x')

理论上,我认为上面的代码应该输出“x”的值。'y' 包含'x' 的内存地址,因此'*y' 应该是'x'。如果这可行(顺便说一下,在尝试编译它时,它不会——它告诉我它不能从字符串转换为 int,这没有多大意义,因为你认为内存地址可能是存储在一个 int 罚款)。

为什么我们需要使用特殊的指针变量声明(例如string *y = &x)?在这里面,如果我们*在上面的示例中按字面意思在指针声明中使用运算符,我们将'y'的值设置为'x'的内存地址,但是稍后当我们想要访问该值时在内存地址 ('&x'),我们可以使用我们之前设置为内存地址的相同的 '*y'。

4

6 回答 6

3

C 和 C++ 在编译时解析类型信息,而不是运行时。甚至运行时多态性也依赖于编译器构建一个函数指针表,其中偏移量在编译时固定。

出于这个原因,程序可以知道cout << *y;正在打印字符串的唯一方法是因为y它被强类型化为指向字符串的指针 ( std::string*)。程序不能仅从地址确定存储在地址处的对象ystd::string. (即使 C++ RTTI 也不允许这样做,您需要足够的类型信息来识别多态基类。)

于 2012-04-21T16:52:22.903 回答
2

简而言之,C 是一种类型化语言。您不能将任意内容存储在变量中。

检查维基百科上的类型安全文章。C/C++ 通过检查操作数和函数参数的类型来防止在编译时出现问题的操作和函数调用(但请注意,使用显式转换可以更改表达式的类型)。

将字符串存储在整数中是没有意义的 -> 就像在其中存储指针没有意义一样。

于 2012-04-21T16:48:40.737 回答
2

简单来说,一个内存地址有一个类型,就是指针。指针不是整数,因此您不能将指针存储在 int 变量中。如果您好奇为什么整数和指针不可替代,那是因为每个整数的大小都是由实现定义的(有一定的限制),并且不能保证它们的大小相同。

例如,正如@Damien_The_Unbeliever 指出的那样,64 位系统上的指针必须是 64 位长,但 int 是 32 位是完全合法的,只要它不长于长且不短于一个简短的。

至于为什么每种数据类型都有自己的指针类型,那是因为每种类型(尤其是用户定义的类型)在内存中的结构不同。如果我们要取消引用无类型(或void)指针,将没有信息表明应该如何解释该数据。另一方面,如果您要创建一个通用指针并消除指定类型的“不便”,那么内存中的每个实体可能都必须与其类型信息一起存储。虽然这是可行的,但它远非高效,而效率是 C++ 的设计目标。

于 2012-04-21T16:53:28.457 回答
1

C++是一种强类型语言,指针和整数是不同的类型。通过制作这些单独的类型,编译器能够检测到误用并告诉您您所做的事情是不正确的。

同时,指针类型维护了指向对象类型的信息,如果你获得了 double 的地址,你必须将它存储在 adouble*中,编译器知道取消引用该指针你会得到 a double。在您的示例代码中,int y = &x; cout << *y;编译器会丢失y 指向的信息,表达式的类型*y将是未知的,并且无法确定operator<<要调用的不同重载中的哪一个。将其与std::string *y = &x;编译器看到的地方进行比较,y它知道它是 astd::string*并且知道取消引用它会得到 a std::string(而不是 double 或任何其他类型),从而使编译器能够静态检查包含y.

最后,虽然您认为指针只是对象的地址并且应该可以用整数类型表示(在 64 位体系结构上必须是int64而不是int),但情况并非总是如此。在不同的体系结构上,指针不能真正用整数值表示。例如,在具有分段内存的体系结构中,对象的地址可以包含一个段(整数值)和该段的偏移量(另一个整数值)。在其他架构上,指针的大小不同于任何整数类型的大小。

于 2012-04-21T17:01:06.253 回答
1

一些非常低级的语言……例如机器语言……完全按照您的描述运行。一个数字就是一个数字,程序员可以在脑海中记住它所代表的含义。一般来说,高级语言的希望是让您远离这种开发风格带来的担忧和潜在的错误。

您实际上可以无视 C++ 的类型安全,但后果自负。例如,我拥有的 32 位机器上的 gcc 将在我运行此命令时打印“Hello”:

string x = "Hello";
int y = reinterpret_cast<int>(&x);
cout << *reinterpret_cast<string*>(y) << endl;

但正如几乎所有其他回答者所指出的那样,不能保证它可以在另一台计算机上运行。如果我在 64 位机器上尝试这个,我会得到:

错误:从 'std::string*' 转换为 'int' 失去精度

我可以通过将其更改为long

string x = "Hello";
long y = reinterpret_cast<long>(&x);
cout << *reinterpret_cast<string*>(y) << endl;

C++ 标准为这些类型指定了最小值,但没有指定最大值,所以当你面对一个新的编译器时,你真的不知道你将要处理什么。请参阅:C++ 标准规定 int、long 类型的大小是多少?

因此,一旦你开始走这条路并“抛弃”语言中的安全性,编写不可移植代码的可能性就很高。 reinterpret_cast是最危险的铸造类型...

什么时候应该使用 static_cast、dynamic_cast、const_cast 和 reinterpret_cast?

但这只是从技术上深入研究“为什么不 int”部分,以防您感兴趣。请注意,正如@BenVoight 在下面的评论中指出的那样,从 C99 开始确实存在一种称为intptr_t的整数类型,它保证可以保存任何指针。因此,与丢失精度相比,丢弃类型信息时会出现更大的问题……比如不小心转换回错误的类型!

于 2012-04-21T17:22:34.970 回答
0

该语言试图保护您避免混淆两个不同的概念——即使在硬件级别它们都只是一组位;

除了需要在调试器的各个部分之间手动传递值之外,您永远不需要知道数值。

在数组的古老用途之外,将 10 加到指针上是没有意义的 - 所以你不应该将它们视为数值。

通过编译器保留类型信息,它还可以防止您犯错误 - 如果所有指针都相等,那么编译器将无法指出您试图取消引用的int内容是指向 a 的指针string

于 2012-04-21T16:57:10.543 回答