|
|
  |
Ламерской вопрос, массив unsigned char |
|
|
|
Feb 8 2007, 11:26
|

Частый гость
 
Группа: Свой
Сообщений: 75
Регистрация: 1-02-07
Из: Украина
Пользователь №: 24 940

|
Цитата(SergeyDDD @ Feb 8 2007, 12:22)  все зависит от контроллера памяти и самой памяти где будет массив Ну например для AT91SAM7S64, если разместить во внутреннем ОЗУ?
|
|
|
|
|
Feb 8 2007, 11:57
|
Местный
  
Группа: Свой
Сообщений: 231
Регистрация: 7-12-06
Из: Киев
Пользователь №: 23 248

|
Цитата(Fortune @ Feb 8 2007, 12:26)  Цитата(SergeyDDD @ Feb 8 2007, 12:22)  все зависит от контроллера памяти и самой памяти где будет массив
Ну например для AT91SAM7S64, если разместить во внутреннем ОЗУ? Как правило внутреннее ОЗУ поддерживают операции с байтами А вообще это справочная информация. См. документацию. А еще проще сходу попробовать на железе
|
|
|
|
|
Feb 8 2007, 12:15
|
Местный
  
Группа: Свой
Сообщений: 231
Регистрация: 7-12-06
Из: Киев
Пользователь №: 23 248

|
Цитата(DASM @ Feb 8 2007, 13:00)  по-моему ерунду говорите. Массив всегда будет расположен ЛИНЕЙНО. Точка. И один char в массиве всегда занимает один байт PS "по-моему" добавлено только для смягчения фразы, но никак не по причине наличия сомнений ;-) Почему же ерунду? Проблема может возникнуть с железом, а не с компилятором. Для примера возьмем EP7312: ширина шины данных управляется линиями HALFWORD и WORD (думаю назначение понятно). Для 32 разрядной шины используются линии адреса начиная с A2. Так вот в такой конфигурации записать байт можно только по адресам 0, 4, 8, 12, 16... А вот с SDRAM памятью и внутренней (в EP7312) такой проблемы нет.
|
|
|
|
|
Feb 8 2007, 14:56
|

Знающий
   
Группа: Свой
Сообщений: 648
Регистрация: 11-02-06
Из: Санкт-Петербург
Пользователь №: 14 237

|
Цитата(SpiritDance @ Feb 8 2007, 13:19)  Цитата(DASM @ Feb 8 2007, 12:37)  Значит компилер будет считывать 32-битное значения и выдирать сам байиы,
Тут уж, наверно, не компилер, а сам процессор. Вы оба неправы, господа. Этим занимается аппаратно контроллер памяти (неважно какой, SDRAM, SRAM, DDR) - все эти дела совершенно прозрачны для программиста и для процессора, исполняющего команды программиста. Обратите внимание - несмотря на то, что разрядность шины данных процессора увеличивается (от 8 битных 8088, далее через 16 битные 8086/80286, через 32 битные 80386/Pentium-ы, а теперь есть и 64 битные ...) - ПО КАЖДОМУ АДРЕСУ ПАМЯТИ ЛЕЖИТ РОВНО ОДИН БАЙТ !!! С точки зрения любого процессора (и программиста) физическая память является линейной структурой байтов, адрес каждого байта уникален и отличается от адреса предыдущего и последующего ровно на единицу. 32 разрядный процессор "хочет" загрузить себе в регистр один байт из адреса 0x123456FF, и его совершенно не волнует, каким образом внешняя память "исполнит его желание". Более того, проц не обязан "знать", что за память расположена по этому адресу. Дальнейшие действия целиком и полностью возложены на контроллер памяти, который всё это "знает". Если память 32 разрядная - будет выполнено ДВА цикла чтения - один по адресу 0x123456FC, другой по адресу 0x12345700, а оставшиеся два младших бита в исходном адресе будут определять, как расположить считанные 8 байт на 32 разрядной шине так, чтобы процесоор "почувствовал", будто он и вправду считал двойное слово с адреса 0x123456FF. ДВА цикла чтения заменятся на ОДИН цикл в одном случае - если адрес кратен 4. Поэтому у программиста (который, в отличие от процессора, знает свою архитектуру аппаратной части) есть выбор - указать компилятору, как предпочтительнее располагать данные в памяти (align). Хочется сэкономить память - указывай align=1, но тогда при доступе к 3 элементам из 4 будешь "попадать" по времени на ДВА цикла чтения; если памяти море - указывай align=4 (вроде как это по умолчанию), тогда каждый объект массива char будет лежать "как король" в адресах, кратных 4, чтобы не тратить лишнее время на доступ к нему. Если контроллер памяти очень "умный" - он может сильно ускорять процесс считывания, например, вести кэш, и недавно считанное значение не считывать заново, или при доступе по определённым адресам на чтение/запись генерировать исключения и т. п. Часть этих функций современные процессоры типа Pentium взяли себе на борт (кэш первого уровня, защита памяти и генерация исключений и т п), часть оставили снаружи (кэш второго уровня, разделение RAM/ROM).
--------------------
Сделано в Китае. Упаковано в России.
|
|
|
|
|
Feb 8 2007, 15:34
|

