【问题标题】:Assert is failing in a string array comparison [duplicate]断言在字符串数组比较中失败[重复]
【发布时间】:2019-09-10 09:46:46
【问题描述】:

在我的这部分代码中,我删除了string1 的空格并将结果复制到string2

char * remove_blank_spaces(char * string1) {
   char * string2 = malloc(sizeof(string1));
   int index = 0;

   for (int i = 0; string1[i] != 0; i++) {
      if(string1[i] != ' ') {
         //printf("i: %d\n", i);
         //printf("c2: %c\n", string1[i]);
         string2[index] = string1[i];
         index++;
      }
   }
   string2[index] = '\0';

   printf("string2: %s\n", string2);
   return string2;
}

我检查结果:

assert(remove_blank_spaces("a  b") == "ab");  // Edit: here is the error!

我收到一个错误:Assertion failed!Expression: remove_blank_spaces("a b") == "ab"

我比较了 Virtual-C 中的字符串,它们看起来是一样的。 为什么断言失败?

【问题讨论】:

  • 使用strcmp比较字符串,而不是==
  • sizeof of a pointer 是指针本身的大小,而不是它所指向的大小。要获取字符串的长度(不包括非常重要的空终止符),请使用strlen
  • 至于您的断言,这是因为您比较了两个永远不会相等的指针。如前所述,您使用strcmp 解决了这个问题。也许您应该考虑更新您的基本 C 知识?
  • 不,因为string1 仍然是一个指针,而sizeof string1 会给你指针本身的大小。您需要使用strlen(string1) + 1+1 表示终止符)。另请注意,sizeof(char) 由 C 规范定义为始终等于 1
  • 最后,您的代码如图所示存在内存泄漏。由于您不存储由remove_blank_spaces 返回的指针,因此您不可能free 函数分配的内存。

标签: c string comparison assert c-strings


【解决方案1】:

您的代码有一个错误:malloc 分配的空间不足,这会导致在尝试访问未分配的内存时出现未定义的行为。

断言也失败了,因为您是通过 == 比较指针,而不是通过 strcmp 比较 C 字符串。

此外,我建议进行两处更改:

  • 不要混合计算和输出。 返回值,不要在函数内部printf它。
  • 使用描述性的和正确的名称。这需要考虑上下文。例如,index 通常是一个好名字,但在您的情况下,不清楚您指的是 哪个 索引,这会引发错误,其中 index 用于索引错误多变的。至于“正确”的名称,您所说的“空格”通常称为“空白”。

为了改进第二点,我建议实际更改实现,而不是使用第二个index 变量,而是使用指针迭代输出。还有其他可能性,但这种可能性的优点是不可能意外使用错误的变量进行索引。

综合起来,我们得到

char *remove_whitespace(const char *str) {
    char *result = malloc(strlen(str) + 1);
    char *out = result;

    for (size_t i = 0; str[i] != '\0'; i++) {
       if (str[i] != ' ') {
           *out++ = str[i];
       }
    }
    *out = '\0';
    return result;
}

我们还可以取消 i 循环计数器。不幸的是,结果可读性更少,而不是更多,因为我们需要在循环结束时增加str,这会给我们留下难看的for (; *str != '\0'; str++)循环结构。

【讨论】:

  • 正如其他人所指出的,此代码还可能会分配 way 过多的内存。但是,这可能是可以接受的,具体取决于具体的用例。
  • 请问你为什么把变量调出来?
  • @kame 因为它是一个用于 output 的指针,即将值写入(以及使用它的代码并不关心值的去向,仅此而已它去“某处”)。在 C++ 中,这被正式称为“输出迭代器”,但这个概念也存在于 C 中。
【解决方案2】:

对于初学者这个函数声明

