【问题标题】:-1 not less than 12? [duplicate]-1 不小于 12? [复制]
【发布时间】:2015-10-05 14:01:42
【问题描述】:

我有 C 中的小程序(子字符串):

char *str_sub(char *string, int from, int to) {
    assert(to < 0 || from < to);

    if (!(to < strlen(string))) {
        printf("%d %ld\n", to, strlen(string));
    }

    assert(from < strlen(string) && to < strlen(string));

    char *result = (char *) calloc(to - from + 1, 1);

    memcpy(result, &string[from], to);
    result[to] = '\0';

当我将 -1 传递给 to 时,我想让函数与字符串的其余部分匹配,但这并不重要。你看到第二个断言了吗?当我将 -1 传递给 to 时,它会引发错误,在上述情况下,它也会显示 false,但它只是 -1 和 12,正如我们从学校知道的那样,-1

那么问题出在哪里?

【问题讨论】:

标签: c string substring assert


【解决方案1】:

您正在比较具有不同符号的变量,因此-1 的表示在它们之间并不统一。另请参阅 cmets 中引用的 @Shafik Yaghmour 的 SO 答案。

我可以像这样显示来自编译器的警告:

$ clang -Wsign-compare -c sign.c
sign.c:9:14: warning: comparison of integers of different signs: 'int' and 'unsigned long' [-Wsign-compare]
    if (!(to < strlen(string))) {
          ~~ ^ ~~~~~~~~~~~~~~
sign.c:13:17: warning: comparison of integers of different signs: 'int' and 'unsigned long' [-Wsign-compare]
    assert(from < strlen(string) && to < strlen(string));
           ~~~~ ^ ~~~~~~~~~~~~~~
/usr/include/assert.h:89:5: note: expanded from macro 'assert'
  ((expr)                                                               \
    ^
sign.c:13:40: warning: comparison of integers of different signs: 'int' and 'unsigned long' [-Wsign-compare]
    assert(from < strlen(string) && to < strlen(string));
                                    ~~ ^ ~~~~~~~~~~~~~~
/usr/include/assert.h:89:5: note: expanded from macro 'assert'
  ((expr)                                                               \
    ^

$ cat sign.c
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

char *str_sub(char *string, int from, int to) {
    assert(to < 0 || from < to);

    if (!(to < strlen(string))) {
        printf("%d %ld\n", to, strlen(string));
    }

    assert(from < strlen(string) && to < strlen(string));

    char *result = (char *) calloc(to - from + 1, 1);

    memcpy(result, &string[from], to);
    result[to] = '\0';

    return result;
}

【讨论】:

    【解决方案2】:

    很多人都说过,to &lt; strlen(string)(size_t)-1 &lt; strlen(string) 相比。由于size_t 是一些无符号类型,(size_t)-1 是一个很大的正值,并且这种比较经常失败。 @Shafik Yaghmour

    修复:考虑size_t

    char *str_sub(char *string, int from, int to) {
        assert(to < 0 || from < to);
        assert(from >= 0);
    
        size_t length = strlen(string);
        size_t uto = to < 0 ? length : to; 
    
        assert(from < uto);
    
        if (!(uto < strlen(string))) {
            printf("%zu %zu\n", uto, strlen(string));
        }
    
        assert(from < length && uto < length);
    
        char *result = malloc(uto - from + 1);
    
        // memcpy(result, &string[from], to);
        memcpy(result, &string[from], uto - from);
        // result[to] = '\0';
        result[uto - from] = '\0';
        return result;
    }
    

    注意:建议为from,to的所有组合定义操作,而不是所有这些assert()

    对于fromto,而不是int,使用size_t 并创建#define MATCH_REST ((size_t)-1)。示例:

    #define  MATCH_REST ((size_t)-1)
    
    char *str_sub(const char *string, size_t from, size_t to) {
      size_t length = strlen(string);
      if (to > length) to = length;
      if (from > to) from = to;
    
      size_t diff = to - from;
      char *result = malloc(diff + 1);
      if (result) {
        memcpy(result, &string[from], diff);
        result[diff] = '\0';
      }
      return result;
    }
    

    【讨论】:

      【解决方案3】:

      比较运算符假定您比较的两个变量属于同一类型。事实并非如此,“-1”无符号作为有符号变量的二进制表示完全不是“-1”,而是变量的最大值。

      【讨论】:

        【解决方案4】:

        strlen 返回一个size_t(无符号相关)变量,在进行任何比较之前,两个变量必须具有相同的类型。因此,其中一个必须转换为另一个。

        在您的示例中,我确定 to 已转换为 unsigned,导致溢出 (-1),并且您可能正在将 strlen(string) 与最大可表示的无符号整数进行比较(可能的结果溢出)。

        所以,解决办法是:

        char *str_sub(char *string, int from, int to) {
          assert(to < 0 || from < to);
        
          if (!(to < (int)strlen(string))) {
            printf("%d %ld\n", to, strlen(string));
          }
        
          assert(from < strlen(string) && to < (int)strlen(string));
        
          char *result = (char *) calloc(to - from + 1, 1);
        
          memcpy(result, &string[from], to);
          result[to] = '\0';
        

        或者,为了避免重新计算:

        char *str_sub(char *string, int from, int to) {
          int length = strlen(string);
        
          assert(to < 0 || from < to);
        
          if (!(to < length)) {
            printf("%d %ld\n", to, length);
          }
        
          assert(from < length && to < length);
        
          char *result = (char *) calloc(to - from + 1, 1);
        
          memcpy(result, &string[from], to);
          result[to] = '\0';
        

        或者,为了安全铸造,太久了strings(感谢@chux 的评论):

        char *str_sub(char *string, int from, int to) {
          size_t length = strlen(string);
        
          assert(length > 0);
          assert(to < 0 || from < to);
        
          if (!(to > 0 && to < length))
            printf("%d %lu\n", to, length);
        
          assert(from < length);
          assert(to < 0 || to < length);
        
          char *result = (char *) calloc(to - from + 1, 1);
        
          memcpy(result, &string[from], to);
          result[to] = '\0';
        

        【讨论】:

        • 不是真的溢出,而是将 0xffffffff 解释为无符号最大值,而不是 -1...只是有点迂腐。
        • int length = strlen(string); 对于长字符串失败。
        • @chux Pedantic 模式再次:它不会“失败”。如果我没记错的话,这是未定义的行为。
        猜你喜欢
        • 1970-01-01
        • 2021-05-17
        • 1970-01-01
        • 1970-01-01
        • 2017-12-18
        • 1970-01-01
        • 2019-08-02
        • 2017-11-09
        • 2019-05-22
        相关资源
        最近更新 更多