何时将内存分配给char*

sdn*_*rld 21 c string malloc pointers const

我有点困惑何时将内存分配给char*以及何时将其指向const字符串.

是的,我明白如果我想修改字符串,我需要分配内存.

但是在我不希望修改我指向的字符串并且只需要传递值的情况下,我应该执行以下操作?与分配内存相比,以下步骤有哪些缺点malloc

char *str = NULL;

str = "This is a test";

str = "Now I am pointing here";
Run Code Online (Sandbox Code Playgroud)

Tho*_*ard 19

让我们再次尝试使用-Wwrite-strings编译器警告标志的示例,您将看到一个警告:

warning: initialization discards 'const' qualifier from pointer target type
Run Code Online (Sandbox Code Playgroud)

这是因为"这是一个测试"的类型const char *不是char *.因此,当您将文字地址分配给指针时,您将丢失constness信息.

由于历史原因,编译器将允许您存储字符串文字,这些字符串文字是非常量变量中的常量.

然而,这是一种不良行为,我建议你一直使用-Wwrite-strings.

如果您想自己证明,请尝试修改字符串:

char *str = "foo";
str[0] = 'a';
Run Code Online (Sandbox Code Playgroud)

此程序行为未定义,但您可能会在许多系统上看到分段错误.使用Valgrind运行此示例,您将看到以下内容:

Process terminating with default action of signal 11 (SIGSEGV)
  Bad permissions for mapped region at address 0x4005E4
Run Code Online (Sandbox Code Playgroud)

问题是编译器生成的二进制文件会将字符串文字存储在只读的内存位置.通过尝试写入它会导致分段错误.

重要的是要理解你在这里处理两个不同的系统:

  1. C类型系统可以帮助您编写正确的代码,并且可以轻松"静音"(通过强制转换等)

  2. 内核内存页面权限,用于保护您的系统,并始终受到尊重.

同样,由于历史原因,这是1.和2.不同意的一点.或者更清楚一点,1.比2.更宽松(导致你的程序被内核杀死).

所以不要被编译器欺骗,你声明的字符串文字真的是不变的,你不能做任何事情!

考虑你的指针str读写是可以的.但是,要编写正确的代码,它应该是a const char *而不是a char *.通过以下更改,您的示例是C的有效部分:

const char *str = "some string";
str = "some other string";
Run Code Online (Sandbox Code Playgroud)

(const char *指向const字符串的指针)

在这种情况下,编译器不会发出任何警告.执行代码后,您编写的内容以及内存中的内容将匹配.

注意:指向const字符串的const指针是const char *const:

const char *const str = "foo";
Run Code Online (Sandbox Code Playgroud)

经验法则是:始终尽可能保持不变.

如果你需要修改字符串,使用动态分配(malloc()或更好,一些更高级别的字符串操作函数,如来自libc的strdup等),如果你不需要,使用字符串文字.

  • 否则很好的答案,但我认为证明它的部分是误导.写入文字是未定义的行为,并且您不一定会在所有系统上获得段错误.您可以**永远不会信任C中的测试,因为UB不能保证始终不正确地工作.您需要知道代码是正确的.这就是为什么在使用C时启用所有编译器警告非常重要的原因. (2认同)

Bri*_*n L 5

如果您知道str将永远是只读的,为什么不这样声明呢?

char const * str = NULL;
/* OR */
const char * str = NULL;
Run Code Online (Sandbox Code Playgroud)

好吧,实际上有一个原因可能是这很困难 - 当你将字符串传递给一个不会声明自己的只读函数时.假设您正在使用声明此函数的外部库:

int countLettersInString(char c, char * str);
/* returns the number of times `c` occurs in `str`, or -1 if `str` is NULL. */
Run Code Online (Sandbox Code Playgroud)

这个函数有详细记录,你知道它不会尝试更改字符串str- 但如果你用一个常量字符串调用它,你的编译器可能会给你一个警告!你知道没有什么危险,但你的编译器没有.

为什么?因为就编译器而言,这个函数可能尝试修改字符串的内容,这会导致程序崩溃.也许你非常依赖这个库,并且有很多函数都像这样.然后也许更容易不首先声明字符串const- 但是这一切都取决于你,以确保你不要尝试修改它.

在另一方面,如果你是一个countLettersInString功能,则只需确保编译器知道你会不会用它声明修改字符串const:

int countLettersInString(char c, char const * str);
Run Code Online (Sandbox Code Playgroud)

这样它就可以接受常量和非常量字符串而不会出现问题.


Ton*_*mas 3

只要您不打算修改该字符串的内容,您的代码就没有问题。此外,此类字符串文字的内存将在程序的整个生命周期中保留。分配的内存malloc可读写的,因此您可以操作该内存的内容。