我是新手,想学好点子。所以这是我自己的尝试,写出我的 strcat
函数。如果我只返回 a
它打印了一些二进制的东西(我认为它应该打印解决方案),如果我返回了 *a
它说 seg fault core dumped
我找不到错误的地方。接受任何帮助,谢谢。
#include <stdio.h>
#include <string.h>
int main() {
char *strcaT();
char *a = "first";
char *b = "second";
printf("%s", strcaT(a, b));
return 0;
}
char *strcaT(char *t, char *s) {
char buffer[strlen(t) + strlen(s) - 1];
char *a = &buffer[0];
for (int i = 0; i < strlen(s) + strlen(t); i++, t++) {
if (*t == '\n') {
for (int i = 0; i < strlen(s);i++) {
buffer[strlen(t) + i] = *(s + i);
}
}
buffer[i] = *(t + i);
}
return a;
}
这段代码有多个未定义行为的情况。
你返回一个本地数组的地址 strcaT
自动存储,这意味着这个数组一旦超出作用域就不能再使用了,即:当你从函数返回时。
缓冲区的大小太小,应该是长度之和加上1作为空结束符。你写的内容超过了这个局部数组的结束,可能会覆盖一些重要的信息,如调用者的framce指针或返回地址。这种未定义的行为很有可能导致分段故障。
你复制 strlen(t)+strlen(s)
字节,访问的范围超过了第一个字符串的末尾。t
.
不清楚为什么你要测试 '\n'
并在第一个字符串中换行的位置复制第二个字符串。字符串不以换行结束,它们可能包含一个换行,但在空终止符处(字节值为 '\0'
或干脆 0
). 读取的字符串 fgets()
可能会在null结束符前有一个尾部的换行,但并不是所有的字符串都是如此。在你的循环中,当你继续从第一个字符串中复制字节时,复制第二个字符串的效果会被立即取消,甚至超过它的空结束符。您应该分别执行这些循环,首先从 t
那么,从 s
,无论任何一个字符串是否包含换行符。
另外,请注意,声明为 strcaT()
就地 main()
,甚至没有一个合适的原型。在 main
函数及其参数列表。
这里是一个修改过的版本,它分配了连接的字符串。
#include <stdio.h>
#include <stdlib.h>
char *strcaT(const char *s1, const char *s2);
int main() {
const char *a = "first";
const char *b = "second";
char *s = strcaT(a, b);
if (s) {
printf("%s\n", s);
free(s);
}
return 0;
}
char *strcaT(const char *t, const char *s) {
char *dest = malloc(strlen(t) + strlen(s) + 1);
if (dest) {
char *p = dest;
/* copy the first string */
while (*t) {
*p++ = *t++;
}
/* copy the second string at the end */
while (*s) {
*p++ = *s++;
}
*p = '\0'; /* set the null terminator */
}
return dest;
}
但是请注意,这不是 strcat
函数的作用是:它在第一个字符串的末尾复制第二个字符串,所以在第一个字符串的末尾之后,它的数组中必须有足够的空间来容纳第二个字符串,包括空结束符。函数的定义是 a
和 b
在 main()
将不适合这些语义,你必须让 a
数组,大到足以容纳两个字符串。
这里是用这种方法修改的版本。
#include <stdio.h>
char *strcaT(char *s1, const char *s2);
int main() {
char a[12] = "first";
const char *b = "second";
printf("%s\n", strcaT(a, b));
return 0;
}
char *strcaT(char *t, const char *s) {
char *p = t;
/* find the end of the first string */
while (*p) {
*p++;
}
/* copy the second string at the end */
while (*s) {
*p++ = *s++;
}
*p = '\0'; /* set the null terminator */
return t;
}
返回一些局部变量是个很糟糕的主意, 它将在函数完成操作后被清空. 下面的函数应该是可行的。
char* strcaT(char *t, char *s)
{
char *res = (char*) malloc(sizeof(char) * (strlen(t) + strlen(s) + 1));
int count = 0;
for (int i = 0; t[i] != '\0'; i++, count++)
res[count] = t[i];
for (int i = 0; s[i] != '\0'; i++, count++)
res[count] = s[i];
res[count] = '\0';
return res;
}
在主函数中
char *strcaT();
应在 main
函数。
char *strcaT(char *t, char *s);
int main() {...}
你返回本地数组 buffer[]
它是未定义的行为,因为从 strcaT
函数,它可能不存在。你应该使用指针然后为它分配。
你的 buffer
应是 +1
不 -1
就像你在代码中做的那样。
char *strcaT(char *t, char *s) {
char *a = malloc(strlen(t) + strlen(s) + 1);
if (!a) {
return NULL;
}
int i;
for(i = 0; t[i] != '\0'; i++) {
a[i] = t[i];
}
for(int j = 0; s[j] != '\0'; j++,i++) {
a[i] = s[j];
}
a[i] = '\0';
return a;
}
测试的完整代码。
#include <stdio.h>
#include <stdlib.h>
char *strcaT(char *t, char *s);
int main() {
char *a = "first";
char *b = "second";
char *str = strcaT(a, b);
if (str != NULL) {
printf("%s\n", str);
free(str); // Never forget freeing the pointer to avoid the memory leak
}
return 0;
}
char *strcaT(char *t, char *s) {
char *a = malloc(strlen(t) + strlen(s) + 1);
if (!a) {
return NULL;
}
int i;
for(i = 0; t[i] != '\0'; i++) {
a[i] = t[i];
}
for(int j = 0; s[j] != '\0'; j++,i++) {
a[i] = s[j];
}
a[i] = '\0';
return a;
}
首先是函数 strcaT
应该将第二个参数指定的字符串追加到第一个参数指定的字符串的末尾。所以第一个参数应该指向一个足够大的字符数组来存储追加的字符串。
你的函数是不正确的,因为至少它返回了一个指向本地可变长度字符数组的(无效)指针,而这个数组在退出函数后将不再存在,此外,这个数组的大小小于存储两个字符串所需的大小,也就是说,这个数组的大小不适合存储字符串。
char buffer[strlen(t) + strlen(s) - 1];
^^^
至少应该声明为
char buffer[strlen(t) + strlen(s) + 1];
^^^
并可声明为静态
static char buffer[strlen(t) + strlen(s) + 1];
另外嵌套循环也不合理。
注意在调用函数之前应该提供函数的原型,这样的话编译器就可以检查传递给函数的参数。在这种情况下,编译器将能够检查传递给函数的参数。而函数的名称 strcaT
是混乱的。至少函数可以命名为 strCat
.
该函数可以用以下方式定义
#include <stdio.h>
#include <string.h>
char * strCat( char *s1, const char *s2 )
{
char *p = s1 + strlen( s1 );
while ( ( *p++ = *s2++ ) );
return s1;
}
int main(void)
{
enum { N = 14 };
char s1[N] = "first";
char *s2 = "second";
puts( strCat( s1, s2 ) );
return 0;
}
程序输出为
firstsecond
另一方面,如果你已经使用标准的C函数 strlen
那为什么不使用另一个标准的C函数 strcpy
?
有了这个函数,你的函数可以定义得更简单一些,如
char * strCat( char *s1, const char *s2 )
{
strcpy( s1 + strlen( s1 ), s2 );
return s1;
}
如果你想建立一个新的字符数组,其中包含两个字符串,一个附加到另一个字符串,那么函数可以看起来像下面的方式。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char * strCat( const char *s1, const char *s2 )
{
size_t n1 = strlen( s1 );
char *result = malloc( n1 + strlen( s2 ) + 1 );
if ( result != NULL )
{
strcpy( result, s1 );
strcpy( result + n1, s2 );
}
return result;
}
int main(void)
{
char *s1 = "first";
char *s2 = "second";
char *result = strCat( s1, s2 );
if ( result ) puts( result );
free( result );
return 0;
}
同样,程序输出是
firstsecond
当然,对标准C函数 strcpy
你可以用自己的循环来代替,但是这样做意义不大。
如果你不允许使用标准的C字符串函数,那么上面的函数可以用下面的方式实现。
#include <stdio.h>
#include <stdlib.h>
char * strCat( const char *s1, const char *s2 )
{
size_t n = 0;
while ( s1[n] != '\0' ) ++n;
for ( size_t i = 0; s2[i] != '\0'; )
{
n += ++i;
}
char *result = malloc( n + 1 );
if ( result != NULL )
{
char *p = result;
while ( ( *p = *s1++ ) != '\0' ) ++p;
while ( ( *p = *s2++ ) != '\0' ) ++p;
}
return result;
}
int main(void)
{
char *s1 = "first";
char *s2 = "second";
char *result = strCat( s1, s2 );
if ( result ) puts( result );
free( result );
return 0;
}
我已经把你的程序改成了下面的样子。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char* strcaT();
char* a = "first";
char* b = "second";
printf("%s",strcaT(a,b));
return 0;
}
char* strcaT(char *t, char *s)
{
char* a = (char*)malloc(sizeof(char)*(strlen(t) + strlen(s) + 1));
for(int i=0; i<strlen(t); i++) {
a[i] = t[i];
}
for(int i=0; i<strlen(s); i++) {
a[strlen(t) + i] = s[i];
}
a[strlen(t)+strlen(s)] = '\0';
return a;
}
你之所以会出现segfault,是因为你返回的是一个本地数组的地址,而这个数组是在堆栈上的,返回后将无法访问。二是你的字符串连接逻辑比较复杂。