Знающий
   
Группа: Свой
Сообщений: 648
Регистрация: 11-02-06
Из: Санкт-Петербург
Пользователь №: 14 237

|
Цитата(DASM @ Feb 8 2007, 15:02)  ну я собсно и сказал - по барабану кто будет делать, главное что программисту пофик. Зачем забивать себе голову лишними знаниями? А вот это кто сказал ? Цитата(DASM @ Feb 8 2007, 12:37)  Массив в любом случае по адресам будет лежать слитно, иначе вся арифметика указателей летит к чертовой матери вместе с Керниганом и этим.. как его.. Ритчи Массив char в физической памяти будет лежать так, как на это указывает #pragma pack, то бишь выравнивание, если 4, то каждый элемент массива будет расположен с адреса, кратного 4, то есть занимать 4 байта. И Керниган с Ритчи тут ни при чем. И арифметика указателей в порядке. Если Вы прибавите к указателю единицу, прибавка к адресу в машинном коде составит много больше - это зависит от размера элемента, на который указывает этот указатель и от выравнивания. Это уже работа компилятора. А вот преобразование некратных адресов в адреса памяти (то, о чем я писал в пред. посте) - это работа контроллера памяти. Это разные и совершенно не связанные вещи.
--------------------
Сделано в Китае. Упаковано в России.
|
|
|
|
|
Feb 8 2007, 15:51
|
Гуру
     
Группа: Свой
Сообщений: 3 644
Регистрация: 28-05-05
Пользователь №: 5 493

|
"Массив char в физической памяти будет лежать так, как на это указывает #pragma pack, то бишь выравнивание, если 4, то каждый элемент массива будет расположен с адреса, кратного 4, то есть занимать 4 байта." Давайте оставим впокое физическую память, будем говорить про память, адресуемую по скажем [R0] и пусть сама эта память лежит где хочет. Так вот я утверждаю, что при инкременте R0 на единицу мы получаем доступ к следущему элементу массива. И никакие прагмы паки тут совершенно роли не играют. Не так ? pragma pack является директивой выравнивания структур, она не имеет отношения к массивам. По крайней мере в части их размещения
|
|
|
|
|
Feb 8 2007, 16:30
|

Знающий
   
Группа: Свой
Сообщений: 648
Регистрация: 11-02-06
Из: Санкт-Петербург
Пользователь №: 14 237

