【问题标题】:Pointers and Strings C++指针和字符串 C++
【发布时间】:2013-12-27 04:38:26
【问题描述】:

我正在自学 C++,但我对指针有点困惑(特别是在以下源代码中)。但首先,我继续向您展示我所知道的(然后将代码与此进行对比,因为我觉得好像存在一些矛盾)。

我知道的:

int Age = 30;
int* pointer = &Age;

cout << "The location of variable Age is: " << pointer << endl;
cout << "The value stored in this location is: " << *pointer << endl;

指针保存内存地址。使用间接(取消引用)运算符(*),您可以访问存储在指针的内存位置中的内容。关于本书中的代码,我无法理解......

cout << "Enter your name: ";
string name;
getline(cin, name); //gets full line up to NULL terminating character

int CharsToAllocate = name.length() + 1; //calculates length of string input
                          //adds one onto it to adjust for NULL character
char* CopyOfName = new char[CharsToAllocate];
// pointer to char's called CopyOfName, is given the memory address of the 
//beginning of a block
//of memory enough to fit CharsToAllocate. Why we added 1? Because char's need a 
//NULL terminating character (\0)

strcpy(CopyOfName, name.c_str()); //copies the string name, into a pointer?

cout << "Dynamically allocated buffer contains: " << CopyOfName << endl;
delete[] CopyOfName; //always delete a pointer assigned by new to prevent memory leaks

输出:

Enter your name: Adam
Dynamically allocated buffer contains: Adam

上面代码中的cmets就是我的cmets。我的问题从strcpy 开始。为什么name.c_str() 被复制到指针CopyOfName 中?这是否意味着所有字符串都是必不可少的指针?所以喜欢 字符串测试=“你好世界”; 实际上是指向存储“H”的内存位置的指针吗?

接下来,为什么在打印输出语句中使用CopyOfName 而不是*CopyOfName?指针保存内存地址?使用*CopyOfName 将打印出内存位置的内容。我在 Code::Blocks 中试过这个,如果输入文本是“Hello World”。在打印输出语句中使用*CopyOfName 只会给出“H”。这是有道理的,因为当我声明我需要一个带有“新”事物的内存块时,这实际上返回了一个指向动态分配的内存块第一部分的指针。

我可以协调这一点的唯一方法是,如果一个字符串实际上是一个指针。

string testing = "Confused";
cout << testing << endl;

会打印出“困惑”这个词

但是,如果我尝试编译

string testing = "Confused";
cout << *testing; 

我收到一条错误消息。

基本上,总结一下我的问题,我试图用strcpycout 语句来理解代码。

【问题讨论】:

  • 附带说明,没有“NULL 终止字符”之类的东西。然而,有一个“NUL”('\0')字符可能就是你的意思。

标签: c++ pointers


【解决方案1】:

您似乎了解 C 风格的字符串是什么,但总而言之,它们只是内存中的字符数组,按照惯例以 nul 字符 \0 终止。通常它们通过指向字符串中第一个字母的char* 来引用。打印时,通常从第一个字符开始打印字符串的字符,并且在到达 \0 终止符时停止打印(或复制等)。

std::string 是一个(通常)包装 C 风格字符串的类。这意味着std::string 对象(通常)具有用于实现其功能的私有 C 样式字符串。函数 std::string::c_str() 返回一个指向这个底层 C 风格字符串的指针。

假设char *str; 指向一个C 风格的字符串。如果您尝试运行cout &lt;&lt; *str &lt;&lt; endl;,您会注意到只打印了第一个字符。那是因为 C++ 的函数重载。 *str 的数据类型是char,因此调用char 版本的cout 并忠实地打印单个字符*str。为了与 C 样式字符串兼容,将 char* 作为参数的 cout 版本将指针视为 C 样式字符串以进行打印。例如,如果您coutint*,则不会打印底层的int

编辑:另一条评论:

您尝试取消引用 std::string 对象失败的原因是,事实上,它不是一个指针。您可以取消引用std::string::c_str() 的返回值,并且您将取回字符串的第一个char

相关:How is std::string implemented?.

【讨论】:

  • 我认为,OP 在这里理解的重要一点是“指向字符串中的第一个字符”部分。
【解决方案2】:

在 C 中,字符串只是字符数组。当用作函数的参数时,数组会衰减为指针。

在 C++ 中,std::string 是一个类。它在其中包含一个 C 风格的字符数组,这就是 c_str() 返回的内容。但是字符串本身不是指针,所以不能取消引用;您必须使用c_str() 方法来获取指向字符串内容的指针。

