3

在 K&R 书的第 104 页中,我遇到了这样的声明:

char amessage[] = "now is the time"; //an array
char *pmessage = "now is the time";  //a pointer

数组中的单个字符可能会更改,但amessage 始终引用相同的存储。随后pmessage可能会修改指针以指向其他位置,但如果您尝试修改字符串内容,则结果未定义......

那么,这会是他们在两种情况下都意味着的错误吗?

对于数组,

amessage[] = "allocate to another address"; //wrong?

对于指针,

pmessage[0] = 'n'; //wrong?

我只想知道什么时候违反了这些规则。

谢谢。

4

5 回答 5

6
/* OK, modifying an array initialized by the 
 * elements of a string literal */
amessage[0] = 'n';

/* not OK, modifying a string literal.
 * String literals are non-modifiable */
pmessage[0] = 'n';

请注意,在 C 中不能分配数组,因此如果要复制数组,请使用memcpy函数或使用strcpy函数来复制字符串。

于 2012-07-27T15:53:52.537 回答
3
char amessage[] = "now is the time";
/*   ^            ^
     (an array)   (a string literal) */

当您使用字符串文字以这种方式初始化数组时,您将数组元素的初始值设置为文字本身的初始值。当然,该数组被分配了自己的单独内存。你可以修改它的内容。

char *pmessage  = "now is the time";
/*   ^            ^
     (a pointer)  (a string literal) */

当您使用字符串文字来初始化指针时,您正在使指针指向字符串文字。该字符串文字可以存储在只读存储器中。因此无法修改。指针本身可以修改。

什么是有效的,什么是无效的?

amessage[0] = 'n'; /* Valid. Modifying array contents.  */
amessage = pmessage; /* Invalid. You cannot assign to an array.  */
pmessage[0] = 'n'; /* Invalid. You're trying to modify a string literal.  */

但:

pmessage = amessage; /* Valid. You're modifying a pointer. */

随后:

pmessage[0] = 'n'; /* Valid. You just modified pmessage above,
                   and it now points to modifiable memory.  */

最后: 有一个关于这个的 C-FAQ 条目。值得一读。

灵感来自ouah 的回答。我不想对它进行大的编辑。

于 2012-07-27T16:18:54.300 回答
3

将指针用作数组并没有本质上的错误,除非这些指针指向常量数据(并且字符串文字是常量数据)。尽管在语义上不正确,但在过去没有内存保护的情况下,pmessage[0] = 'n';实际上会产生不可预测的结果(例如,影响程序中相同文字的所有出现)。在现代操作系统上,由于内存保护到位,这不可能发生。字符串文字和其他常量被放在可执行文件的所谓只读部分中,当可执行文件被加载到内存中以创建进程时,包含只读部分的内存页面被设为只读,即任何更改其内容的尝试都会导致分段错误。

char amessage[] = "now is the time";

实际上是以下内容的语法糖:

char amessage[] = { 'n','o','w',' ','i','s',' ','t',
                    'h','e',' ','t','i','m','e','\0' };

即,它创建一个包含 16 个字符的数组并使用字符串“now is the time”(连同 NULL 终止符)初始化其内容。

另一方面

char *pmessage = "now is the time";

将相同的字符串数据放在只读数据的某处,并将其地址分配给指针pmessage。它的工作原理类似于:

// This one is in the global scope so the array is not on the stack
const char _some_unique_name[] = "now is the time";

char *pmessage = _some_unique_name;

_some_unique_name选择它是为了不与程序中的任何其他标识符发生冲突。通常使用 C 语言不允许但对汇编器和链接器来说可以的符号(例如,像 in 之类的点string.1634)。

您可以更改指针的值 - 这将使其指向其他内容,例如指向另一个字符串。但是您不能更改数组名称后面的地址,即amessage始终引用最初为其分配的相同数组存储。

You can refer to individual elements of each string using amessage[i] or pmessage[i] but you can only assign to the elements of amessage as they are located in the read-write memory.

于 2012-07-27T16:25:28.410 回答
1

如果你这样做:

char amessage[] = "now is the time"; //an array
char *pmessage = "now is the time";  //a pointer

你可能真的想这样做:

const char *pmessage = "now is the time";  //a pointer

当你的程序被编译时,内存中的某个地方会有“现在是时候”的字节(注意有一个 NULL 终止符)。这将在不断的记忆中。你不应该尝试改变它,如果你做了奇怪的事情可能会导致(究竟会发生什么将取决于你的环境以及它是否存储在只读或读写内存中)。因此,当 K&R 试图启发您如何做事时,实用的方法是使指针指向常量字符串 const,然后如果您尝试更改内容,编译器会抱怨。

于 2012-07-27T16:11:07.347 回答
1
char amessage[] = "now is the time"; //an array

数组名是一个常数,即数组的地址不能改变,但数组的内容可以改变。

所以

amessage[0]='n';//valid and it change the first element

amessage="hello";//if you try then it wrong as array address can not be changed

现在看指针部分:-

char *pmessage = "now is the time";  //a pointer

因为它是一个指针,所以它可以指向任何地址。但是内存中的地址位置可能是可修改的,也可能是不可修改的。也就是说,它可能是只读的,也可能是允许读写的。

因此,当您尝试使用指针 pmessage 更改内存中的某些数据时,取决于指向内存结果取决于。这里它指向代码部分,所以只读。所以不允许修改。

所以

pmessage[0]='n';//definitely give you error
于 2012-07-27T16:22:12.883 回答