|
Цитата(DASM @ Feb 8 2007, 15:51)  "Массив char в физической памяти будет лежать так, как на это указывает #pragma pack, то бишь выравнивание, если 4, то каждый элемент массива будет расположен с адреса, кратного 4, то есть занимать 4 байта." Давайте оставим впокое физическую память, будем говорить про память, адресуемую по скажем [R0] и пусть сама эта память лежит где хочет. Так вот я утверждаю, что при инкременте R0 на единицу мы получаем доступ к следущему элементу массива. И никакие прагмы паки тут совершенно роли не играют. Не так ? pragma pack является директивой выравнивания структур, она не имеет отношения к массивам. По крайней мере в части их размещения Зависит от компилятора, размера элемента массива и установленного выравнивания !!! Если мы говорим о массиве char, размер элемента равен 1. Если мы установили выравнивание данных, равное 16, то, чтобы получить доступ к следующему элементу массива типа char, Вам в приведенном Вами примере придется увеличить R0 на 16. В части различия компиляторов -- часть компиляторов применяет #pragma pack только к структурам, при этом для остальных типов у них есть что-то типа #pragma data_align. Это непринципиально. И обсуждалось уже здесь
--------------------
Сделано в Китае. Упаковано в России.
|
|
|
|
|
Feb 8 2007, 17:02
|
Местный
  
Группа: Свой
Сообщений: 359
Регистрация: 9-12-05
Пользователь №: 12 034

|
Цитата(Demeny @ Feb 8 2007, 16:56)  Вы оба неправы, господа. Этим занимается аппаратно контроллер памяти (неважно какой, SDRAM, SRAM, DDR) - все эти дела совершенно прозрачны для программиста и для процессора, исполняющего команды программиста.  ИМХО тут неправы Вы. Для программиста не так уж и всё прозрачно, если конечно он не на бейсике пишет. Цитата Обратите внимание - несмотря на то, что разрядность шины данных процессора увеличивается (от 8 битных 8088, далее через 16 битные 8086/80286, через 32 битные 80386/Pentium-ы, а теперь есть и 64 битные ...) - ПО КАЖДОМУ АДРЕСУ ПАМЯТИ ЛЕЖИТ РОВНО ОДИН БАЙТ !!! На некотором подмножестве из всех имеющихся платформ. Цитата С точки зрения любого процессора (и программиста) физическая память является линейной структурой байтов, адрес каждого байта уникален и отличается от адреса предыдущего и последующего ровно на единицу. Не всегда и не везде. Цитата 32 разрядный процессор "хочет" загрузить себе в регистр один байт из адреса 0x123456FF, и его совершенно не волнует, каким образом внешняя память "исполнит его желание". Более того, проц не обязан "знать", что за память расположена по этому адресу. Дальнейшие действия целиком и полностью возложены на контроллер памяти, который всё это "знает". Если память 32 разрядная - будет выполнено ДВА цикла чтения - один по адресу 0x123456FC, другой по адресу 0x12345700, а оставшиеся два младших бита в исходном адресе будут определять, как расположить считанные 8 байт на 32 разрядной шине так, чтобы процесоор "почувствовал", будто он и вправду считал двойное слово с адреса 0x123456FF. На x86 оно реализовано ради совместимости, на очень многих других платформах этого нет. А есть или исключения, или игнорирование младших битов (как в случае с ARM) К томуже в тех же x86-расширениях (типа MMX/SSE/SSEx) есть команды допускающие только обращение к выравненным данным. Цитата Хочется сэкономить память - указывай align=1, но тогда при доступе к 3 элементам из 4 будешь "попадать" по времени на ДВА цикла чтения; если памяти море - указывай align=4 (вроде как это по умолчанию), тогда каждый объект массива char будет лежать "как король" в адресах, кратных 4, чтобы не тратить лишнее время на доступ к нему. Применительно к char (на х86) чтение будет всегда одно. PS а архитектуры всякие бывают, не надо всех под гребёнку x86 причёсывать.
|
|
|
|
|
Feb 8 2007, 17:09
|

Знающий
   
Группа: Свой
Сообщений: 648
Регистрация: 11-02-06
Из: Санкт-Петербург
Пользователь №: 14 237

