我最近开始学习 C 语言,并多次遇到错误,其中从<string.h>
模块调用 strcat 函数会导致分段错误。 我已经在网上搜索了答案,包括在这个堆栈溢出帖子上,但没有成功。 我认为这个社区可能对这个问题有更个人的见解,因为一般的解决方案似乎不起作用。 可能是用户错误,可能是代码的个人问题。 看一看。
#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;
}
}
对此错误的任何见解将不胜感激。 也请指出错别字...众所周知,我会制作它们。 谢谢。
好的,让我们这样做。
#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
返回size_t
(无符号类型),而i
返回int
(有符号,可能太小)。
在循环中调用strlen
效率低下,因为它必须遍历整个字符串才能找到结尾。
if(buffer!=NULL){
这是一个检查分配失败的奇怪地方。另外,您不会在任何地方使用buffer
,那么为什么要创建/检查它呢?
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
是此函数中的局部数组。你返回它的第一个元素的地址,这是没有意义的:一旦函数返回,它的所有局部变量都消失了。您在此处返回的指针无效。
此外,这是一个类型错误:deblank
被声明为返回char *
但实际上返回一个char **
。
}
int main(void){
char str[] = "This has spaces in it.";
char new[strlen(str)];
*new = deblank(str);
这是一个类型错误:*new
是char
,但deblank
返回char *
。
puts(new);
puts
需要一个字符串,但此时new
本质上是垃圾。
}
你不能像以前那样使用strcat
,它的目的是在另一个给定字符串的末尾连接一个 C 字符串。str[i]
是字符而不是 C 字符串(请记住,C 字符串是连续的字符序列,最后一个是 NUL 字节)。
您也不能使用标准比较运算符比较字符串,如果您确实需要比较字符串,那么有一个strcmp
函数。但是您可以将字符与标准运算符进行比较,因为 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
}
所以,不确定这是否对你有帮助,但是与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]
是否是空格,请将其与空格字符' '
进行比较,而不是唯一字符是空格" "
的字符串。因此,请将其更改为str[i]!=' '
strcat
将字符串作为第二个参数而不是字符,就像您用str[i]
给出它一样。
另外,您使用buffer
做什么?
另一个错误是,您可能假设未初始化的数组采用零值。new
数组具有随机值,而不是零/空。strcat
连接两个字符串,因此它会尝试将字符串放在第一个参数末尾的第二个参数中new
。字符串的"结尾"是空字符。程序new
搜索它能找到的第一个空字符,当它找到这个空字符时,它开始从那里编写第二个参数。
但是因为new
是未初始化的,程序可能不会在new
中找到空字符,它会继续搜索超过new
、strlen(str)
的长度,继续在未分配的内存中搜索。这可能就是导致分段错误的原因。
任务可以有三种方法。
第一个是"就地"更新字符串。在这种情况下,该函数可能如下所示
#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
,您需要包含以下标头
#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
然后它猫两个字符串。
例如
#include <stdio.h>
#include <string.h>
int main(void)
{
char s1[12] = "Hello ";
char *s2 = "World";
puts( strcat( s1, s2 ) );
return 0;
}
目标数组(在本例中为s1
)必须有足够的空间才能附加字符串。
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] != ' '; 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 = ' ';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);
}