将char转换为char数组还是将char数组转换为char?

5

假设我有一个char变量,我想在一行代码中将其与char数组连接起来。以一个[不切实际的]例子为例:

strcat("ljsdflusdfg",getchar());

如果我想要进行相反的操作,无论数据类型如何,哪个函数是合适的用于连接或强制转换字符串?或者可能有一些语法我不知道...
这里有一个例子。它可以正常编译,但会崩溃。
char* input(){
 char* inp="";
 while(1){
  char c=getchar();
  if(c){
   if(c=='\n'||c==EOF){
    break;
   }else{
    strcat(inp,(char*)c);
   }
  }
 }
 return inp;
}
8个回答

3

strcat将其参数视为指向以空字符结尾的字符串的指针。将char转换为char *只是危险的,我无法想象它有任何有用的理由(并不是在暗示你愚蠢地尝试了它 - 每个人在学习时都会犯一些愚蠢的错误。这就是我们在这里的原因)。

原因是它会将单个字节的char和围绕该char的额外sizeof(char*)-sizeof(char)(通常为3)字节解释为指针,它将指向... 任何地方。你不知道它指向哪里,因为其中3个字节超出了你的控制范围,因此你无法知道它是否指向有效数据。

你可以采用以下第二种方法:

strcat(inp, &c);

这次,你做得更好了,因为&c是类型为char *的表达式,不需要进行强制转换。但同样地,strcat假设它的参数是以空字符结尾的字符串,并且由于你无法保证在char数据后面有一个空字符,所以这样做行不通。
最佳方式是这样的:
size_t len = strlen(inp); // this may already be calculated somewhere else
...
inp[len++] = c; // add c as the last character, and adjust our len
inp[len] = '\0'; // add a new nul terminator to our string

更新:

事实上,我撒谎了。最好的方法是使用标准库函数fgets,它似乎正在做你试图做的事情。说实话,我忘了它,但如果这是一个作业,你的教授可能不希望你使用fgets,以便你学习如何手动完成。然而,如果这不是作业,fgets恰好符合你的要求。(实际上,第三种方法正在重新实现fgets或类似fgets的功能。)

我还想对你的input函数添加一些其他评论:

  1. char* inp = "";将指向只读数据。C标准中的一个缺陷(为了向后兼容)允许字符串字面量被分配给char*类型,而不是const char *类型,因为它应该是(IMHO)。

    有几种方法可以处理这个问题,但最好的方法是动态分配。使用malloc函数保留一些数据,在函数中跟踪你已经使用了多少,并在需要更多空间来存储时使用realloc函数。如果你需要帮助,我相信你会回来的(希望不是太快)有另一个问题。 :)
  2. getchar()返回一个int,因为EOF被定义为超出char的正常范围。为了区分任何一个charEOF,最好将c设置为int。否则,一个完全有效的字符可能会发出EOF信号。但在将其添加到字符串中时,请务必将c转换为char

这看起来最接近我想要的,但是&c给了我一串看起来像转义字符的字符串。另一个解决方案代码太多了。 - Anonymous
@匿名 - 另一个解决方案就是唯一的解决方案。&c版本与(char *)c之所以不正确,是因为相同的原因。如果您认为另一个版本代码太多,那么欢迎来到C编程的世界。:P(实际上,有第四种方法,但我不确定这是否是作业,而且我不想冒险替你完成作业,所以我会保持谨慎。) - Chris Lutz
这不是作业。我试图为了自己的利益理解C语言,但我手头没有那么多时间,而且我觉得这个过程非常痛苦。第四种方法是什么? - Anonymous
谢谢指点-我会研究一下fgets。我不知道我的输入函数很糟糕。关于你的第一点-我不担心缓冲区溢出,并且我不知道如何声明其他语言中可能称为“固定长度字符串”的内容,除非它是我正在寻找的固定长度字符串声明函数,否则我不想学习malloc。关于第二点-我认为getchar返回一个称为字节的char数据类型。 - Anonymous
@匿名 - mallocrealloc 用于可变长度字符串。固定长度字符串只是一个 char [](不是 char *),但如果你在函数中声明一个数组(在堆栈上),你不能将其传回给调用者,所以你需要将它(以及它的长度,以免溢出)作为参数传递。这就是 fgets 的工作原理。 :P - Chris Lutz
显示剩余2条评论

2
strcat(inp,(char*)c);

这段代码试图将存储在任意内存位置的'c'后的内容连接起来,直到找到一个零。

更好的方法是创建一个空字符串,大小为您认为合理的最大值,用零填充它,然后只需将'c'插入当前最后位置即可。


1
不,它试图连接存储在指针“c”所指向的内存内容,直到找到零为止。"&c"是"c"存储的位置。但是这种方法更好,加一分。 - Chris Lutz
这就是我想要避免指针这个词的意思 - 但再读一遍,它可能可以表达得更好,谢谢。 - Martin Beckett
如果我还没有给你点赞,我现在会这样做。在开始学习C语言时避免使用P字母可能是一个好策略。 - Chris Lutz

