почему while (* s ++ == * t ++) не работает для сравнения двух строк - PullRequest
4 голосов
/ 18 апреля 2020

Я реализую strcmp(char *s, char *t), который возвращает <0, если <code>s<t, 0, если s==t, и> 0, если s>t, сравнивая значение кулака, которое отличается между двумя строками.

реализация с разделением постфиксного приращения и операторов реляционного равенства работает:

for (; *s==*t; s++, t++)
    if (*s=='\0')
        return 0;
return *s - *t;

однако группировка инкрементов постфикса и операторов реляционного равенства не работает (например, так):

while (*s++ == *t++)
    if (*s=='\0')
        return 0;
return *s - *t;

Последний всегда возвращает 0. Я думал, что это может быть потому, что мы слишком быстро увеличиваем указатели, но даже с разницей в две строки, встречающиеся с индексом 5 из 10, все равно дает тот же результат.

Пример input: strcomp("hello world", "hello xorld");

возвращаемое значение: 0

Я догадываюсь, что это из-за приоритета оператора, но я не уверен, и если это так, я не могу точно определить, почему.

Спасибо за ваше время!

Ответы [ 4 ]

7 голосов
/ 18 апреля 2020

Поскольку в for l oop приращение (s++, t++ в вашем случае) равно , а не , вызываемое, если условие (*s==*t в вашем случае) ложно. Но в вашем while l oop приращение также вызывается в этом случае, поэтому для strcomp("hello world", "hello xorld") оба указателя в конце указывают на o s в строках.

4 голосов
/ 18 апреля 2020

Так как вы всегда увеличиваете s и t в тесте, вы должны обратиться к s[-1] для завершения в случае одинаковых строк и s[-1] и t[-1] в случае их различия.

Также обратите внимание, что порядок сравнения определяется как unsigned char.

Вот модифицированная версия:

int strcmp(const char *s, const char *t) {
    while (*s++ == *t++) {
        if (s[-1] == '\0')
            return 0;
    }
    return (unsigned char)s[-1] - (unsigned char)t[-1];
}

После комментариев от LL chux Здесь приведена полностью соответствующая реализация для порочных архитектур с представлением дополнения, отличным от двух, и / или CHAR_MAX > INT_MAX:

int strcmp(const char *s0, const char *t0) {
    const unsigned char *s = (const unsigned char *)s0;
    const unsigned char *t = (const unsigned char *)t0;

    while (*s++ == *t++) {
        if (s[-1] == '\0')
            return 0;
    }
    return (s[-1] > t[-1]) - (s[-1] < t[-1]);
}
3 голосов
/ 19 апреля 2020

Для полноты в дополнение к тому, что уже было хорошо сказано о неправильном смещении во время вычитания:

*s - *t; неверно, когда *s, *t отрицательно.

Стандартная библиотека C указывает, что строковые функции сравнивают , как если бы char было unsigned char. Таким образом, код, который вычитает через char *, дает неправильный ответ, когда символы являются отрицательными.

Для всех функций в этом подпункте каждый символ должен интерпретироваться так, как если бы он имел тип unsigned char ( и, следовательно, каждое возможное представление объекта является действительным и имеет другое значение). C17dr § 7.24.1 3

int strcmp(const char *s, const char *t) {
  const unsigned char *us = (const unsigned char *) s;
  const unsigned char *ut = (const unsigned char *) t;
  while (*us == *ut && *us) {
    us++;
    ut++;
  }
  return (*us > *ut) - (*us < *ut);
}

Этот код также разрешает неясные проблемы доступа не-2 к дополнению -0 и диапазон char, превышающий int.

3 голосов
/ 18 апреля 2020

Каждый дает правильный совет, но все еще непросто встроить эти операторы приращения в выражение сравнения и выполнить странную задачу на 1 штуку.

Следующее выглядит проще и легче для чтения. Ни один указатель никогда не увеличивается или уменьшается на неверный адрес.

while ((*s == *t) && *s)
{
    s++;
    t++;
}
return *s - *t;
...