Страницы

Поиск по вопросам

воскресенье, 15 марта 2020 г.

Функция memset_word делает пропуск между словами

#c #указатели #память #memory


Пока писал учебную ОС, пришлось в качестве одной из функций стандартной библиотеки
написать функцию memset_word. Проблема была тут же решена "в лоб":

    void memset_word(uint16_t* mem, uint16_t value, size_t count) {
        uint16_t* addr;
        for(addr = mem; addr < mem + count * sizeof(uint16_t);
                addr += sizeof(uint16_t)) {
            *addr = value;
        }
    }


Но у этой функции было обнаружено неприятное свойство: она записывает слова через
одно. Другими словами, память была такая:

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00,

а после вызова

    memset((uint16_t*) 0, 0xCDAB, 3)


стала такая:

AB CD 00 00 AB CD 00 00 AB CD 00 00 00 00 00 00 00 00 00 00,

хотя должна была стать такой:

AB CD AB CD AB CD 00 00 00 00 00 00 00 00 00 00 00 00 00 00.

В принципе, помогает замена addr += sizeof(uint16_t) на addr++, но я не понимаю:
ведь указатели в C - настоящие адреса, так почему же для того, чтобы перейти к следующему
слову, нужно прибавлять 1, а не размер слова? Или же ошибка кроется где-то в коде функции?
Прошу объяснить мне это.
    


Ответы

Ответ 1



У вас эта функция void memset_word(uint16_t* mem, uint16_t value, size_t count) { uint16_t* addr; for(addr = mem; addr < mem + count * sizeof(uint16_t); addr += sizeof(uint16_t)) { *addr = value; } } некорректная. В ней неправильно используется арифметика указателей. Я думаю, вы имели в виду следующее void memset_word(uint16_t* mem, uint16_t value, size_t count) { for(uint16_t *addr = mem; addr != mem + count; ++addr ) *addr = value; } } Вы должны перейти к следующему объекту. Увеличение указателя на 1 увеличивает его адрес на sizeof( uint16_t ). В этом состоит принцип работы оператора индексирования, когда вы пишите, например, mem[1] что эквивалентно выражению *(mem + 1) и в виду коммутативности операции сложения вы можете также записать 1[mem]

Ответ 2



Я бы сказал не так. Арифметика указателей верная, и Ваша функция работала бы правильно, если бы архитектура машины была 16-разрядная. В 32-разрядных машинах размер слова равен 4 байтам. При этом все числа (кроме char), размер которых меньше 4 байт, аппаратно выравниваются по 4-байтному слову. Другими словами, нельзя (не рекомендуется) разместить два 16-разрядных слова в одном 4-байтном аппаратном слове. В случае, если вы запишете подряд два 16-разрядных слова, процессору придётся осуществлять больше операций, чтобы извлечь его из оперативной памяти и записать обратно. Нужно извлечь 4 байта (1 инструкция), далее в зависимости от того, младшее это слово или старшее (сравнение, условный переход - 2 инструкции) осуществляется наложение маски 0xFFFF0000 или 0x0000FFFF соответственно (2 инструкции). Если это старшее слово, то осуществляется сдвиг на 16 бит (1 инструкция). Все эти инструкции осуществляются аппаратно. Итого извлечение 16-разрядного полуслова из 32-разрядного слова тяжелее в 6 раз (приблизительно). Поэтому решите, под какую архитектуру вы пишете и какой размер слова вам нужен.

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

Отправить комментарий