【问题标题】:C - printing a char* through strcpy is acting contradictory?C - 通过 strcpy 打印 char* 是矛盾的吗?
【发布时间】:2018-03-09 14:12:01
【问题描述】:

第一次在这里发帖,我已经使用这个网站多年,但这个困境真的很烦人。因此,当我使用 C 编译器通过 VS2015 开发人员命令提示符运行此 C 代码时:

#include <stdio.h>
#include <string.h>

int main(){
   char* ptr = NULL;
   const char* pt2 = "hello";
   strcpy(&ptr, pt2);
   printf("%s",&ptr);
   return 0;
}

我收到以下警告:

midterm_c.c(35): warning C4047: 'function': 'char *' differs in levels of indirection from 'char **'
midterm_c.c(35): warning C4024: 'strcpy': different types for formal and actual parameter 1
midterm_c.c(36): warning C4477: 'printf' : format string '%s' requires an argument of type 'char *', but variadic argument 1 has type 'char **'

然而,当我运行它时,它会打印“hello”,这不应该发生,因为在 ptr 变量上使用引用运算符没有意义。但是当我在没有它们的情况下运行这段代码时:

char* ptr = NULL;
const char* ptr2 = "hello";
strcpy(ptr, ptr2);
printf("%s",ptr);
return 0;

我没有收到任何警告,编译成功,但是当我运行它时程序崩溃了,即使它应该可以正常工作。我真的很沮丧,因为这对我来说没有任何意义,也是我现在真的讨厌 C 的原因之一。如果有人知道这是如何发生的,我将不胜感激。谢谢。

【问题讨论】:

  • 使用strdup(),它只是为ptr变量分配内存,然后将ptr2的内容复制进去。
  • '这应该可以正常工作' 为​​什么?您正在复制到一个空指针。
  • 你没有为ptr分配内存。
  • '我没有收到警告,成功编译,但是当我运行它时程序崩溃' - 是的,这是未调试程序的正常行为。如果一个程序在第一次编译时运行良好,那就买彩票吧!
  • 其实你是复制到一个空指针的地址。

标签: c pointers reference char


【解决方案1】:

在您的工作代码中,您基本上只是将堆栈的一部分视为字符串缓冲区;按照标准,这是公然违法的,但它恰好起作用,因为在踩踏之后您不会从任何踩踏的东西中读取内容。

您是正确的,将 char** 传递给 strcpy 没有意义,但最终发生的是它写入 char* 变量本身(如果它是 32 位程序,则写入一些相邻的内存),将其视为char 的数组(毕竟,一个八字节指针有足够的空间容纳一个七个字符串加上NUL 终止符)。实际上,您的代码是不正确的代码,但仍然碰巧表现得像正确的代码,因为:

char* ptr = NULL;
const char* pt2 = "hello";
strcpy(&ptr, pt2);
printf("%s",&ptr);

编译成行为几乎完全一样的代码(除了警告):

char ptr[sizeof(char*)];  // Your char* behaves like an array
const char* pt2 = "hello";
strcpy(ptr, pt2);
printf("%s", ptr);

当您不获取ptr 的地址时,您将NULL 指针传递给strcpy,它会立即崩溃(因为您无法在几乎所有系统上写入NULL 指针的目标) .

当然,正确的解决方案是让ptr 指向已分配的内存,可以是全局、自动或动态分配的内存。例如,使用堆栈缓冲区,这是合法的:

char buf[8];

char *ptr = buf; // = &buf[0]; would be equivalent
const char* pt2 = "hello";
strcpy(ptr, pt2);
printf("%s", ptr);

尽管这毫无意义,因为 ptr 可以随处替换为 buf 并删除 ptr

char buf[8];
const char* pt2 = "hello";
strcpy(buf, pt2);
printf("%s", buf);

【讨论】:

  • 我希望我能接受所有这些答案,但你已经为我的问题写了最详细的内容。这就说得通了。谢谢。
【解决方案2】:

首先,你不知道如何使用strcpy。第一个参数需要指向可复制到的空间,它不会为您分配空间。

您的第一个示例可能仅“有效”,因为它正在复制到堆栈空间中。这是未定义的行为,会导致各种问题。

您的第二个示例(更正确的示例)通常会导致段错误,因为您试图复制到(或读取)空地址。

【讨论】:

【解决方案3】:

您正在写入随机内存,这是未定义的行为。这意味着根据 C 标准,允许编译器生成可以执行任何操作的代码,包括但不限于:

  1. 打印“你好”。

  2. 打印随机乱码。

  3. 崩溃。

  4. 损坏了您应用中的一些其他变量。

  5. 在时空结构上撕开一个洞,导致来自第五维度的邪恶太空狐猴入侵,导致供应过剩,从而破坏了 Kopi Luwak 咖啡的市场。

    李>

Tl;dr:不要那样做。

【讨论】:

  • 其实&amp;ptr不是“随机”内存。 OP 正在写入指针本身的字节。这是可怕的,可能是官方未定义的行为,但这不是“随机”记忆;它是堆栈上的一个已知位置。
  • @ShadowRanger "hello" 超过 4 个字节,因此在 32 位操作系统上,这将超出指针的边界。在 64 位机器上,“hello”将适合指针,但如果它长于 7 个字符,因为 OP 简化为这个示例的原始代码可能是,它也会溢出并重写谁知道什么.无论哪种方式,这都是糟糕的juju。
【解决方案4】:

您需要为 ptr 变量 (malloc) 分配内存。然后,一个有用的函数是 strdup()。
它需要一个字符串,为新字符串分配好的内存空间,并在返回新字符串之前复制其中的源内容。
你可以这样做:

ptr = strdup(pt2);

文档:http://manpagesfr.free.fr/man/man3/strdup.3.html

【讨论】:

    猜你喜欢
    • 2021-07-13
    • 1970-01-01
    • 2017-11-06
    • 2017-06-27
    • 1970-01-01
    • 2010-12-21
    • 1970-01-01
    • 2011-08-14
    • 1970-01-01
    相关资源
    最近更新 更多