Как включить 4-символьную строку в C?

#c #string #macros #switch-statement

#c #строка #макросы #switch-инструкция

Вопрос:

Мне нужно переключаться на основе 4-символьной строки. Я помещаю строку в объединение, чтобы я мог, по крайней мере, ссылаться на нее как на 32-разрядное целое число.

 union
{
    int32u  integer;
    char    string[4];
}software_version;
  

Но теперь я не знаю, что писать в операторах case. Мне нужен какой-нибудь макрос для преобразования 4-символьного строкового литерала в целое число. Например,

 #define STRING_TO_INTEGER(s)    ?? What goes here ??
#define VERSION_2_3_7           STRING_TO_INTEGER("0237")
#define VERSION_2_4_1           STRING_TO_INTEGER("0241")

switch (array[i].software_version.integer)
{
    case VERSION_2_3_7:
        break;

    case VERSION_2_4_1:
        break;
}
  

Есть ли способ создать макрос STRING_TO_INTEGER(). Или есть лучший способ справиться с переключением?

Комментарии:

1. Вы знаете, что это невозможно перенести через границы конечного байта? Возможно, было бы лучшей идеей просто определить целое число версии, которое увеличивается или вычисляется из числовой версии при запуске программы.

2. Я не думаю, что вы можете перейти из string -> number во время компиляции.

3. Почему вы настаиваете на преобразовании ваших VERSION констант из строк с помощью макроса? Вы могли бы просто… вы знаете… помещайте целые числа напрямую (например, #define VERSION_2_3_7 237 )…

4. @SethCarnegie Можно использовать sscanf для разбора целых чисел внутри строк.

5. Также обратите внимание, что если вы придерживаетесь представления version в виде массива символов и вам нужно 4 символа, вы должны определить строковый элемент структуры так, чтобы он принимал 5 символов, один для NULL.

Ответ №1:

Переносимый пример кода:

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

#define CHARS_TO_U32(c1, c2, c3, c4) (((uint32_t)(uint8_t)(c1) | 
    (uint32_t)(uint8_t)(c2) << 8 | (uint32_t)(uint8_t)(c3) << 16 | 
    (uint32_t)(uint8_t)(c4) << 24))

static inline uint32_t string_to_u32(const char *string)
{
    assert(strlen(string) >= 4);
    return CHARS_TO_U32(string[0], string[1], string[2], string[3]);
}

#define VERSION_2_3_7 CHARS_TO_U32('0', '2', '3', '7')
#define VERSION_2_4_1 CHARS_TO_U32('0', '2', '4', '1')

int main(int argc, char *argv[])
{
    assert(argc == 2);
    switch(string_to_u32(argv[1]))
    {
        case VERSION_2_3_7:
        case VERSION_2_4_1:
        puts("supported version");
        return 0;

        default:
        puts("unsupported version");
        return 1;
    }
}
  

Код предполагает только существование целочисленных типов uint8_t и uint32_t и не зависит от ширины и знаковости типа char , а также от порядкового номера. Она свободна от коллизий, пока кодировка символов использует только значения в диапазоне uint8_t .

Ответ №2:

Вы включаете четырехсимвольные коды следующим образом

 switch (fourcc) {
case 'FROB':
}
  

Обратите внимание на разницу: "XXXX" это строка, 'XXXX' это символьный / целочисленный литерал.

Однако я бы предложил вам вместо этого использовать отдельные номера версий, например:

 struct Version {
    int major, minor, patch;
};

bool smaller (Version lhs, Version rhs) {
    if (lhs.major < rhs.major) return true;
    if (lhs.major > rhs.major) return false;
    if (lhs.minor < rhs.minor) return true;
    if (lhs.minor > rhs.minor) return false;
    if (lhs.patch < rhs.patch) return true;
    if (lhs.patch > rhs.patch) return false; // redundant, for readabiltiy

    return false; // equal
}
  

Комментарии:

1. вы неправильно интерпретируете стандарт. Доступ к другому элементу, отличному от того, в который был записан последним, возможен только в том случае, если значение является представлением-ловушкой для другого типа. int32u (Я полагаю, это то же самое, что uint32_t ) не имеет представлений trap.

2. Я не понимаю, как это помогает моему оператору switch.

3. @Rocketmagnet: Вы можете переключать только целочисленные типы, переключение на строки / массивы символов не определено.

4. @Jens: Я удалю эту фразу. Спасибо за подсказку.

5. @phresnel Да, я знаю это, как очевидно из моего вопроса. Вот почему я хочу автоматический способ преобразования строки в целое число.

Ответ №3:

Обновлено:

 #define VERSION_2_3_7 '0237'
  

Комментарии:

1. Ему нужно перейти от строки к числу, а не от числа к строке.

2. Вопрос в обратном направлении.

3. @SethCarnegie, смотри комментарий вверху

4. Люди, прекратите голосовать против этого. На самом деле это правильный ответ.

5. @Rocketmagnet, этот ответ некорректен, не используйте его таким образом. Символьные константы, которые содержат более одного символа, как предложенные '0237' , зависят от компилятора. И, кроме того, у вас нет причин использовать такой cruft, если вас действительно устраивают только отдельные определения. Просто определите ее как длинное десятичное число без знака, например 237UL .

Ответ №4:

 int versionstring_to_int(char * str)
{
char temp [ 1  sizeof software_version.string ]; /* typically 1 4 */

if (!str || ! *str) return -1;
memcpy (temp, str, sizeof temp -1);
temp [sizeof temp -1] = 0;

return atoi (temp );
}
  

Редактировать:

 #define STRING_TO_INTEGER(s)    versionstring_to_int(s)
#define VERSION_2_3_7           237
#define VERSION_2_4_1           241

switch ( STRING_TO_INTEGER( array[i].software_version.string) )
{
case VERSION_2_3_7:
    break;

case VERSION_2_4_1:
    break;
}
  

Комментарии:

1. Нет, это не работает, потому что это нельзя использовать как в инструкции case.

2. Да, это возможно, просто используйте числовые идентификаторы в падежах. Предполагается, что они являются литералами и тщательно сопоставляются программистом с соответствующим блоком кода. Зачем кому-то нужна строка в качестве метки регистра, когда число так же читаемо и менее подвержено ошибкам?

3. Что, если строка содержала буквы? Например, «ABC1» -> 0x41424331

4. Ну, вам пришлось бы ее разобрать, не так ли? И, похоже, не имеет особого смысла менять схему именования / нумерации в период кризиса среднего возраста проекта. Схемы, подобные этим, должны быть стабильными.

5. суть в том, чтобы переинтерпретировать 4×8 бит строки как 32-разрядное целое число. Не разбирать строку. Любая 4-символьная строка, включая «0_?Q», может быть повторно представлена как целое число. Однако только числовые строки могут быть преобразованы в целое число.