常量字符 * 与其他指针不同

Is const char * different from other pointers?

本文关键字:指针 其他 字符 常量      更新时间:2023-10-16

所以我最近一直在研究指针和引用,因为它们在我用来学习OpenGL的资源中经常使用。我注意到const char *指针的行为似乎与其他指针有些不同。为了演示,我创建了这个测试程序:

#include <iostream>
int main() {
int i = 2; 
int *pi;
//const char c = "hello world"; 
const char *pc = "hello world";
pi = &i;
std::cout << "'hello world' type is " << typeid("hello world").name() << std::endl;
std::cout << "pi is " << pi << std::endl;
std::cout << "*pi is " << *pi << std::endl;
std::cout << "pc is " << pc << std::endl;
std::cout << "*pc is " << *pc << std::endl;

return 0;
}

输出:

'hello world' type is char const [12]
pi is 0079FC38
*pi is 2
pc is hello world
*pc is h

由于几个原因,这个结果让我感到困惑。首先,注释掉的行const char c = "hello world"完全失败;为什么?通过查看输出,我们可以看到与"hello world"本身关联的类型是char const [12]。有趣的是,以下行确实有效。但是,与与int指针(pi)关联的输出相比,输出不是我所期望的。即为什么当我打印pc时输出hello world而不是内存地址?(不太重要)然后,为什么当我打印*pc时,输出只是该地址中包含的数据的第一个元素?

不,指向const char *的指针与其他指针的工作方式不同。

代码中的不同之处在于C++输出流(特别是)如何处理const char *与如何处理其他指针。 它通过提供不同的operator<<()重载来实现这一点。

一个重载将const char *作为参数,假设它指向一个以 nul 结尾的char数组(这就是像"Hello World"这样的字符串文字在内存中的表示方式)并输出每char一次,直到达到该 NUL。

另一个重载接受void *,并简单地输出值(即地址)。 这依赖于将指针类型隐式转换为void *

因此,与其他指针行为不同的不是const char *。 事实上,C++输出流处理const char *的方式与其他指针不同(通过提供执行不同操作的不同重载)。

注意:严格来说,如果要打印地址,则应这样做

std::cout << "pi is " << (void *)pi << std::endl;
std::cout << "pc is " << (void *)pc << std::endl;

这将以相同的方式处理两个指针(即打印地址,而不是这些地址的数据),因为然后使用相同版本的operator<<()打印两个指针。

char *类型(非常)经常用于表示"非托管字符串",即以''字符结尾的字符序列(与类std::string相反,它包装了这样的"非托管"字符串)。所以cout照顾了这种常见的用法,因为它为类型char *提供了一个重载,它不将值视为内存地址,而是将值视为指向字符序列的指针。 但这只是(普通)函数重载,并且 - 除了一些关于对齐的特殊处理 - 指向字符的指针和指向 int 的指针之间没有区别。

首先,注释掉的行const char c = "hello world"失败 完全;为什么?通过查看输出,我们可以看到类型 与"Hello World"本身相关的是Char Const[12]

因为数组与单个对象不同。您也不能用int const[12]初始化int

为什么当我打印时pc输出是hello world而不是内存地址?

std::cout是一个类型std::ostream的对象,它是std::basic_ostream模板类的实例化,该类具有用于void const*的成员operator<<函数:

http://en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt(过载 7)

该函数允许将每个指针打印为地址,因为每个指针都可以转换为void const*。因此,为您的int*选择此重载。

char const*很特别std::basic_ostream因为它还为各种char*变体重载了非成员operator<<函数,其中一个函数在您的示例中优先:

http://en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt2

char const*的重载非成员operator<<期望指针指向以 null 结尾的字符数组的第一个字符,并相应地执行输出操作。

如您所见,正是库使char const*与这种特定情况下的其他指针不同,而不是核心语言。

然后,为什么当我打印*pc时输出只是第一个元素 该地址包含的数据是什么?

由于*pc产生char而不是指针,因此选择了非成员operator<<的不同重载。

如果operator<<实际上采用char&获取char的地址,以便将其视为以 null 结尾的字符数组,那么混乱就会随之而来;像std::cout << 'X';这样简单的事情会导致未定义的行为。

你可能会争辩说,在相反的方向上也存在同样的问题:

#include <iostream>
int main()
{
char c = 'X';
char cc = 'Y'; // for more astonishing effect in practice, especially
// when compiled without optimisation
std::cout << &c; // undefined behaviour
}

这是真的;这样的错误可能会发生。但我想说的是,能够编写std::cout << "Hello world";的有用性超过了成本。

首先。指针在您不希望它们出现的时候可能会非常令人困惑。

现在,对于第一个问题。

const char c = "hello world".在这一行中,cchar类型的变量,您为其分配的是一个string(hello world)。所以,它会失败。

第二

*pi会给你pi指向的变量的值,但在char *的情况下,*pc给出字符串中第一个字符的值(这意味着在"hello world"中*pc指向字符串中的第一个字符)。

如果你这样做pc[1]它会给你字符串中的第二个字符,但pc表示整个字符串。