Когда можно сравнивать строки C по равенству char *? - PullRequest
0 голосов
/ 26 июня 2018

Я знаю, что для двух произвольных c-строк (const char *) не имеет смысла сравнивать их (a == b).

Но я думаю, что когда оба определены одним и тем же строковым литералом, это допустимо.

Например, здесь:

#include <stddef.h>

const char * const meals[] = {
    "none",
    "breakfast",
    "lunch",
    "dinner"
};

#define NO_MEALS  meals[0]
#define BREAKFAST meals[1]
#define LUNCH     meals[2]
#define DINNER    meals[3]

// i hours after midnight, hour_to_meals_map[floor(i)] is being served.
const char * hour_to_meal_map[] = {
    NO_MEALS,
    NO_MEALS,
    NO_MEALS,
    NO_MEALS,
    NO_MEALS,
    BREAKFAST, // i = 5
    BREAKFAST,
    BREAKFAST,
    BREAKFAST,
    BREAKFAST,
    BREAKFAST,
    LUNCH, // i = 11
    LUNCH,
    LUNCH,
    LUNCH,
    LUNCH,
    LUNCH,
    DINNER, // i = 17
    DINNER,
    DINNER,
    DINNER,
    DINNER,
    DINNER,
    DINNER // i = 23
};

// Returns a boolean for whether the two hours have the same meal being eaten.
int same_meal(size_t hour_one, size_t hour_two) {
    return hour_to_meal_map[hour_one] == hour_to_meal_map[hour_two];
}

(Что касается того, почему вы должны hour_to_meal_map отображать в строки, а не в индексы, никто не знает ... но я работаю над проектомэто настроено таким образом.)

Правильно ли я понимаю, что это допустимо здесь, и что важно то, что есть только одна точка, в которой каждое значение записывается как литерал?(#define NO_MEALS "none" было намеренно исключено !!)

Если этот код находится в заголовочном файле, это не имеет значения, не так ли?(Я ожидаю, что стандарт требует, чтобы meals имел одинаковые значения в каждой единице компиляции?).

Я нахожу множество вопросов от начинающих, спрашивающих о случаях, когда совершенно очевидно, что они должны использовать strcmp, но я не могу найти тот, который отвечает на этот конкретный случай.Буду признателен за любую помощь, особенно если вы можете указать мне правую часть стандарта C, чтобы я мог быть действительно уверен, что я понимаю все тонкости.

Ответы [ 2 ]

0 голосов
/ 26 июня 2018

Сравнение двух строк одного типа с == или != всегда допустимо.Это подробно описано в разделе 6.5.9 стандарта C , в котором подробно описываются операторы равенства:

2 Должно выполняться одно из следующего:

  • оба операнда имеют арифметический тип;
  • оба операнда являются указателями на квалифицированные или неквалифицированные версии совместимых типов;
  • один операнд является указателем на тип объекта, а другой -указатель на квалифицированную или неквалифицированную версию void или
  • один операнд является указателем, а другой - константой нулевого указателя.

...

4 Два указателя сравниваются равными тогда и только тогда, когда оба являются нулевыми указателями, оба являются указателями на один и тот же объект (включая указатель на объект и подобъект в его начале) или функция , оба являются указателями на один за последним элементом одного и того же объекта массива, или один - указатель на один за концом одного объекта массива, а другой - указатель на начало другого объекта массиваэто происходит тo сразу же следовать первому объекту массива в адресном пространстве

В этом случае у вас есть массив указателей, и вы присваиваете значение одного из этих указателей в другом массиве.Поэтому, если вы сравниваете два указателя, и они оба содержат значение (например) meals[0], например, адрес строковой константы «none», они гарантированно будут сравниваться равными.out for, если данная строковая константа используется в нескольких местах.В этом случае они не обязательно совпадают.

Например, учитывая это:

const char *s1 = "test";
const char *s2 = "test";

Значения s1 и s2 не гарантируются одинаковымипоскольку две строковые константы могут отличаться друг от друга, хотя компиляторы могут сделать их одинаковыми.Это отличается от:

const char *s1 = "test";
const char *s2 = s1;

Где s1 и s2 будут одинаковыми, и это отражает ваш случай.

Как вы упомянули, это будетболее разумно, чтобы hour_to_meal_map содержал числовые константы (предпочтительно члены enum), и чтобы эти константы впоследствии отображались в массив строк.Но указатели на строковые константы фактически являются именно этим.

0 голосов
/ 26 июня 2018

Я могу вспомнить несколько случаев, когда char * равенство имеет смысл:

  1. случай, который вы дали: путем копирования из того же указателя
  2. для большинства (всех?)компиляторы: с любыми строковыми литералами одинакового значения в одной и той же единице перевода.Это чрезвычайно распространенная оптимизация, и ее можно легко протестировать в любом случае.
  3. , если вы явно пропустите строку через функцию intern()
  4. в качестве быстрого сравнения короткого замыкания перед выполнением дорогостоящей проверкизначения
...