|
Цитата(DASM @ Feb 8 2007, 16:39)  И что ? Вы хотите сказать, что вашим шаманством я могу сделать такое - char data[256]; printf ("%d\r\n" (int) &data[1] - (int) &data[0]); и получить на экране 16 ? Сообщите пожалуйста такой удивительный компилятор и я посыплю голову пеплом RVDS на этот счет говорит ясно "There are no packed array types. A packed array is an array of objects of packed type. There is no padding in the array." Ага, я понял. Мы говорим о разных вещах. Вы в приведенном примере путаете УКАЗАТЕЛЬ на элемент и АДРЕС элемента в памяти. Это разные вещи. Операция & возвращает указатель на элемент, и результат будет равен 1. По сути, указатель - это всего лишь ИНДЕКС элемента в сегменте данных программы. Именно поэтому указатель всегда указывает на вполне ОПРЕДЕЛЁННЫЙ тип данных, чтобы компилятор мог правильным образом обслуживать арифметику указателей. Например, Код double * pDouble; double data[100]; pDouble = &(data[45]); pDouble = pDouble + 1; Вас же не удивляет такая операция - указатель увеличился на 1, а указывает уже на 46 элемент массива double (что говорит о том, что адрес сместился на 8). Если Вы отпечатаете pDouble, то увидите, что он действительно увеличился всего на 1. А изначально Ваш пример содержал регистр процессора R0, на уровне машинных кодов регистр косвенной адресации действительно увеличится на число, зависящее от размера элемента и от выравнивания данных. Чтобы не продолжать спор, я готов его завершить, если Вы мне скажете, как я могу разместить массив char data[200000] с адресов, кратных 4, чтобы не делать лишнюю работу контроллеру 32 разрядной памяти при доступе к каждому элементу? Если, по Вашему, невозможно применить выравнивание к массивам ...
--------------------
Сделано в Китае. Упаковано в России.
|
|
|
|
|
Feb 8 2007, 17:12
|
Гуру
     
Группа: Свой
Сообщений: 3 644
Регистрация: 28-05-05
Пользователь №: 5 493

|
мы по прежнему друг друга не понимаем. Не можете. Можно указать что начальный адрес массивы будет с адреса кратного 4, но все char - ы в нем будут идти ПОСЛЕДОВАТЕЛЬНО и адрес последнего элемента будет N + (200000-1) Перефразирую так. Имеем линейную память. 0,1,2,3,4,5,6,7.... Вы можете вашими директивами расположить массив чаров так, что [0] лежал в 0-й ячейке, а [1] в 4-ой ячейке ? Я утверждаю что нет
|
|
|
|
|
Feb 8 2007, 17:23
|

Знающий
   
Группа: Свой
Сообщений: 648
Регистрация: 11-02-06
Из: Санкт-Петербург
Пользователь №: 14 237

|
Цитата(DASM @ Feb 8 2007, 17:12)  мы по прежнему друг друга не понимаем. Не можете. Можно указать что начальный адрес массивы будет с адреса кратного 4, но все char - ы в нем будут идти ПОСЛЕДОВАТЕЛЬНО и адрес последнего элемента будет N + (200000-1) Перефразирую так. Имеем линейную память. 0,1,2,3,4,5,6,7.... Вы можете вашими директивами расположить массив чаров так, что [0] лежал в 0-й ячейке, а [1] в 4-ой ячейке ? Я утверждаю что нет Как же , по Вашему, будут идти элементы массива, если размер каждого элемента равен 13, при установленном выравнивании (не важно какой директивой) 16?
--------------------
Сделано в Китае. Упаковано в России.
|
|
|
|
|
Feb 8 2007, 18:03
|
Участник

Группа: Свой
Сообщений: 60
Регистрация: 3-08-06
Пользователь №: 19 285

|
Цитата(DASM @ Feb 8 2007, 17:25)  через 13 байт. Слитно. Массива ! Будем продолжать спор, я пока что на RVDS и IAR проверил (вот дожил, очевидное проверять), и , естественно убедился, что в массиве структур никакого паддинга между элементами массива не будет А вы смотрели на расположение в ROM или после инициализации в RAM? Насколько я понимаю в RAM с выравниваем 4, паддинг будет.
|
|
|
|
|
Feb 8 2007, 19:22
|
Местный
  
Группа: Свой
Сообщений: 359
Регистрация: 9-12-05
Пользователь №: 12 034