char * remove_blank_spaces(char * string1) {

不正确,只会使函数的用户感到困惑。如果在函数中创建新字符数组,则参数应具有限定符 const

char * remove_blank_spaces( const char * string1) {

否则该函数应该“就地”更改原始字符串。

这个电话

char * string2 = malloc(sizeof(string1));

也是不正确的。我想你的意思是

char * string2 = malloc( strlen( string1 ) + 1 );

但即使是这个调用也不是很好,因为结果字符串可能比原始字符串少很多。

所以首先你应该计算结果字符串中的字符数,然后才分配内存。

这个断言也不正确

assert(remove_blank_spaces("a  b") == "ab");

在这个表达式中,比较了两个字符串的地址:第一个是函数返回的字符串,第二个是字符串字面量。

即使你会写出这样的表达式

assert( "ab" == "ab");

表达式的值可以等于逻辑真或假,具体取决于编译器选项,该选项指定相等的字符串文字是作为一个字符串文字存储还是占用不同的内存范围。

你应该改写

assert( strcmp( remove_blank_spaces("a  b"), "ab" ) == 0 );

考虑到在 if 语句中考虑制表符 '\t' 也是合理的

if(string1[i] != ' ' && string1[i] != '\t') {

或者你可以使用标准函数isblank

这是一个演示程序

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
#include <string.h>

char * remove_blank_spaces( const char *s ) 
{
    size_t n = 0;

    for ( size_t i = 0; s[i] != '\0'; i++ )
    {
        if ( !isblank( ( unsigned char )s[i] ) ) ++n;
    }

    char *result = malloc( n + sizeof( ( char )'\0' ) );

    char *p = result;

    do
    {
        if ( !isblank( ( unsigned char )*s ) )
        {
            *p++ = *s;
        }
    } while ( *s++ != '\0' );

    return result;
}

int main(void) 
{
    const char *s1 = "a b";
    char *s2 = remove_blank_spaces( s1 );

    assert( strcmp( s1, s2 ) == 0 );

    puts( s2 );

    free( s2 );

    return 0;
}

程序输出是

ab

请注意,而不是其他答案中显示的类型int,您应该为变量indexi 使用类型size_t,因为它是用于字符串长度的类型和索引和函数 malloc。 int 类型不够大,无法存储字符串的大小。

如果你确实想像这样声明函数

char * remove_blank_spaces( char *s ) 

即当参数没有限定符 const 时,您不应在函数内动态分配新的字符数组,函数本身看起来会简单得多。

这是一个演示程序。

#include <stdio.h>
#include <assert.h>
#include <string.h>

char * remove_blank_spaces( char *s ) 
{
    char *destination = s;
    char *source = s;

    do
    {
        if ( *source != ' ' && *source != '\t' )
        {
            *destination++ = *source;
        }
    } while ( *source++ != '\0' );

    return s;
}

int main(void) 
{
    char s[] = "a b";

    remove_blank_spaces( s );

    assert( strcmp( s, "ab" ) == 0 );

    puts( s );

    return 0;
}

它的输出是

ab

【讨论】:

  • sizeof( ( char )'\0' ) 一定是我见过的最复杂的1 拼写方式之一。它真的增加了可读性吗?我对此表示怀疑。我也不确定在isblank 调用中对unsigned char 的强制转换应该完成什么。这没有任何意义。
  • @KonradRudolph 是的,它确实增加了可读性。与其使用幻数 1,不如指定为哪个字符保留一个字节。
  • 你需要平衡混乱和明确。而+ 1malloc 中的char* 被普遍理解为指的是空终止(实际上,它不可能意味着其他任何东西),没有必要为显而易见的事情而苦恼。这类似于在i++ 之后添加评论说“这增加了 i”。从这个意义上说,在代码中添加1 的偏移量通常不会被视为“幻数”。
  • @KonradRudolph 你可以随心所欲。请不要指点我如何编写定性代码。如果您在自己的代码中将类型 int 替换为类型 size_t 会更好,就像我在回答中所做的那样。运气不好。)
  • “请不要告诉我如何编写定性代码”——嗯,这就是 Stack Overflow 的全部目的。顺便说一句,使用size_t 的建议很好。
【解决方案3】:

因为你的代码有一些问题。

首先,您在指针上使用sizeof,它给出了指针的大小而不是字符串的大小。解决方案是使用strlen 加上一个字符的大小作为空字符,因为它是空字符终止的。

第二个也是最后一个我认为在比较您使用的== 的字符串时,这将比较C 中的字符串指针而不是字符串。解决方案是使用strcmp

那么代码会是这样的

char * remove_blank_spaces(char * string1) {
   char * string2 = malloc(strlen(string1) + sizeof(char)); // < -- one change here, was sizeof(string1)
   int index = 0;

   for (int i = 0; string1[i] != 0; i++) {
      if(string1[i] != ' ') {
         //printf("i: %d\n", i);
         //printf("c2: %c\n", string1[i]);
         string2[index] = string1[i];
         index++;
      }
   }
   string2[index] = '\0';

   printf("string2: %s\n", string2);
   return string2;
}

断言将如下所示:

assert(strcmp(remove_blank_spaces("a b"), "ab") == 0);

【讨论】:

  • malloc(strlen(string1))错了。
  • 其实这也是错的,C 中的sizeof '\0'sizeof(int),而不是sizeof(char)。此外,sizeof(char) 的定义为 1,因此您永远不需要编写它。
  • 哈哈确实...显然我知道...sizeof(char)似乎更正确
  • @memosdp 您的断言不起作用。以下正在工作。 assert(strcmp(remove_blank_spaces("a b"), "ab") == 0);
  • 我实际上并没有检查断言是否有效......我的错:)......编辑帖子
猜你喜欢
  • 2021-02-10
  • 2014-01-28
  • 1970-01-01
  • 2011-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-02
  • 2016-07-05
相关资源
最近更新 更多