char * string 的直接分配与动态分配

Posted

技术标签:

【中文标题】char * string 的直接分配与动态分配【英文标题】:Direct assignment vs Dynamic allocation for char * string 【发布时间】:2016-05-25 14:48:17 【问题描述】:

我使用 C++ 已经有一段时间了,但有一个基本概念我无法理解。首先,我将列出将文本字符串分配给 char * 的两种方法。

方法一:

char * str = "Hello World";

方法二:

char * str = new char [12];
strcpy(str,"Hello World");

方法2我很熟悉。方法1是让我头疼的方法。我的问题是

    这两种方法的根本区别是什么?有什么优点/缺点? 是否应该为方法 1 手动清理内存? 方法 1 中字符串的寿命是多少?只要指针仍然有效,我可以相信它会持续存在吗?我可以更改内容吗(前提是我没有在结尾处超出“\0”)?

我已经阅读了无数的 C++ 教科书和文章。他们都告诉我方法1有效,没有详细说明影响。我自己的实验并没有产生令人信服的结果。

谢谢(也许请原谅我的英语不好)

编辑: 实际上我在 VS2015 中使用带有 tchar 字符串的 WinAPI 进行编程,方法 1 可以完美编译。 std::string 在处理 Unicode 时很糟糕。

假设您在一个解决方案中有两个项目,一个使用 Unicode,另一个使用多字节,这两个项目使用同一个库。在这个库中,使用 tchar 很好。 std 字符串你必须明确告诉它是哪个版本。

我必须这样做,因为多字节项目是我需要注入另一个应用程序的 dll。 DLL 的 unicode 版本会导致应用崩溃,只有多字节版本有效。

【问题讨论】:

如果您尝试使用 C 字符串,您将落后一分。 您忘记了第三个选项:char str[] = "Hello World" 请注意,方法 1 在 C++11 中是不允许的,它必须是 const char* 绝对没有理由不使用std::string。此外,尽管 char *string = "string literal"; 在 c 中是合法的,但正如许多人评论的那样,这仍然是一个坏主意,因为字符串文字是只读的,写入只读内存将调用未定义的行为。 【参考方案1】:
char * str = "Hello World";

在 C++ 中已弃用,因为它违反了 const 正确性。 "Hello World"const char[] 并用 char* 指向它是对 undefined behavior 的邀请,因为您可以尝试修改它。如果你想在 C++ 中使用字符串,我建议你使用std::string,它可以防止你陷入 c-strings 的众多陷阱。

如果你确实需要一个 c 字符串,那么你可以使用

char str[] = "Hello World";

这将创建一个正确长度的 char 数组并允许您修改内容。

编辑:实际上我在 VS2015 中使用带有 tchar 字符串的 WinAPI 进行编程,方法 1 可以完美编译。 std::string 在处理 Unicode 时很糟糕。

没有什么可以阻止方法一在大多数编译器上进行编译,但如果你想符合标准,那么你需要停止使用它。它已被弃用,最终(希望)对它的编译器支持将被删除。

如果您需要 Unicode 支持,请使用 std::wstring 包装 wchar_t*

【讨论】:

其实我在VS2015中使用WinAPI和tchar字符串进行编程,方法1编译完美。 std::string 对 Unicode 处理很糟糕(假设您在一个解决方案中有两个项目,一个使用 Unicode,另一个使用多字节,这两个项目使用同一个库。在这个库中,最好使用 tchar.std字符串,您必须明确告知它是哪个版本)。无论如何,我也非常熟悉 std 字符串。感谢您的信息。 @Leon "Unicode 字符串的 std::string 很糟糕" 然后使用 std::wstring @Leon 那你需要告诉我们。如果你不告诉我们,我们就无法知道你在做什么。 我非常抱歉投反对票。现在我再次阅读它,我明白了,你是 100% 正确的。请让我们删除无用的cmets?【参考方案2】:

在此声明中

char * str = "Hello World";

对 C 有效,对 C++ 无效,创建了两个对象。

首先,编译器创建一个以零结尾的字符数组,该数组具有字符串文字"Hello World" 的静态存储持续时间。

Ib C 字符串文字具有非常量字符数组类型,而在 C++ 中字符串文字具有常量字符数组类型。

尽管如此,无论在 C 还是 C++ 中,您都可以修改字符串文字。任何修改字符串文字的尝试都会导致未定义的行为。

这也意味着您可能无法清除字符串文字占用的内存。为字符串文字保留内存的是编译器。

在 C 中,声明中使用的字符串文字的类型为 char[12],而在 C++ 中,它的类型为 const char[12]

因此在 C++ 中,声明看起来像

const char * str = "Hello World";

在声明中创建的第二个对象是名为str 的指针,它指向字符串文字的第一个字符。指针本身可以更改,即可以重新分配。

如果指针在代码块中声明,则它具有自动存储持续时间。指针的存储时长不影响具有上述静态存储时长的字符串字面量的存储时长。

【讨论】:

真的很感激这个知识,因为字符串文字内容是恒定的并且它被存储为静态的。也许是由于我的英语不好,其他人没有得到我的问题,但这是我需要的答案。谢谢。【参考方案3】:

我的回答涉及 c++。一些细节与c不同。

    这两种方法的根本区别是什么?有什么优点/缺点?

让我们看看你的第一个代码:

char * str = "Hello World";

这是不正确的。您不能将字符串文字分配给非常量指针。至少从 c++11 开始没有。在此之前,转换只是被弃用。

这个:

const char* str = "Hello World";

应该是正确的。但是,如果您需要修改字符串,那么这不是一个选项。