|
По поводу игноригования младших битов адреса в ARM-е я пожалуй погорячился. Всё немного хитрее. хитрый пример (GCC и LPC2292) Код typedef struct __attribute__ ((__packed__)) { uchar uc; uint ui; } test_struct_t;
test_struct_t data[5] = { {'A', 0x00010203}, {'B', 0x04050607}, {'C', 0x08090A0B}, {'D', 0x0C0D0E0F}, {'E', 0x10111213} };
void test() { int n; uint* pui; uchar* puc;
printf("sizeof(test_struct_t)=%d\n", sizeof(test_struct_t)); printf("sizeof(data)=%d\n", sizeof(data)); printf("sizeof(data[0])=%d\n", sizeof(data[0]));
printf("Loop 1\n"); for(n=0; n<5; n++) { printf("%d: %c 0x%08X %p\n", n, data[n].uc, data[n].ui, &data[n].ui); }
printf("Loop 2\n"); for(n=0; n<5; n++) { pui = &data[n].ui; puc = &data[n].uc; printf("%d: %c 0x%08X %p\n", n, *puc, *pui, pui); } } Выдаёт: Код sizeof(test_struct_t)=5 sizeof(data)=25 sizeof(data[0])=5 А теперь угадайте что выдают на печать первый и второй циклы? Одно и тоже? Какбы не так: Код Loop 1 0: A 0x00010203 4000003d 1: B 0x04050607 40000042 2: C 0x08090A0B 40000047 3: D 0x0C0D0E0F 4000004c 4: E 0x10111213 40000051 Loop 2 0: A 0x41010203 4000003d 1: B 0x42000607 40000042 2: C 0x4304050B 40000047 3: D 0x0C0D0E0F 4000004c 4: E 0x45111213 40000051 В первом случае компилятор знает что элемент data[n].ui не (всегда) выровнен на границу слова, поэтому читает слово ЧЕТЫРМЯ!!! инструкциями ldrb, и из этих 4-х байт "собирает" слово. Во втором случае компилятор предполагает что pui - указатель на выровненное слово и читает сразу слово командой ldr. И вот тут вылазит особенности ARMа по работе с невыравненными данными. По сути младшие 2 бита адреса слова используются как количество байт на которое циклически сдвигается прочитанное слово. А с точки зрения голых Сей результат должен быть одинаковый. Т.ч. ИМХО программер должен много чего знать по части платформы, компилятора и т.д.
|
|
|
|
|
Feb 8 2007, 19:30
|
Местный
  
Группа: Свой
Сообщений: 421
Регистрация: 25-12-04
Пользователь №: 1 675

|
Цитата(DASM @ Feb 8 2007, 19:20)  И чего ??? где тут слово array ? ... назовите мне компилятор, который решетом массив разместит При чем тут array? тут даже болише написано: Word alignment (2-byte) When word alignment is on, ..... char and unsigned char variables and fields can be placed at _any_ address
|
|
|
|
|
Feb 8 2007, 19:45
|
Местный
  
Группа: Свой
Сообщений: 359
Регистрация: 9-12-05
Пользователь №: 12 034

|
Цитата(DASM @ Feb 8 2007, 21:25)  "А с точки зрения голых Сей результат должен быть одинаковый." кто такое сказал ? Ссылку плиз А почему нет то? Пойдём от противного! Где написано что разименованный указатель на элемент структуры не обязательно равен этому элементу структуры? да и на x86 или без __attribute__ ((__packed__)) результат вывода циклов будет одинаковый.
|
|
|
|
|
Feb 8 2007, 20:06
|
Местный
  
Группа: Свой
Сообщений: 359
Регистрация: 9-12-05
Пользователь №: 12 034

|
Цитата(DASM @ Feb 8 2007, 21:53)  ладно, возможно тут и промахнулся, и тем не менее к нашим баранам вернемся, массивам то есть Про массивы я более придерживаюсь твоей точки зрения, т.е. выравнивание должно распрастраняться только на первый элемент массива. Но я не буду столь категоричен, ибо ....
|
|
|
|
|
Feb 8 2007, 22:18
|
Частый гость
 
Группа: Свой
Сообщений: 169
Регистрация: 10-11-05
Из: Воронеж
Пользователь №: 10 687