2
如果您想将单个字符连接到字符串中,可以使用strncat()函数,并将要连接的字符数指定为1。但请注意以下几点:
  • 目标缓冲区必须具有足够的空间;
  • C89要求必须有实际的char对象以便获取其地址(因此不能直接连接getchar()的结果)。
例如:
char str[5] = "foo";
char c;
int ch;

if ((ch = getchar()) != EOF) {
    c = ch;
    strncat(str, &c, 1);
}

在C99中,您可以使用复合字面量,第二个限制不适用:
char str[5] = "foo";
int ch;

if ((ch = getchar()) != EOF) {
    strncat(str, &(char){ ch }, 1);
}

1
这不起作用,因为*inp字符指针没有任何内存支持它。当您声明为char *inp = "";时,您只给了它一个空字符串的地址。如果您给出以下声明 char *inp ="abcdefg",则可以在覆盖'\0'并陷入麻烦之前写入7个字符。
随着输入更多字符,您需要动态增加支持内存的大小。您还将面临知道何时释放内存以避免内存泄漏的问题。
另外,您的strcat需要在c之前加上'&'。

那么我要如何声明一个固定长度的空字符数组,当有错误发生时为什么输出会那么奇怪呢? - Anonymous
如果他给出了char *inp = "abcdefg";,他就无法安全地向字符串写入任何数据 - 它位于只读内存中。 - Chris Lutz
这并不完全正确。在某些系统上是的,但并非全局通用。 - No One in Particular
同样适用于匿名 - char xyz[50]; memset((void *)xyz, 0, 50); 或者你想要的任何长度。如果该数组是全局声明的,那么惯例是它将被全部清零。但是,一些编译器选项会在空间中写入错误的模式,以便您可以检查不当使用。 - No One in Particular

0

首先,您正在返回指向堆栈分配的数组的指针。这是未定义的行为。其次,您正在尝试将字符写入未分配的内存中。第三,inp 的类型是const char*而不是char*

您需要像这样的东西:

char* input(char *dest) {
  strcpy(dest,"");
  while(1){
  char c=getchar();
  if(c){
   if(c=='\n'||c==EOF){
    break;
   }else{
    strcat(inp,&c);
   }
  }
 }
 return dest;
}

  1. 他返回的不是指向堆栈数组的指针,而是返回指向字符串字面量的指针,该字面量将位于全局存储中。
  2. 它不是未分配的内存,而是只读内存。
  3. inp 的类型是有效的,尽管如果你说类型应该是 const char*,我也不会反驳。
  4. 你的函数和那个一样危险,甚至更危险,因为它没有进行边界检查(这是一个更难发现的错误)。如果你想让用户传入自己的缓冲区,也要让他们传入自己的长度。
- Chris Lutz
char *inp = "" 应该最终存储在堆栈上。通过一些优化,它可能会被放入全局存储器中,假设全局存储器存在于他正在编译的架构上。2. 函数没有显式分配 inp+1。3. 类型应为 const char *。他没有收到错误的唯一原因是因为他使用了遗留标准。4. 虽然我的函数很危险,但它并不比 strcatstring.h 中的其他函数更危险。 - KitsuneYMG

0
你的代码崩溃是因为strcat函数要求两个参数都是以null结尾的字符串。
如果你想要连接一个字符,你需要先将它转换成一个以null结尾的字符串。
char c[2] = {0,0};
c[0] = getchar();
strcat(inp, c);

但是不要使用strcat,除非你能保证输入字符串已经为那个额外的字符分配了空间。最好使用strncat,它还包括第一个参数(目标)中的空间量参数。

我建议先阅读this


0

一个简单的方法是使用snprintf:

char newString[50+1];
snprintf(newString, sizeof(newString), "%s%c", "ljsdflusdfg", getchar());

这是一个简单的解决方案,但需要您大致知道字符串的大小。

注:我使用+1来提醒自己需要留出空间进行空终止,并且snprintf比sprintf更好,因为它给出了缓冲区的大小以防止缓冲区溢出。


-1

将char转换为char数组?
这就像是说你想将char转换为字符串。
你不能直接这样做。
取而代之的是,将它放在一个数组中(myChar[])。
然后,使用strcat将其与原始char数组连接起来。


你需要在使用strcat函数时以0来终止数组。 - Brennan Vincent
@brennan:我已经删除了我的答案中的一个语句,以使其正确。感谢您提醒我关于0的事情。 - Neilvert Noval
我没有给你的评论点“踩”(我本来要点的,但后来你修正了它),虽然你已经把它修改正确了,但并没有让它更清晰或者更有帮助性。了解 C 语言的人可以知道这是正确的,但是不理解为什么将“char”类型强制转换为“char *”类型行不通的人也就不会明白将“char”类型强制转换为字符串类型也是行不通的。你只是陈述了一个重言式。 - Chris Lutz

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