#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 раз (приблизительно). Поэтому решите, под какую архитектуру вы пишете и какой размер слова вам нужен.
Комментариев нет:
Отправить комментарий