【讨论】:

    【解决方案3】:

    指针与数组不同。字符串文字是不可变的,当您拥有指向字符串文字的指针时,您可以检查其内容,但修改它们是未定义的行为。使用此语法时:

    char arr[] = "hi there";
    

    字符串文字被复制到数组中。因为您没有指定大小,所以编译器会自动推导出它。 NUL 终止符也会自动添加。如果指定大小,则必须确保缓冲区可以容纳 NUL 终止符。因此:

    char arr[5] = "hello";
    

    是一个错误。如果使用大括号初始化语法:

    char arr[5] = { "h", "e", "l", "l", "o" };
    

    这是一个错误,因为没有NUL 终结符。如果您使用strcpy,则会为您添加一个NUL 终止符。

    std::string 提供了两种方法来返回指向其底层内容的指针:datac_str。在 C++11 之前,唯一的区别是 data 不包括 NUL 终止符。在 C++11 中,它现在可以,所以它们的行为是相同的。因为指针很容易失效,所以操作这些指针是不安全的。执行char * ptr = str.c_str(); 也不安全,因为c_str 返回的数组的生命周期在分号处终止。您需要将其复制到缓冲区中。

    【讨论】:

      【解决方案4】:

      按顺序回答您的问题:

      "为什么name.c_str()会被复制到指针CopyOfName中?这是否意味着 所有字符串都是必不可少的指针?所以像字符串测试= “你好世界”;实际上是一个指向内存位置的指针 “H”存储在哪里?”

      正如余浩在他/她的评论中指出的那样,了解 C++ 风格字符串和 C 风格字符串之间的区别很重要。在第一种情况下,您处理的是“不透明”对象,而在后一种情况下,您基本上处理的是字符“数组”。

      使用 C++ 字符串对象,您可以使用c_str() 方法获取(指向)C 样式字符数组的(指针)。在 C 中,使用指向数组开头的指针表示数组,然后通过提供从该起始地址开始的偏移量(数组的索引)来实现引用。所以这个包中最后一个问题的答案是“是”,指向 C 风格字符串的指针就是指向第一个字符 'H' 的指针。

      "接下来,为什么打印出来的语句中没有CopyOfName *名称复制?指针保存内存地址?”

      因为运算符 &lt;&lt; 被重载以处理 C 字符串。此方法的实现“知道如何处理”指针。

      【讨论】:

        【解决方案5】:

        那么像string testing = "Hello world";其实是一个指针,指向存放“H”的内存位置?

        不,上面有一个名为 string 的对象。 char* testing = "Hello World" 是这样。如您所见,它甚至被声明为指针,它指向字符串中的第一个字符 - H.

        接下来,为什么在打印出来的语句中CopyOfName不是*CopyOfName?指针保存内存地址?使用*CopyOfName 将打印出内存位置的内容。我在代码块中尝试了这个,如果输入文本是“Hello World”。在打印输出语句中使用*CopyOfName 只会给出“H”

        cout 将指针指向字符串的第一个字符,因此CopyOfName 是正确的。在这种情况下,它将打印从 H 开始的每个字符,直到找到 \0(空字符)。像 "hello" 这样的字符串实际上有 6 个字符 - 'h' 'e' 'l' 'l' 'o' '\0' 当你写 *CopyOfName 时,你正在取消引用这个指针,*CopyOfName 实际上只有一个字符

        【讨论】:

        • 快速格式化提示:您可以用&gt; 开始一行以使其成为块引用,并格式化一段内联代码,用反引号将其包围。有兴趣的可以看the full reference to formatting here
        【解决方案6】:

        作为学习者,您提出了正确的问题。

        答案:

        1. 在 C++ 中,string 是一个对象,c_str() 本质上返回一个指向第一个 字符串的字符(C风格)
        2. 你对 C 中的字符串是正确的,变量实际上指向第一个字符 字符串
        3. C++ 根据变量的类型做了很多事情。当您传递 string 对象时 cout 打印字符串。此外,C++ 足够聪明,可以确定 *testing 是非法的

        【讨论】:

        • @Sudhee 我认为是 c_str() 而不是 c_ptr()。
        【解决方案7】:

        为什么 name.c_str() 被复制到指针 CopyOfName 中?

        "name" 是一个 STL 字符串。这是一个对象,与 c 字符串不同。 c-string 是一个内存集合,它保存字符,并且有一个空终止符。因此,如果您正在使用 STL 字符串并希望将它们转换为 c 字符串,则可以在其上使用 .c_str() 来获取 c 字符串。

        CopyOfName 包含足够的内存来保存 name,因为它被分配来保存它。

        cout 有很多不同的东西可以和

        当您介绍“测试”时,我有点困惑,但我认为您对 c 字符串(即 char *)和 STL 字符串(即对象)感到困惑。不要难过,也不要放弃。这东西很棘手,需要一段时间才能得到。

        我建议尝试理解术语“c-string”、“char *”、“stl string”和“指向 stl string”之间的区别。

        【讨论】:

          【解决方案8】:

          在不存在 C++ 标准字符串的 C 语言中,char* 就是所谓的“字符串”。如您所述,它是以 NULL 字符结尾的字符数组。几乎所有采用 C 风格字符串的标准库函数都会采用指向所述字符串的指针,原因有两个:

          1. 与其他数组不同,将 C-Style 字符串视为一个整体而不是一堆字符更容易,因此采用指针保持这种想法
          2. 将数组作为函数参数来获取指向第一个元素的指针是最简单的方法,尤其是在 C 字符串的情况下,它们只能被读取到 NULL 字符。

          【讨论】:

            【解决方案9】:

            我认为你在做什么,如果我错了,其他人请纠正我,是你正在将你的字符串复制到一个动态 char 数组中。所以不,你没有将它复制到指针中。那里涉及指针的原因是因为如果我是对的,动态数组需要指针来正确分配它们的内存。

            【讨论】:

            • 数组与动态集合相反,除非您指的是非常量指针与 const 指针。在 strcpy 的情况下,要复制到的数组必须已经分配了与原始字符串大小一样多或更多的空间。
            猜你喜欢
            • 2012-03-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-04-12
            相关资源
            最近更新 更多