理解char*, char[]和strcpy()

8
我理解如下:
  • char * 指向一个字符串常量,修改其指向的数据是未定义的。但是,您可以更改它指向的位置。

  • char[] 是指一块可以更改的内存块。您可以更改其内容,但不能更改它所指向的位置。

  • strcpy(dest, src)src 复制到 dest 中。

我的问题是,如果 dest 是已经指向某个位置的 char *,使用 strcpy() 是否不正确(因为我认为旧内容将被 strcpy() 覆盖,这是未定义的行为)?

例如:

char *dest = malloc(5);
dest = "FIVE";

char *src = malloc(5);
src = "NEW!";

strcpy(dest, src); /* Invalid because chars at dest are getting overwritten? */

5
char*指向一个字符串常量 - 不对。一个适当设置的char *指向一个**char*。它是否是以适当的空字符结尾的char序列,取决于所访问数据的特性,并且它并不是常量。在您提供的列表中,数组(如char[])的一般描述更接近现实。指针持有一个地址;数组是*一个地址。 - WhozCraig
2
首先,char * 是指向 char 的指针。在 C 语言中,字符串是以零字符结尾的 char 序列,因此通常 char * 指向这样一个字符串的开头。但它也可以指向一个旨在接受这样一个字符串的缓冲区的开头。如果该字符串不是字面量,则可以对其进行修改。 - Rudy Velthuis
2
@WhozCraig "数组是地址" - 呃,数组具有地址。 - M.M
3
数组不是一个地址,而是由若干非零元素组成的对象。 - M.M
1
@user93353 我知道我的理解能力很差,我是一个非常初学者。作为我的第一门语言学习C语言很困难。 - CS Student
显示剩余19条评论
4个回答

17

很抱歉,您的理解并不完全正确。

char * 指向字符数据,并且因为没有const,所以可以对指向的数据进行写入。

但是,以下做法也是完全可行的:

char *a = "hello";

因为字符串文字存储在只读内存中,但不被语言的语法视为常量,所以它会给你一个读/写指针来读取只读数据。

最好将上述内容改写为:

提供一个读/写指针以读取只读数据,因为字符串字面常量存储在只读内存中,但不被语言的语法视为常量。

const char *a = "hello";
为了使“你不能修改指向a的数据”这一点更清晰,请注意一下,您混合使用malloc()和赋值的示例是错误的。以下是正确的做法:
char *dest = malloc(5);
dest = "FIVE"; /* BAD CODE */

这是糟糕的代码,请勿模仿。它只是简单地用字符串字面量"FIVE"覆写了由dest返回的指针,该字符串存在于(再次强调,只读)内存中。

使用strcpy()来将新分配的内存初始化为字符串数据是正确的方法:

char *dest = malloc(5);
if(dest != NULL)
  strcpy(dest, "five");

请注意,检查malloc()的返回值是一个好习惯。

重复向同一内存写入并没有问题,这是C语言中非常基本的概念;变量代表内存,并且可以通过“覆盖写入”在不同时间赋予不同的值。

就像这样简单的例子:

int a = 2;

printf("a=%d\n", a);
a = 4;
printf("a=%d\n", a);

You can extend the above malloc()-based example:


展示了这一点,当然对于字符串来说也同样适用,因为它们只是内存块。
您可以扩展上面的基于malloc()的示例:
char *dest = malloc(5);
if(dest != NULL)
{
  strcpy(dest, "five");
  printf("dest='%s'\n", dest);
  strcpy(dest, "four");
  printf("dest='%s'\n", dest);
  strcpy(dest, "one");
  printf("dest='%s'\n", dest);
}

并且它会打印:

dest='five'
dest='four'
dest='one'

