strcat 上的分段错误
Segmentation fault on strcat
最近开始学习C语言,多次运行报错,从<string.h>
模块调用strcat函数导致段错误。我在网上搜索了答案,包括 this Whosebug post,但没有成功。我认为这个社区可能对这个问题有更个人化的见解,因为一般的解决方案似乎并不奏效。可能是用户错误,可能是代码的个人问题。看一看。
#include <stdio.h>
#include <string.h>
char * deblank(const char str[]){
char *new[strlen(str)];
char *buffer = malloc(strlen(new)+1);
for (int i=0; i<strlen(*str); i++){
if(buffer!=NULL){
if(str[i]!=" "){
strcat(new,str[i]); //Segmentation fault
}
}
}
free(buffer);
return new;
}
int main(void){
char str[] = "This has spaces in it.";
char new[strlen(str)];
*new = deblank(str);
puts(new);
}
我已经在我追踪到分段错误的行上发表了评论。以下是一些 Java 使此 C 代码有意义的内容。
public class deblank {
public static void main(String[]args){
String str = "This has space in it.";
System.out.println(removeBlanks(str));
}
public static String removeBlanks(String str){
String updated = "";
for(int i=0; i<str.length(); i++){
if(str.charAt(i)!=' '){
updated+=str.charAt(i);
}
}
return updated;
}
}
任何对此错误的见解将不胜感激。也请指出错别字……众所周知,我会犯错。谢谢
所以,不确定这是否对您有帮助,但是与您的 Java 代码执行相同操作的 C 代码如下所示:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static char *removeBlanks(const char *str)
{
char *result = malloc(strlen(str) + 1);
if (!result) exit(1);
const char *r = str;
char *w = result;
while (*r)
{
// copy each character except when it's a blank
if (*r != ' ') *w++ = *r;
++r;
}
*w = 0; // terminate the result to be a string (0 byte)
return result;
}
int main(void)
{
const char *str = "This has spaces in it.";
char *new = removeBlanks(str);
puts(new);
free(new);
return 0;
}
我不建议命名变量 new
...如果您想使用 C++,这是一个保留关键字。
我尝试在启用警告的情况下进行编译,这里有一些您应该修复的问题。
- 您需要包括
stdlib.h
char *new[strlen(str)]
创建一个 char*
而不是 char
的数组,所以不是真正的字符串。将其更改为 char new[strlen(str)]
.
- 要检查
str[i]
是否为 space,您将其与 space 字符 ' '
进行比较,而不是将其与唯一字符为 [=50= 的字符串进行比较] " "
。所以将其更改为 str[i]!=' '
strcat
将字符串作为第二个参数而不是字符,就像您用 str[i]
. 给它一样
另外,你用 buffer
做什么?
另一个错误是,您可能假设未初始化的数组取零值。 new
数组具有随机值,而不是 zero/null。 strcat
连接两个字符串,因此它会尝试将字符串放在第一个参数 new
末尾的第二个参数中。字符串的 "end" 是空字符。该程序搜索 new
它可以找到的第一个空字符,当它找到这个空字符时,它开始从那里写入第二个参数。
但由于new
未初始化,程序可能无法在new
中找到空字符,并且会继续搜索超过new
、[=29=的长度],继续在未分配的内存中搜索。这可能是导致分段错误的原因。
您不能像以前那样使用 strcat
,它的目的是在另一个给定字符串的末尾连接一个 C 字符串。 str[i]
是一个字符而不是 C 字符串(请记住,C 字符串是一个连续的字符序列,最后一个是 NUL 字节)。
你也不能用标准的比较运算符比较字符串,如果你真的需要比较字符串,那么有一个 strcmp
函数。但是您可以将 char 与标准运算符进行比较,因为 char 只是一种整数类型。
这应该可以解决问题:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char * deblank(const char str[]) {
char *buffer = malloc(strlen(str)+1); // allocate space to contains as much char as in str, included ending NUL byte
for (int i=0, j=0; i<strlen(str)+1; i++) { // for every char in str, included the ending NUL byte
if (str[i]!=' ') { // if not blank
buffer[j++] = str[i]; // copy
}
}
return buffer; // return a newly constructed C-string
}
int main(void){
char str[] = "This has spaces in it.";
char *new = deblank(str);
puts(new);
free(new); // release the allocated memory
}
好的,我们开始吧。
#include <stdio.h>
#include <string.h>
char * deblank(const char str[]){
char *new[strlen(str)];
^ 这一行创建了一个指针数组,而不是一个字符串。
char *buffer = malloc(strlen(new)+1);
malloc
未申报。缺少 #include <stdlib.h>
。另外,你应该在这里检查分配失败。
strlen(new)
是类型错误。 strlen
需要一个 char *
但 new
是(或者更确切地说计算为)一个 char **
.
for (int i=0; i<strlen(*str); i++){
strlen(*str)
是类型错误。 strlen
采用 char *
但 *str
是 char
(即单个字符)。
i<strlen(...)
值得怀疑。 strlen
returns size_t
(无符号类型)而 i
是 int
(有符号,可能太小)。
在循环中调用 strlen
是低效的,因为它必须遍历整个字符串才能找到结尾。
if(buffer!=NULL){
这是一个检查分配失败的奇怪地方。此外,您不会在任何地方使用 buffer
,那么为什么 create/check 呢?
if(str[i]!=" "){
str[i]!=" "
是类型错误。 str[i]
是 char
而 " "
是(或者更确切地说是计算为)char *
.
strcat(new,str[i]); //Segmentation fault
这是类型错误。 strcat
接受两个字符串 (char *
),但 new
是 char **
而 str[i]
是 char
。此外,strcat
的第一个参数必须是有效字符串,但 new
未初始化。
}
}
}
free(buffer);
return new;
new
是这个函数中的局部数组。您正在 returning 它的第一个元素的地址,这是没有意义的:一旦函数 returns,它的所有局部变量都消失了。您在此处return输入了一个无效指针。
此外,这是一个类型错误:deblank
声明为 return a char *
但实际上 returns a char **
.
}
int main(void){
char str[] = "This has spaces in it.";
char new[strlen(str)];
*new = deblank(str);
这是类型错误:*new
是 char
但 deblank
return 是 char *
.
puts(new);
puts
接受一个字符串,但 new
在这一点上本质上是垃圾。
}
可以通过三种方法完成任务。
第一个是更新字符串"in place"。在这种情况下,该函数可以类似于以下方式
#include <stdio.h>
#include <ctype.h>
#include <iso646.h>
char * deblank( char s[] )
{
size_t i = 0;
while ( s[i] and not isblank( s[i] ) ) ++i;
if ( s[i] )
{
size_t j = i++;
do
{
if ( not isblank( s[i] ) ) s[j++] = s[i];
} while( s[i++] );
}
return s;
}
int main(void)
{
char s[] = "This has spaces in it.";
puts( s );
puts( deblank( s ) );
return 0;
}
程序输出为
This has spaces in it.
Thishasspacesinit.
另一种方法是将源字符串复制到目标字符数组中,跳过空格。
在这种情况下,该函数将有两个参数:源数组和目标数组。并且目标数组的大小必须等于源数组的大小,因为通常源数组不能有空格。
#include <stdio.h>
#include <ctype.h>
#include <iso646.h>
char * deblank( char *s1, const char *s2 )
{
char *t = s1;
do
{
if ( not isblank( *s2 ) ) *t++ = *s2;
} while ( *s2++ );
return s1;
}
int main(void)
{
char s1[] = "This has spaces in it.";
char s2[sizeof( s1 )];
puts( s1 );
puts( deblank( s2, s1 ) );
return 0;
}
程序输出与上图相同。
注意这个声明
char s2[sizeof( s1 )];
目标字符串的大小一般应不小于源字符串的大小。
最后第三种方法是在函数内部动态创建一个数组,并从函数返回指向数组第一个元素的指针。
在这种情况下,最好首先计算源数组中的空白数量,以分配适当大小的目标数组。
要使用函数 malloc
和 free
,您需要包含以下内容 header
#include <stdlib.h>
如演示程序所示,可以实现该功能
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <iso646.h>
char * deblank( const char *s )
{
size_t n = 1; /* one byte reserved for the terminating zero character */
for ( const char *t = s; *t; ++t )
{
if ( not isblank( *t ) ) ++n;
}
char *s2 = malloc( n );
if ( s2 != NULL )
{
char *t = s2;
do
{
if ( not isblank( *s ) ) *t++ = *s;
} while ( *s++ );
}
return s2;
}
int main(void)
{
char s1[] = "This has spaces in it.";
char *s2 = deblank( s1 );
puts( s1 );
if ( s2 ) puts( s2 );
free( s2 );
return 0;
}
程序输出与前两个程序相同。
至于标准的C函数strcat
那么它cat两个字符串。
例如
#include <stdio.h>
#include <string.h>
int main(void)
{
char s1[12] = "Hello ";
char *s2 = "World";
puts( strcat( s1, s2 ) );
return 0;
}
目标数组(在本例中为 s1
)必须有足够的 space 才能追加字符串。
C 标准中还有另一个 C 函数 strncat
,它允许将单个字符附加到字符串。例如上面的程序可以改写成下面的方式
#include <stdio.h>
#include <string.h>
int main(void)
{
char s1[12] = "Hello ";
char *s2 = "World";
for ( size_t i = 0; s2[i] != '[=17=]'; i++ )
{
strncat( s1, &s2[i], 1 );
}
puts( s1 );
return 0;
}
但是对您的原始任务使用这种方法效率不高,因为每次调用该函数时,它都必须在源字符串中找到要附加字符的终止零。
你可以递归试试
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void deblank(const char* str, char *dest) {
if (!*str) {*dest = '[=10=]';return;}
// when we encounter a space we skip
if (*str == ' ') {
deblank(str+1, dest);
return;
}
*dest = *str;
deblank(str+1, dest+1);
}
int main(void) {
const char *str = "This has spaces in it.";
char *output = malloc(strlen(str)+1);
deblank(str, output);
puts(output);
free(output);
}
最近开始学习C语言,多次运行报错,从<string.h>
模块调用strcat函数导致段错误。我在网上搜索了答案,包括 this Whosebug post,但没有成功。我认为这个社区可能对这个问题有更个人化的见解,因为一般的解决方案似乎并不奏效。可能是用户错误,可能是代码的个人问题。看一看。
#include <stdio.h>
#include <string.h>
char * deblank(const char str[]){
char *new[strlen(str)];
char *buffer = malloc(strlen(new)+1);
for (int i=0; i<strlen(*str); i++){
if(buffer!=NULL){
if(str[i]!=" "){
strcat(new,str[i]); //Segmentation fault
}
}
}
free(buffer);
return new;
}
int main(void){
char str[] = "This has spaces in it.";
char new[strlen(str)];
*new = deblank(str);
puts(new);
}
我已经在我追踪到分段错误的行上发表了评论。以下是一些 Java 使此 C 代码有意义的内容。
public class deblank {
public static void main(String[]args){
String str = "This has space in it.";
System.out.println(removeBlanks(str));
}
public static String removeBlanks(String str){
String updated = "";
for(int i=0; i<str.length(); i++){
if(str.charAt(i)!=' '){
updated+=str.charAt(i);
}
}
return updated;
}
}
任何对此错误的见解将不胜感激。也请指出错别字……众所周知,我会犯错。谢谢
所以,不确定这是否对您有帮助,但是与您的 Java 代码执行相同操作的 C 代码如下所示:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static char *removeBlanks(const char *str)
{
char *result = malloc(strlen(str) + 1);
if (!result) exit(1);
const char *r = str;
char *w = result;
while (*r)
{
// copy each character except when it's a blank
if (*r != ' ') *w++ = *r;
++r;
}
*w = 0; // terminate the result to be a string (0 byte)
return result;
}
int main(void)
{
const char *str = "This has spaces in it.";
char *new = removeBlanks(str);
puts(new);
free(new);
return 0;
}
我不建议命名变量 new
...如果您想使用 C++,这是一个保留关键字。
我尝试在启用警告的情况下进行编译,这里有一些您应该修复的问题。
- 您需要包括
stdlib.h
char *new[strlen(str)]
创建一个char*
而不是char
的数组,所以不是真正的字符串。将其更改为char new[strlen(str)]
.- 要检查
str[i]
是否为 space,您将其与 space 字符' '
进行比较,而不是将其与唯一字符为 [=50= 的字符串进行比较]" "
。所以将其更改为str[i]!=' '
strcat
将字符串作为第二个参数而不是字符,就像您用str[i]
. 给它一样
另外,你用 buffer
做什么?
另一个错误是,您可能假设未初始化的数组取零值。 new
数组具有随机值,而不是 zero/null。 strcat
连接两个字符串,因此它会尝试将字符串放在第一个参数 new
末尾的第二个参数中。字符串的 "end" 是空字符。该程序搜索 new
它可以找到的第一个空字符,当它找到这个空字符时,它开始从那里写入第二个参数。
但由于new
未初始化,程序可能无法在new
中找到空字符,并且会继续搜索超过new
、[=29=的长度],继续在未分配的内存中搜索。这可能是导致分段错误的原因。
您不能像以前那样使用 strcat
,它的目的是在另一个给定字符串的末尾连接一个 C 字符串。 str[i]
是一个字符而不是 C 字符串(请记住,C 字符串是一个连续的字符序列,最后一个是 NUL 字节)。
你也不能用标准的比较运算符比较字符串,如果你真的需要比较字符串,那么有一个 strcmp
函数。但是您可以将 char 与标准运算符进行比较,因为 char 只是一种整数类型。
这应该可以解决问题:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char * deblank(const char str[]) {
char *buffer = malloc(strlen(str)+1); // allocate space to contains as much char as in str, included ending NUL byte
for (int i=0, j=0; i<strlen(str)+1; i++) { // for every char in str, included the ending NUL byte
if (str[i]!=' ') { // if not blank
buffer[j++] = str[i]; // copy
}
}
return buffer; // return a newly constructed C-string
}
int main(void){
char str[] = "This has spaces in it.";
char *new = deblank(str);
puts(new);
free(new); // release the allocated memory
}
好的,我们开始吧。
#include <stdio.h>
#include <string.h>
char * deblank(const char str[]){
char *new[strlen(str)];
^ 这一行创建了一个指针数组,而不是一个字符串。
char *buffer = malloc(strlen(new)+1);
malloc
未申报。缺少 #include <stdlib.h>
。另外,你应该在这里检查分配失败。
strlen(new)
是类型错误。 strlen
需要一个 char *
但 new
是(或者更确切地说计算为)一个 char **
.
for (int i=0; i<strlen(*str); i++){
strlen(*str)
是类型错误。 strlen
采用 char *
但 *str
是 char
(即单个字符)。
i<strlen(...)
值得怀疑。 strlen
returns size_t
(无符号类型)而 i
是 int
(有符号,可能太小)。
在循环中调用 strlen
是低效的,因为它必须遍历整个字符串才能找到结尾。
if(buffer!=NULL){
这是一个检查分配失败的奇怪地方。此外,您不会在任何地方使用 buffer
,那么为什么 create/check 呢?
if(str[i]!=" "){
str[i]!=" "
是类型错误。 str[i]
是 char
而 " "
是(或者更确切地说是计算为)char *
.
strcat(new,str[i]); //Segmentation fault
这是类型错误。 strcat
接受两个字符串 (char *
),但 new
是 char **
而 str[i]
是 char
。此外,strcat
的第一个参数必须是有效字符串,但 new
未初始化。
}
}
}
free(buffer);
return new;
new
是这个函数中的局部数组。您正在 returning 它的第一个元素的地址,这是没有意义的:一旦函数 returns,它的所有局部变量都消失了。您在此处return输入了一个无效指针。
此外,这是一个类型错误:deblank
声明为 return a char *
但实际上 returns a char **
.
}
int main(void){
char str[] = "This has spaces in it.";
char new[strlen(str)];
*new = deblank(str);
这是类型错误:*new
是 char
但 deblank
return 是 char *
.
puts(new);
puts
接受一个字符串,但 new
在这一点上本质上是垃圾。
}
可以通过三种方法完成任务。
第一个是更新字符串"in place"。在这种情况下,该函数可以类似于以下方式
#include <stdio.h>
#include <ctype.h>
#include <iso646.h>
char * deblank( char s[] )
{
size_t i = 0;
while ( s[i] and not isblank( s[i] ) ) ++i;
if ( s[i] )
{
size_t j = i++;
do
{
if ( not isblank( s[i] ) ) s[j++] = s[i];
} while( s[i++] );
}
return s;
}
int main(void)
{
char s[] = "This has spaces in it.";
puts( s );
puts( deblank( s ) );
return 0;
}
程序输出为
This has spaces in it.
Thishasspacesinit.
另一种方法是将源字符串复制到目标字符数组中,跳过空格。
在这种情况下,该函数将有两个参数:源数组和目标数组。并且目标数组的大小必须等于源数组的大小,因为通常源数组不能有空格。
#include <stdio.h>
#include <ctype.h>
#include <iso646.h>
char * deblank( char *s1, const char *s2 )
{
char *t = s1;
do
{
if ( not isblank( *s2 ) ) *t++ = *s2;
} while ( *s2++ );
return s1;
}
int main(void)
{
char s1[] = "This has spaces in it.";
char s2[sizeof( s1 )];
puts( s1 );
puts( deblank( s2, s1 ) );
return 0;
}
程序输出与上图相同。
注意这个声明
char s2[sizeof( s1 )];
目标字符串的大小一般应不小于源字符串的大小。
最后第三种方法是在函数内部动态创建一个数组,并从函数返回指向数组第一个元素的指针。
在这种情况下,最好首先计算源数组中的空白数量,以分配适当大小的目标数组。
要使用函数 malloc
和 free
,您需要包含以下内容 header
#include <stdlib.h>
如演示程序所示,可以实现该功能
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <iso646.h>
char * deblank( const char *s )
{
size_t n = 1; /* one byte reserved for the terminating zero character */
for ( const char *t = s; *t; ++t )
{
if ( not isblank( *t ) ) ++n;
}
char *s2 = malloc( n );
if ( s2 != NULL )
{
char *t = s2;
do
{
if ( not isblank( *s ) ) *t++ = *s;
} while ( *s++ );
}
return s2;
}
int main(void)
{
char s1[] = "This has spaces in it.";
char *s2 = deblank( s1 );
puts( s1 );
if ( s2 ) puts( s2 );
free( s2 );
return 0;
}
程序输出与前两个程序相同。
至于标准的C函数strcat
那么它cat两个字符串。
例如
#include <stdio.h>
#include <string.h>
int main(void)
{
char s1[12] = "Hello ";
char *s2 = "World";
puts( strcat( s1, s2 ) );
return 0;
}
目标数组(在本例中为 s1
)必须有足够的 space 才能追加字符串。
C 标准中还有另一个 C 函数 strncat
,它允许将单个字符附加到字符串。例如上面的程序可以改写成下面的方式
#include <stdio.h>
#include <string.h>
int main(void)
{
char s1[12] = "Hello ";
char *s2 = "World";
for ( size_t i = 0; s2[i] != '[=17=]'; i++ )
{
strncat( s1, &s2[i], 1 );
}
puts( s1 );
return 0;
}
但是对您的原始任务使用这种方法效率不高,因为每次调用该函数时,它都必须在源字符串中找到要附加字符的终止零。
你可以递归试试
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void deblank(const char* str, char *dest) {
if (!*str) {*dest = '[=10=]';return;}
// when we encounter a space we skip
if (*str == ' ') {
deblank(str+1, dest);
return;
}
*dest = *str;
deblank(str+1, dest+1);
}
int main(void) {
const char *str = "This has spaces in it.";
char *output = malloc(strlen(str)+1);
deblank(str, output);
puts(output);
free(output);
}