int main()
{
//CODE
printf("Enter the destination string : ");
char *dest_string = (char *)malloc(sizeof(char));
int le = 0;
while(dest_string[le - 1] != 'n')
{
dest_string = (char *)realloc(dest_string,sizeof(char) * (le + 1) );
dest_string[le] = getchar();
le++;
}
*(dest_string + le - 1) = ' ';
le = 0;
printf("Enter the source string : ");
char *source_string = (char *)malloc(sizeof(char));
le = 0;
while(source_string[le - 1] != 'n')
{
source_string = (char *)realloc(source_string,sizeof(char) * ( le + 1 ) );
source_string[le] = getchar();
le++;
}
*(source_string + le - 1) = ' ';
_concatenate(dest_string, source_string);
puts(dest_string);
free(dest_string);
free(source_string);
exit(0);
}
/* following function takes two parameter, first parameter is
destination string and second parameter is source string
This function makes changes to destination string by
appending the source string to it */
void _concatenate(char *dest_string, char *source_string){
int le = 0; // loop enumerator
int dest_string_len = strlen(dest_string);
while(source_string[le] != ' ')
{
dest_string = (char *)realloc(dest_string,sizeof(char) * (dest_string_len + le + 1) );
*(dest_string + dest_string_len + le) = *(source_string + le);
le++;
}
dest_string = (char *)realloc(dest_string,sizeof(char) * (dest_string_len + le) );
*(dest_string + dest_string_len + le ) = ' ';
puts(dest_string);
return ;
}
这里有许多需要注意的问题,有大有小,不分先后:
-
main
的两个标准签名是:int main(int argc, char **argv)
和int main(void)
。 -
应该避免以
_
开头的标识符,因为除了极少数例外,它们是为实现保留的。 -
exit(0)
位于main
的末尾,这在很大程度上是不必要的,因为如果main
到达其终止}
,则会隐式地显示return 0;
(从C99开始)。 -
sizeof (char)
保证是1
-
您不需要强制转换
malloc
的返回值,因为void *
可以安全地隐式转换为任何其他指针类型。强制转换返回值被认为是不好的做法。 -
正如评论中指出的,
if (dest_string[le - 1] != 'n')
将在第一次迭代时访问dest_string[-1]
,这是越界的。这是未定义的行为。当然,对于包含source_string
的循环也是如此。 -
您需要检查
*alloc
函数是否失败,通过测试它们的返回值不是NULL
。您不能盲目地重新分配realloc
的返回值,因为在它失败的情况下,原始指针仍然有效,并且您迟早需要将原始指针free
。 -
另一方面,如果
realloc
成功,则认为先前的指针值无效。虽然内存块的基址可能没有改变,但如果不先检查就无法知道这一点,而且测试它们是否相同是毫无意义的。如果返回值不是NULL
,则直接使用该指针。 -
您需要测试
getchar
是否返回文件结束标记(EOF
),否则,如果它发生,您将循环直到内存耗尽。
您没有向我们显示任何头文件,所以我不得不假设您没有包含它们。你需要
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
每当你重复自己的时候,这几乎总是一个信号,你应该把这些部分重新组合成某种形式的抽象。在本例中,您编写的用于从stdin
构建字符串的相同代码部分应该简化为一个函数。
因为你没有错误检查realloc
,我们必须假设它总是成功的。因此,列表中的#8告诉我们,不能可靠地假设main
中的dest_string
在调用_concatenate
之后指向有效内存,其中可能已经移动了位于该指针上的数据,并释放了旧数据
如果没有足够的空间来扩大ptr所指向的内存分配,realloc()创建一个新的分配,复制ptr所指向的旧数据,尽可能多地适合新的分配,释放旧的分配,并返回一个指向已分配内存的指针。
,因此puts(dest_string)
可能访问无效或未初始化的内存,free(dest_string)
可能导致双free。
您使用strlen
来获取目标字符串的长度,但随后您手动计数并复制源字符串。使用其他标准库函数会让代码更简洁:
char *concat(char *dest, char *src) {
size_t dest_len = strlen(dest),
src_len = strlen(src);
char *p = realloc(dest, dest_len + src_len + 1);
return p ? strcat(p, src) : NULL;
}
正如你所看到的,管理内存和执行实际的字符串操作应该是分开的;strcat
并不关心目标缓冲区是否动态分配,只要调用者确保结果有足够的空间。
为每个字节重新分配并不是一个很好的策略,但它很简单,并且确实有效。
下面是一个完整的示例程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *concat(char *, char *);
char *input(const char *);
int main(void) {
char *dest = input("Enter the destination string : "),
*source = input("Enter the source string : "),
*result;
if (!dest || !source || !(result = concat(dest, source))) {
free(dest);
free(source);
fprintf(stderr, "Failed to allocate memory.n");
return EXIT_FAILURE;
}
puts(result);
free(result);
free(source);
}
char *concat(char *dest, char *src) {
size_t dest_len = strlen(dest),
src_len = strlen(src);
char *p = realloc(dest, dest_len + src_len + 1);
return p ? strcat(p, src) : NULL;
}
char *input(const char *msg) {
int ch;
size_t length = 0;
char *buf = malloc(1),
*rebuf;
if (!buf) return NULL;
if (msg) printf("%s", msg);
while ((ch = getchar()) != EOF && ch != 'n') {
if (!(rebuf = realloc(buf, length + 1))) {
free(buf);
return NULL;
}
(buf = rebuf)[length++] = ch;
}
buf[length] = ' ';
return buf;
}