1
char *a = "hello"; 这是一个反向兼容性可能会被不赞成的情况。 - StoryTeller - Unslander Monica
2
你的理解不完全正确,很遗憾。我也是这么想的。我来这里学习,绝不指望自己总是正确的 :) - CS Student
1
所以使用char *a = "hello"将"hello"存储在只读内存中,但指向它的指针允许您修改内容?如果您确实修改了内容,那是非法的,对吗? - CS Student
@CSStudent 对的!有点奇怪。 - unwind
我得出了相同的结论,但是经过更多的试错和测试代码片段。如果早些时候我能找到这个答案就好了 :) - RequireKeys

4

我的理解如下:

  • char * 指向一个字符串常量,修改它所指向的数据是不被定义的。但是你可以改变它指向的位置。

这里指的是像下面这样的表达式:

char * string = "mystring";

您说得对,执行 string[1]='r'; 是未定义的。但这不是因为 char *,而是因为涉及到字符串文字放入只读内存。

将其与以下内容进行比较:

char string[] = "mystring";

我在RAM中定义了一个数组,将该字符串放入其中。在这里,我们可以使用string[1] = 'r';,因为我们在普通数据内存中。

这似乎支持您的假设,但请看下面的内容:

char string[] = "mystring";
char * string2 = string;

这里string2[1] = 'r';是有效的,因为它指向一个允许写入的位置。

char[] refers to a block of memory that you can change its contents but not what it refers to.
是的,因为这里的名称只是一个变量的名称而不是指针。
strcpy(dest, src) copies src into dest.

好的。

我的问题是,使用strcpy()时,如果dest已经指向某些字符(因为我相信旧内容将被strcpy()覆盖-这是未定义的行为),那么使用strcpy()是否不正确?

这取决于您所说的“已经指向某些字符”是什么意思...

For example:

char *dest = malloc(5);
dest = "FIVE";

char *src = malloc(5);
src = "NEW!";

strcpy(dest, src); /* Invalid because chars at dest are getting

overwritten? */

在这里,您混淆了几件事情。

首先,您让dest指向一个全新的内存块。然后,您让它指向另一个您无法写入的地方,这个内存块就丢失了(内存泄漏)。

src也是同样的情况。

因此,strcpy()失败了。

您可以这样做:

char *dest = malloc(5);

char *src = "NEW!";

strcpy(dest, src);

在这里,dest指向一个可写的位置,而src指向有用的数据。


感谢您提供详细的答案。我所说的“已经指向某个东西”是指,如果我删除了mallocs,只有char *dest =“FIVE”char *src =“NEW!”,然后使用strcpy(),这样是否合法,或者strcpy()是否会覆盖dest指向的字符串? - CS Student
1
“或者说strcpy()是在覆盖dest指向的字符串?” 是的,这就是它的作用。它获取一个地址,尝试写入该地址,但当它无法写入时,会导致未定义行为(UB)。 - glglgl
那么,如果我有一个指向从malloc分配的新内存块的 dest ,我可以使用 strcpy() 而不会发生未定义行为吗? - CS Student
@CSStudent 是的,它就是为此而创建的。但你也可以这样做:char dest[10]; strcpy(dest, "DATA");. - glglgl

2

简要分析:

char *dest = malloc(5);
// 'dest' is set to point to a piece of allocated memory
// (typically located in the heap)
dest = "FIVE";
// 'dest' is set to point to a constant string
// (typically located in the code-section or in the data-section)

您正在两次给变量dest赋值,因此第一次赋值没有任何意义。

这就像写:

int i = 5;
i = 6;

此外,您会“丢失”已分配内存的地址,因此您将无法在以后释放它。

1

char* 是指向内存地址的指针,因此您可以修改该地址中包含的信息。

char* 和 char[] 的区别在于 char[] 不是动态的,您无法更改其大小。此外,char* 指向堆中的地址,而 char[] 存储在程序的堆栈中。

您可以对指针和数组使用 strcpy,因为两者的数据都可以被覆盖。


"char[]不是动态的",这并不完全正确。您可以使用VLA,或者数组是动态分配的结构的一部分。最好您能更准确地表达您的断言,而不仅仅是关于"char[]"的概括性陈述。或者使用代码示例来澄清。 - StoryTeller - Unslander Monica

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接