我最近试图真正掌握C ++中的引用和指针,我有点困惑。我了解*
和&
运算符,它们可以分别获取地址的值并获取值的地址,但为什么这些运算符不能简单地用于int
等基本类型S'
我不明白为什么你不能,例如,做类似下面的事情而不使用任何奇怪的指针变量创建:
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'的内存地址,但是稍后当我们想要访问内存地址('&amp; x')的值,我们可以使用之前设置为内存地址的'* y'。
答案 0 :(得分:3)
C和C ++在编译时解析类型信息,而不是运行时。甚至运行时多态也依赖于编译器构造一个函数指针表,在编译时修复了偏移量。
因此,程序可以知道cout << *y;
正在打印字符串的唯一方法是因为y
被强类型化为指向字符串的指针(std::string*
)。该程序不能仅从地址确定存储在地址y
的对象是std::string
。 (即使C ++ RTTI不允许这样做,您需要足够的类型信息来识别多态基类。)
答案 1 :(得分:2)
简而言之,C是一种打字语言。你不能在变量中存储任意东西。
查看维基百科上的type safety文章。 C / C ++通过检查操作数和函数参数的类型来防止在编译时出现问题操作和函数调用(但请注意,通过显式强制转换,您可以更改表达式的类型)。
将字符串存储在整数中是没有意义的 - &gt;以同样的方式将指针存储在其中是没有意义的。
答案 2 :(得分:2)
简单地说,内存地址有一个类型,即指针。指针不是整数,因此您不能将指针存储在int变量中。如果你很好奇为什么int和指针是不可替代的,那是因为每个的大小都是实现定义的(有一些限制),并且不能保证它们的大小相同。
例如,正如@Damien_The_Unbeliever所指出的,64位系统上的指针必须是64位长,但是对于32位的int来说完全合法,只要它不长于也不短于短暂。
至于为什么每种数据类型都有自己的指针类型,这是因为每种类型(特别是用户定义的类型)在内存中的结构不同。如果我们要取消引用无类型(或void
)指针,则不会有指示如何解释该数据的信息。另一方面,如果您要创建通用指针并消除指定类型的“不便”,则内存中的每个实体可能必须与其类型信息并存。虽然这是可行的,但它远没有效率,而且效率也取决于C ++的设计目标。
答案 3 :(得分:1)
C ++是一种强类型语言,指针和整数是不同的类型。通过创建这些单独的类型,编译器能够检测到错误并告诉您正在执行的操作是不正确的。
同时,指针类型维护有关指向对象类型的信息,如果获得double的地址,则必须将其存储在double*
中,并且编译器知道解除引用指针,你将到达double
。在您的示例代码int y = &x; cout << *y;
中,编译器会丢失y
指向的信息,表达式*y
的类型将是未知的,它不会能够确定调用operator<<
的哪些不同重载。将其与编译器看到std::string *y = &x;
的{{1}}进行比较,它知道它是y
并且知道解除引用它会导致std::string*
(而不是双精度或任何其他类型) ),使编译器能够静态检查包含std::string
的所有表达式。
最后,当您认为时,指针只是对象的地址,并且应该由整数类型表示(在64位体系结构上必须是y
而不是int64
int
)情况并非总是如此。有不同的体系结构,指针不能通过整数值真正表示。例如,在具有分段存储器的体系结构中,对象的地址可以包含段(整数值)和段中的偏移(另一个整数值)。在其他体系结构上,指针的大小不同于任何整数类型的大小。
答案 4 :(得分: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 ++标准规定了这些类型的最小值,但不是最大值,所以当你面对新的编译器时,你真的不知道你要处理什么。请参阅:What does the C++ standard state the size of int, long type to be?
因此,一旦您开始使用该路线并“丢弃”语言中的安全设备,编写非可移植代码的可能性就会很高。 reinterpret_cast
是最危险的铸造类型......
When should static_cast, dynamic_cast, const_cast and reinterpret_cast be used?
但是,如果您感兴趣的话,那只是技术上深入到“为什么不是int”部分。请注意,正如@BenVoight在下面的注释中指出的那样,确实存在一个称为intptr_t的C99整数类型,它保证保存任何poniter。因此,当您丢弃类型信息而不是丢失精度时会出现更大的问题......比如意外地重新输入错误的类型!
答案 5 :(得分:0)
该语言试图保护您不要混淆两个不同的概念 - 即使在硬件级别它们只是位组;
除了需要在调试器的各个部分之间手动传递值之外,您永远不需要知道数值。
除了古老的数组使用之外,对指针“添加10”没有意义 - 所以你不应该将它们视为数值。
通过编译器保留类型信息,它还可以防止您犯错误 - 如果所有指针都相同,那么编译器就无法帮助指出您尝试取消引用的内容因为int
是指向string
的指针。