编辑:实际上我在VS2015中使用WinAPI和tchar字符串进行编程,方法1编译完美。

即使您的编译器支持转换,这样做也很危险,因为您可能会意外地修改字符串文字,这很糟糕,因为

让我们看看你的第二个代码:

罢工>

char * str = new char [12];
strcpy(str,"Hellow World");

这会调用未定义的行为。字符串字面量为 13 个字符长(因为空终端字符),strcpy 溢出分配的数组。

编辑:有问题的代码现在已修复,但这很好地说明了为什么手动指定大小容易出错。

我推荐一种更简单的方法:

char str[] = "Hello World";

这样更简洁,不会使用错误大小的数组。它也比动态分配更有效,但不如直接使用字符串文字有效。但是,与字符串文字不同,您可以修改此数组。

如果数组是本地的,那么它会在作用域结束时被销毁。此外,您不能调整数组的大小。如果您需要一个可调整大小的字符串,那么您确实需要动态分配。如果你需要动态分配,我推荐std::string

std::string str("Hello World");
    是否应该为方法 1 手动清理内存?

不,你不应该。字符串字面量具有静态存储。

    方法 1 中字符串的寿命是多少?只要指针仍然有效,我可以相信它会持续存在吗?

您可以相信字符串文字会在程序的整个执行过程中退出。

我可以更改内容吗(前提是我没有跑到末尾的“\0”)?

修改字符串文字会产生未定义的行为。您不希望在程序附近出现未定义的行为。

std::string 在处理 Unicode 时很糟糕。

std::string 具有与纯字符数组完全相同的 unicode 处理。

假设您在一个解决方案中有两个项目,一个使用 Unicode,另一个使用多字节,这两个项目使用同一个库。在这个库中,使用 tchar 很好。 std 字符串你必须明确告诉它是哪个版本。

我会完全避免使用tchar,除非在处理 Windows API 时。但是如果你确实使用它并且需要std::string 的细节,那么你可以简单地使用std::basic_string<tchar>

【讨论】:

数组可以用字符串初始化,所以char str[] = "Hello World"; 既有效又正确。结果数组可以是修饰符,但不能调整大小,就像您不能调整使用 new 运算符分配的内存一样。对于char * 指针,我会在c++ 上使用malloc(),因为它没有构造函数,因此如果需要,您将拥有调用realloc() 的优势。 @iharob 我同意,但如果我需要动态分配的字符串,我会使用std::string 次要观点:IMO 对实验者来说是一个重要的区别。 “您不能更改字符串文字的内容。”不是由语言指定的 - 它可能工作 - 它可能不会,代码可能会崩溃。正如您所暗示的,尝试这样做是 UB。 @user2079303 是的,这是 100% 正确的。这会更高效,更多c++ish。此外,它将避免处理内存分配和释放的地狱。 @chux 是的,我想。从技术上讲,我并没有说“标准说您可能不会……”,但这是暗示的。我想我是在投射我对自己永远不做 UB 的限制。现在答案应该是正确的。【参考方案4】:

如果是第一种方法

 char * str = "Hello World";

您将string literal 的地址存储到给定的指针中。但是由于const char[]char *类型不匹配,这个构造是非法的。

记住,内存地址的内容不应该被修改,尝试这样做会调用undefined behavior。此外,您不需要释放任何东西,因为您没有分配任何动态内存。

在第二种方法中,

char * str = new char [12];
strcpy(str,"Hellow World");

您正在为指针分配动态内存并用 字符串文字 的内容填充它。这个数组是完全可写的。但是,请注意,对于12 的维度,您没有空终止符的空间。您可能希望大小至少为13,以便为空终止符留出空间。最后,您需要在使用后释放分配的内存。

【讨论】:

"内存地址的内容不能修改。"最好是“尝试修改内存是UB”。 C没有指定内存不能修改,只是表示尝试修改的是UB。 @chux 修改了措辞,现在应该可以了。谢谢。【参考方案5】:

方法一中字符串的寿命是多少?

对于字符串文字本身,程序的生命周期;字面量的存储在程序启动时分配(甚至可能在程序加载到内存后立即分配),并在程序退出时释放。

只要指针仍然有效,我可以相信它会持续存在吗?

无论指针变量str 的生命周期如何,您都可以相信文字会持续存在。

我可以更改内容吗(前提是我没有跑到末尾的“\0”)?

没有。 C++ 字符串字面量是const char 的数组,这意味着它们不能被修改(这将破坏它们作为字面量 的全部目的;这在逻辑上与更改42 的内容相同)。

这两种方法的根本区别是什么?

第一种方法没有预留任何新的内存,str指向的内容可能不会被修改。

第二种方法动态分配一块新的内存,并将字符串字面量的内容复制到其中;您可以将分配块的内容修改为您喜欢的内容。

有什么优点/缺点?

使用第一种方法为字符串文字创建符号常量(您想要这样做 - 我不止一次被拼写错误的文字所困扰)。

第二种方法没有很多好的用例;如果您需要操作文本数据,请使用std::string 类型而不是char 的数组。 C 风格的字符串处理非常痛苦,而std::string 类型在这方面让生活更加更轻松。如果您需要创建和存储多个字符串,请使用std::vector 之类的标准容器。

【讨论】:

以上是关于char * string 的直接分配与动态分配的主要内容,如果未能解决你的问题,请参考以下文章

String 类型与char 类型 输入

继承与动态内存分配

如何将函数中使用的数组传递给动态分配的main数组?

char数组的动态内存分配

如何在C中为char**动态分配内存

从 const char 数组初始化为动态分配的 const char 数组