所以我正在通过“Sams每天一小时教你自己C编程,第七版”第10课练习7,要求“编写一个接受两个字符串的函数。使用malloc()函数分配足够的内存来保存它们被连接(链接)后的两个字符串。返回一个指向这个新字符串的指针。“
我相信有更多优雅的方式来解决这个问题,而不是我在下面尝试的方法。我最感兴趣的是为什么我的解决方案不起作用。我只学习了几个月的C并且没有重要的编程背景。请让我知道为什么这会在编译时崩溃。我正在使用Win 7上的代码块与GNU GCC编译器,如果这有所作为。谢谢 :)
#include <stdio.h>
#include <stdlib.h>
char * concatenated(char array1[], char array2[]);
int ctrtotal;
int main(void)
{
char *comboString;
char *array1 = "You\'re the man ";
char *array2 = "Now Dog!";
comboString = (char *)malloc(ctrtotal * sizeof(char));
concatenated(array1, array2);
if (comboString == NULL)
{
puts("Memory error");
exit(1);
}
puts(comboString);
free(comboString);
return 0;
}
char * concatenated(char array1[], char array2[])
{
char *array3;
int ctr;
int ctr2;
for (ctr = 0; array1[ctr] != '\0'; ctr++)
array3[ctr] = array1[ctr];
ctr2 = ctr;
for (ctr = 0; array2[ctr] != '\0'; ctr++)
{
array3[ctr2 + ctr] = array2[ctr];
}
array3[ctr2 + ctr + 1] = '\0';
ctrtotal = (ctr2 + ctr + 2);
return array3;
}
感谢您的帮助。在审核了每个人对我的错误的反馈之后,我将代码修改为以下内容:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * concatenated(char array1[], char array2[]);
int main(void)
{
char *array1 = "Testing Testing One Two ";
char *array2 = "Three. Finally, not crashing the mem o ry.";
char *comboString = malloc( (strlen(array1)+strlen(array2) + 1)*sizeof(char));
comboString = concatenated(array1, array2);
if (comboString == NULL)
{
puts("Memory error");
exit(1);
}
puts(comboString);
free(comboString);
return 0;
}
char * concatenated(char array1[], char array2[])
{
char *array3;
array3 = malloc( (strlen(array1)+strlen(array2) + 1)*sizeof(char) );
strcat(array3, array1);
strcat(array3, array2);
return array3;
}
如果有人看到任何裁员/不必要的剩余代码,可以/应该删除,请告诉我。我认识到尽可能简洁的好处。
您的代码有很多问题:
int ctrtotal
永远不会被初始化,所以你是malloc
ing 0字节concatenated()
正在将字符复制到未初始化的array3
。该指针应指向malloc
d缓冲区。concatenated
正在分配内存,那么main
不需要。相反,它应该使用concatenated
的结果。我不想给你完整的代码,让你错过这个学习机会。所以concatenated
在psuedo代码中看起来应该是这样的:
count = length_of(string1) + length_of(string2) + 1
buffer = malloc(count)
copy string1 to buffer
copy string2 to buffer, after string1
set the last byte of buffer to '\0' (NUL)
return buffer
在C中,字符串表示为NUL
-terminated array of characters。这就是为什么我们分配一个额外的字节,并用\0
终止它。
作为旁注,在处理字符串时,使用指针更容易,而不是将它们视为数组并通过索引访问它们。
这里有很多代码,没有任何意义。我建议你先在纸上写这个程序。然后,“执行”你头脑中的程序,逐步执行每一行。如果你遇到了一些你不理解的东西,那么你需要修复你的理解,或者你的错误代码。不要尝试编写看起来像其他代码的代码。
还有一个名为strcat
的库函数,它可以使这项任务变得更加容易。看看你是否可以在这里弄清楚如何使用它。
Spoiler --> #include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *concatenate2(const char* s1, const char* s2);
int main(void)
{
char *comboString;
char *array1 = "You're the man ";
char *array2 = "Now Dog!";
comboString = concatenate2(array1, array2);
if (comboString == NULL)
{
puts("Memory error");
exit(1);
}
puts(comboString);
free(comboString);
return 0;
}
char *concatenate2(const char* s1, const char* s2)
{
char *result;
result = malloc(strlen(s1) + strlen(s2) + 1);
*result = '\0';
strcat(result, s1);
strcat(result, s2);
return result;
}
你忘了为第三个连接的字符数组分配内存(在函数中)你应该做这样的事情:
char *array3;
array3 = (char *)malloc( (strlen(array1)+strlen(array2) + 1)*sizeof(char) ); // +1 for '\0' character.
然后将第一个和第二个数组中的字符写入第三个。
也许漫步问题代码是最好的。
#include <stdio.h>
#include <stdlib.h>
char * concatenated(char array1[], char array2[]);
int ctrtotal;
请注意,上面的行将ctrtotal声明为整数,但不指定整数的值。
int main(void)
{
char *comboString;
char *array1 = "You\'re the man ";
char *array2 = "Now Dog!";
comboString = (char *)malloc(ctrtotal * sizeof(char));
请注意,上面的行分配内存并设置'comboString'指向该内存。但是,分配了多少内存?
(ctrtotal [???] * sizeof(char)[1])
(??? * 1)的价值是多少?这是个问题。
concatenated(array1, array2);
上面这一行的意图是array1 [“你是男人”]和array2 [“Now Dog!”]将被连接起来形成一个新的字符串[“你是那个男人现在的狗!”],它将被放置在已分配的内存中并返回给调用者。
不幸的是,这里没有捕获包含字符串的返回内存。例如,上面的行应该是:
comboString = concatenated(array1, array2);
虽然这是有道理的,但对于这一行,它引出了一条线的目的问题:
comboString = (char *)malloc(ctrtotal * sizeof(char));
以及全局变量:
int ctrtotal;
以及后来的参考:
ctrtotal = (ctr2 + ctr + 2);
也许所有这3行都应删除?
if (comboString == NULL)
{
puts("Memory error");
exit(1);
}
puts(comboString);
free(comboString);
return 0;
}
char * concatenated(char array1[], char array2[])
{
char *array3;
请注意,'* array3'现在是一个已定义的指针,但它并未指向任何特定的指针。
int ctr;
int ctr2;
'concatenated()'的目的是将array1和array1连接到已分配的array3中。不幸的是,没有为array3分配内存。
下面,将修改array3指向的内存。由于array3没有指向任何特定的位置,因此这不安全。
在修改数组3所指向的内存之前,重要的是将array3指向可以安全修改字节的内存。我建议在这里插入以下代码:
array3 = malloc(strlen(array1) + strlen(array2) + 1);
现在,array3指向已分配的内存,大到足以容纳两个字符串加上字符串终止字符'\ 0'。
for (ctr = 0; array1[ctr] != '\0'; ctr++)
array3[ctr] = array1[ctr];
ctr2 = ctr;
for (ctr = 0; array2[ctr] != '\0'; ctr++)
{
array3[ctr2 + ctr] = array2[ctr];
}
array3[ctr2 + ctr + 1] = '\0';
ctrtotal = (ctr2 + ctr + 2);
return array3;
}
我正在回复您修改后的代码。它有一些错误。
...
char *array2 = "Three. Finally, not crashing the mem o ry.";
char *comboString = malloc( (strlen(array1)+strlen(array2) + 1)*sizeof(char));
comboString = concatenated(array1, array2);
...
这里不需要malloc,实际上是代码中的错误。你正在分配一块内存,但是然后用指向调用concatenated.
的指针替换指针comboString的值你丢失了指向main中分配的内存块的指针,因此永远无法释放它。虽然这不会是你现在拥有的代码中的问题,因为很快就会发生主要的返回,它可能会导致应用程序运行较长时间内存泄漏。
strcat(array3, array1);
这也是一个错误。 strcat
将遍历array3以找到'\ 0',然后一旦发现从该索引中复制array1,替换'\ 0'。这在这里工作正常,因为为array3分配的内存块将被清零**因为您的程序尚未释放任何块。但是,在运行时间较长的程序中,最终可能会出现一个不以'\ 0'开头的块。您可能最终破坏堆,获得段错误等。
要解决这个问题,你应该使用strcpy,array3[0] = '\0',
或*array3 = '\0'
**当操作系统启动程序时,它会初始化它为零保留的内存段(这实际上不是必需的,但几乎在任何操作系统上都是如此)。当你的程序分配并释放内存时,你最终会得到不为零的值。请注意,未初始化的局部变量可能会出现相同的错误,例如:
int i;
for (; i < 10; i++);
每当存储i
的运行时堆栈上的空间已经为0时,此循环将运行10次。
总的来说,外卖是要非常小心C中的数组和动态内存分配.C不提供现代语言所做的保护。您有责任确保保持在数组的范围内,初始化变量,并正确分配和释放内存。忽视这些事情会导致隐藏的错误,需要花费数小时才能找到,而且大多数时候这些错误都不会马上出现。