|
DASM полностью прав!!! НЕ МОЖЕТ компилятор положить массив в памяти с дырками! Это противоречит всем законам языка. Я имею полное право сделать так: Код uchar test[10]; void test { char *ptr;
//Классический вариант: ptr = test + 5; //А теперь так: ptr = (uchar*)((void*)test + 5 * sizeof(uchar)); //И вот так: ptr = (uchar*)((int)test + 5 * sizeof(uchar)); Кто покажет мне (и DASM'у) хоть один компилятор, у которого эти три варианта будут не одинаковы??? Хочу сказать отдельное спасибо Alex03 за правильное предупреждение, т.к. доступ через указатель к переменной, не выровненной по умолчанию на архитектуре АРМ может работать некорректно. Я с этим еще не сталкивался, но словив такой глюк, можно долго проблемы разгребать
|
|
|
|
|
Feb 9 2007, 01:36
|
Местный
  
Группа: Свой
Сообщений: 421
Регистрация: 25-12-04
Пользователь №: 1 675

|
Цитата(DASM @ Feb 8 2007, 19:38)  ну а вот теперь сделайте из этих variables ARRAY - .... не надо .... уходить от темы Как раз не ухожу, а наоборот: если уж при "Word alignment" компилятор будет "char variables and fields can be placed at _any_ address", то о каких дырах в массиве может идти речь. (но это конкретный компилятор, допускаю, что есть специфические, может для DSP каких, где компилятору "удасться" сделать дыры, но я таких не знаю).
|
|
|
|
|
Feb 9 2007, 07:05
|
Местный
  
Группа: Свой
Сообщений: 359
Регистрация: 9-12-05
Пользователь №: 12 034

|
Цитата(gladov @ Feb 9 2007, 00:18)  Хочу сказать отдельное спасибо Alex03 за правильное предупреждение, т.к. доступ через указатель к переменной, не выровненной по умолчанию на архитектуре АРМ может работать некорректно. Я с этим еще не сталкивался, но словив такой глюк, можно долго проблемы разгребать  Угу,  Мне вот интересно что выдаёт первый цикл из моего примера на других компиляторах? Кому не лень попробуйте на всяких IAR/Keil/RVDS? Только упаковку (например pragm pack(1) вместо __attribute__ ((__packed__))) надо подправить чтобы sizeof(test_struct_t)=5 было. Интуиция мне говорит что возможно не все компиллеры такие разборчивые в этом плане как GCC.
|
|
|
|
|
Feb 9 2007, 10:42
|
Частый гость
 
Группа: Свой
Сообщений: 169
Регистрация: 10-11-05
Из: Воронеж
Пользователь №: 10 687

|
Цитата(Alex03 @ Feb 9 2007, 07:05)  Мне вот интересно что выдаёт первый цикл из моего примера на других компиляторах? Кому не лень попробуйте на всяких IAR/Keil/RVDS? Только упаковку (например pragm pack(1) вместо __attribute__ ((__packed__))) надо подправить чтобы sizeof(test_struct_t)=5 было. Интуиция мне говорит что возможно не все компиллеры такие разборчивые в этом плане как GCC.  Проверил на ИАР 4.40а. На строки Код printf("%d: %c 0x%08X %p\n", n, data[n].uc, data[n].ui, &data[n].ui); pui = &data[n].ui; ругается так: Warning[Pa039]: use of address of unaligned structure member При работе, первый цикл проходит как положено, а во втором из printf вываливается в обработчик ABORT DATA. Почему тогда в первом случае работает я не понимаю
|
|
|
|
|
Feb 9 2007, 12:49
|
Местный
  
Группа: Свой
Сообщений: 359
Регистрация: 9-12-05
Пользователь №: 12 034

|
Цитата(gladov @ Feb 9 2007, 12:42)  ругается так: Warning[Pa039]: use of address of unaligned structure member При работе, первый цикл проходит как положено, а во втором из printf вываливается в обработчик ABORT DATA. Почему тогда в первом случае работает я не понимаю  Вооо. Какой процик, я так понимаю всяко не LPC.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|