该代码具有多种未定义行为的情况:
-
您将使用自动存储功能在strcaT
中返回本地数组的地址,这意味着一旦超出范围,即从函数返回时,该数组将无法再使用。 / p>
-
缓冲区大小太小,它应该是长度的总和加上空终止符的1。您写的内容超出了此本地数组的末尾,可能会覆盖一些重要信息,例如调用方的framce指针或返回地址。这种不确定的行为很可能导致分割错误。
-
您从第一个字符串复制strlen(t)+strlen(s)
个字节,访问范围超出t
的末尾。
-
目前尚不清楚为什么要测试'\n'
并将第二个字符串复制到第一个字符串中换行符的位置。字符串不以换行符结尾,它们可以包含换行符,但以空终止符(字节值'\0'
或简单地为0
)。由fgets()
读取的字符串可能在空终止符之前有尾随换行符,但并非所有字符串都具有。在循环中,当您继续从第一个字符串中复制字节时,甚至超过其空终止符时,复制第二个字符串的效果都会立即被取消。您应该分别执行这些循环,首先从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;
}
,
对于初学者来说,函数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;
}
,
在主要功能中
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,i++) {
a[i] = s[j];
}
a[i] = '\0';
return a;
}
,
我已将您的程序更改为如下所示:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char* strcaT();
char* a = "first";
char* b = "second";
printf("%s",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;
}
之所以会出现段错误,是因为您要返回的是本地堆栈的地址,该地址在堆栈中,返回后将无法访问。其次,连接字符串的逻辑很复杂。
本文链接:https://www.f2er.com/2348953.html