-1 不小于 12?

-1 not less than 12?

我有 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] = '[=10=]';

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

那么问题出在哪里?

您正在比较具有不同符号的变量,因此 -1 的表示在它们之间不统一。另请参阅评论中引用的@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] = '[=10=]';

    return result;
}

strlen returns 一个 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] = '[=10=]';

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

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] = '[=11=]';

或者,为了进行安全转换,时间过长 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] = '[=12=]';

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

很多人说过,to < strlen(string) 比较 (size_t)-1 < strlen(string)。由于 size_t 是某种无符号类型,因此 (size_t)-1 是一个很大的正值,这种比较经常失败。

修复:考虑 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] = '[=10=]';
    result[uto - from] = '[=10=]';
    return result;
}

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

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] = '[=11=]';
  }
  return result;
}