Проблема с шестнадцатеричным литералом в сравнении строк

Я читаю в файле ROM NES, где первые четыре байта являются "\ x4e \ x45 \ x53 \x1a" или NES \x1a. В моем реальном коде данный файл может быть произвольным, поэтому я хочу проверить, чтобы этот заголовок был здесь. Тем не менее, я столкнулся с некоторыми проблемами, которые демонстрирует следующий код:

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

int main()
{
    FILE *fp;
    fp = fopen("mario.nes", "rb");

    char nes[4];
    char real_nes[4] = "NES\x1a";
    fread(nes, 4, 1, fp);
    printf("A: %x\n", nes[3]);
    printf("B: %x\n", real_nes[3]);
    printf("C: %s\n", nes);
    printf("D: %s\n", real_nes);
    if (strcmp(nes, real_nes) != 0) {
        printf("not a match\n");
    }
    fclose(fp);
    return 0;
}

который возвращает:

A: 1a
B: 1a
C: NES?
D: NES
not a match

где вопросительный знак \x1a.

Я новичок в C, так что возможно я упускаю что-то неуловимое (или очевидное) о том, почему две строки не совпадают, и почему вопросительный знак не отображается при печати строки D, чтобы показать, что \x1a там, в конце строки, строка B, кажется, указывает, что это должно быть.

5 ответов

Решение

Основная проблема в вашем коде:

char real_nes[4] = "NES\x1a";

Это не строка, так как она не заканчивается символом nul-terminator ('\0'). Это та же проблема для "нет".

Просто объявите их как:

char real_nes[] = "NES\x1a"; /* this is a string, ended by '\0' */
char nes[sizeof real_nes];

Чтобы быть уверенным, что есть место для '\0'.

Теперь вы можете использовать спецификатор%s или strcmp(). В любом случае, я рекомендую вместо этого использовать strncmp(), как в:

if(0 != strncmp(real_nes, nes, sizeof real_nes)) { /* some stuff */ }

НТН.

Некоторые замечания и предложения:

  • открывайте файлы в двоичном режиме - в противном случае в системах, отличных от POSIX, могут происходить забавные вещи (исправлено)

    fp = fopen("mario.nes", "rb");
    
  • завершите нулевые буферы, если вы хотите распечатать или сравнить их или использовать такие функции, как strncmp() которые принимают длину строки в качестве дополнительного аргумента

    printf("C: %.4s\n", nes);
    printf("D: %.4s\n", real_nes);
    if (strncmp(nes, real_nes, 4) != 0) {
    
  • '\x1a' является неграфическим символом замены ^Z

  • проверьте возвращаемые значения функций io на наличие ошибок

Ну, одна проблема - это использование strcmp. Эта функция ожидает строку с нулевым разрешением (ни nes, ни real_nes не являются строкой с нулевым символом в вашем коде).

Еще одна проблема - фред. Используйте это так:

fread(nes, 1, 4, fp); // first size_t param is size and second is member count

Измените свой код следующим образом:

int main()
{
        FILE *fp;
        fp = fopen("mario.nes", "rb");

        char nes[5];
        char real_nes[5] = "NES\x1a";
        fread(nes, 1, 4, fp);
        nes[4] = '\0';
        printf("A: %x\n", nes[3]);
        printf("B: %x\n", real_nes[3]);
        printf("C: %s\n", nes);
        printf("D: %s\n", real_nes);
        if (strcmp(nes, real_nes) != 0) {
            printf("not a match\n");
        }
        fclose(fp);
        return 0;
}

И посмотри, работает ли это.

Не используйте строковые функции в байтовых массивах с нулевым символом в конце.

Проблема в том, что у вас есть два 4-байтовых массива, которые должны содержать строку "NES\x1a" (для '\0' не осталось места, поскольку она уже имеет длину 4 байта), но формат%s и strcmp нуждаются в '\ 0 конец в конце, чтобы знать конец строки. Вот почему это не работает правильно.

1.: Не используйте printf с форматом%s в этом байтовом массиве. 2.: Используйте memcmp для сравнения байтов.

Попробуйте это вместо этого:

int i;

printf("Read bytes: 0x");
for(i = 0; i < sizeof(nes); i ++)
  printf("%02X", nes[i]);
printf("\n");

if (memcmp(nes, real_nes, sizeof(nes)) != 0) {
  printf("not a match\n");
}

Возможно, немного уже поздно, но вот как я это делаю:

 // Read the 16 byte iNES header
    char header[16];
    fread( header, 16, 1, file );

    // Search for the "NES^Z" signature
    if( memcmp( header, "NES\x1A", 4 ) )
    {

Как предложил Ксено, с memcmp вам нет дела до нулевых терминаторов. В конце концов, вы на самом деле не используете строки, а скорее как массивы символов, что не совпадает с нулевыми терминаторами. Поскольку на самом деле вам не нужно печатать подпись, кроме как для отладки, вам совершенно не нужно использовать строковые функции.

Другие вопросы по тегам