реклама на сайте
подробности

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> Union c внешними (external) переменными, подскажите, как сделать
Dmitro25
сообщение Apr 14 2010, 12:09
Сообщение #1


Участник
*

Группа: Свой
Сообщений: 60
Регистрация: 4-04-07
Пользователь №: 26 770



Здравствуйте.
Кратко опишу проблему:
В программе есть два модуля: один низкоуровневый - "lowlevel.c" и основной модуль "main.c". В модуле "lowlevel.c" объявлен некий буфер
Код
char buffer[10];

и модуль умеет с ним работать, например, посылать данные из этого буфера на внешнее устройство. Этому модулю нет дела до того, что означает каждый байт этого буфера, его дело - передать данные.
На самом деле каждый байт из этого буфера имеет особый смысл: заголовок, управляющие поля, данные, контрольная сумма.
Хочется, чтобы основной модуль мог работать с этим буфером как со структурой, т.е. что-то вроде того:
Код
  union {            
    char buffer[10];
    struct {
      char NT;
      char mask;
      char event;
      char blok;
      char echo;
      char data[3];
      int crc;
    };
  } LO_TRBuf;

Однако, всё осложняется тем, что элементы структуры описаны с модуле "main.c", а буфер находится в "lowlevel.c". Пробовал использовать директиву "extern" в разных местах, но добиться желаемого не удалось. Можно, конечно перенести описание структуры в "lowlevel.h", но это будет неправильно с точки зрения инкапсуляции данных.
Посоветуйте какое-либо решение?
Go to the top of the page
 
+Quote Post
MrYuran
сообщение Apr 14 2010, 12:15
Сообщение #2


Беспросветный оптимист
******

Группа: Свой
Сообщений: 4 640
Регистрация: 26-12-07
Из: Н.Новгород
Пользователь №: 33 646



Цитата(Dmitro25 @ Apr 14 2010, 16:24) *
Однако, всё осложняется тем, что элементы структуры описаны с модуле "main.c", а буфер находится в "lowlevel.c".

А нельзя структуру описать в main.h?
И вставляйте потом куда угодно

extern-ом вы даёте ссылку на экземпляр объекта, а не на его описание


--------------------
Программирование делится на системное и бессистемное. ©Моё :)
— а для кого-то БГ — это Bill Gilbert =)
Go to the top of the page
 
+Quote Post
Dmitro25
сообщение Apr 14 2010, 12:18
Сообщение #3


Участник
*

Группа: Свой
Сообщений: 60
Регистрация: 4-04-07
Пользователь №: 26 770



2MrYuran:
Она у меня и так описана в main.c. А подключать к lowlevel.c модуль main.c не вижу смысла - зачем ему данные более высокого уровня?
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Apr 14 2010, 12:30
Сообщение #4


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



Работать с указателями.
Код
typedef struct
{
    char NT;
    char mask;
    char event;
    char blok;
    char echo;
    char data[3];
    int crc;
} mystruct;
extern char *buffer;
void test()
{
    mystruct *pData = (mystruct *)buffer;
    pData->NT = 12;
    pData->echo = 'A';
}
Если процессор требует выровненного доступа (ARM, MSP) - при объявлении структуры указать, что структура упакована - #pragma pack, __attribute__((packed))


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
Dmitro25
сообщение Apr 14 2010, 12:46
Сообщение #5


Участник
*

Группа: Свой
Сообщений: 60
Регистрация: 4-04-07
Пользователь №: 26 770



2Сергей Борщ
Спасибо за ответ. Я уже думал о таком решении. Но при таком способе снижается производительность и возрастает объём кода, поскольку при статическом размещении переменных компилятор мог ещё на этапе компиляции вычислить адрес члена структуры, а теперь каждый раз он будет вычислять место расположения переменной в памяти, прибавляя смещение к базовому адресу.
И вроде бы ничего фантастического я не хочу. Просто нужно, чтобы в модуле создался union с данными, объявленными в другом модуле, но как-то не получается. Если пишу
Код
union {            
    extern char buffer[10];
    struct {
      char NT;
      ...

то компилятор выдаёт "a storage class may not be specified here".
Go to the top of the page
 
+Quote Post
Палыч
сообщение Apr 14 2010, 13:00
Сообщение #6


Гуру
******

Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954



Зачем в union тащить какой-то extern? Нужно в модуле (файле) lowlevel заменить char buffer[10]; на union {...} LO_TRBuf; и обращение соответственно на LO_TRBuf.buffer
А, вот в main нужно extern union {...} LO_TRBuf; и обращения типа LO_TRBuf.NT
Сам же union {...} удобно переопределить с помощью typedef...
Go to the top of the page
 
+Quote Post
baralgin
сообщение Apr 14 2010, 13:06
Сообщение #7


Частый гость
**

Группа: Участник
Сообщений: 92
Регистрация: 23-12-08
Из: Кишинёв
Пользователь №: 42 680



Или так: разместить объект структуры(не массив) в lowlevel и через экстерн иметь к нему доступ из main. А юзать сами данные в lowlevel уже как душе угодно. Типа того
Код
//lowlevel.c
struct mystruct name;
static uint8_t* buffer = (uint8_t*)&name;

и не забывать про sizeof(mystruct), во избежании.
Go to the top of the page
 
+Quote Post
zltigo
сообщение Apr 14 2010, 13:11
Сообщение #8


Гуру
******

Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244



Цитата(Dmitro25 @ Apr 14 2010, 15:01) *
Но при таком способе снижается производительность и возрастает объём кода, поскольку при статическом размещении переменных компилятор мог ещё на этапе компиляции вычислить адрес члена структуры

Например, адрес будет 32бит и на каждый элемент структуры в памяти будет еще храниться 4 байта адреса - вот уже "экономия" smile.gif памяти налицо.
Цитата
, а теперь каждый раз он будет вычислять место расположения переменной в памяти, прибавляя смещение к базовому адресу.

Ага, именно так - поместит в регистр базовый адрес и будет быстренько добавлять смещение хранящееся в теле команды. Вместо того, что-бы для типа "экономии" smile.gif уже производительности грузить из памяти данных в этот-же регистр по 32bit значения.

Как Вам такая трактовка?


Цитата(Dmitro25 @ Apr 14 2010, 15:01) *
то компилятор выдаёт "a storage class may not be specified here".

Ну так пойдите ему на встречу. Опишите структуру отдельно.


--------------------
Feci, quod potui, faciant meliora potentes
Go to the top of the page
 
+Quote Post
Dmitro25
сообщение Apr 14 2010, 13:52
Сообщение #9


Участник
*

Группа: Свой
Сообщений: 60
Регистрация: 4-04-07
Пользователь №: 26 770



2Палыч
2baralgin
Я просто для инкапсуляции хотел, чтобы модуль lowlevel ничего не знал об интерпретации полей своего буфера. Ему для работы это не нужно, достаточно знать начало буфера и его размер. Если завтра будет создан другой проект с новым протоколом, то модуль lowlevel можно будет без изменений перенести в новый проект. Переписан будет только модуль main. А с такими перекрёстными ссылками получается, что "все знают понемножку обо всех". На мой взгляд это неправильно.

2zltigo
Я забыл упомянуть, что пишу для AVR, компилятор IAR, а насчёт выровненности структур, если понадобится в будущем, можно по совету "Сергей Борщ" использовать упакованные структуры. Что касается кода, который генерируется компилятором, я уже наблюдал, что компилятор, когда это ему выгодно, генерирует инструкции с индексной адресацией, а в других случаях - используется прямую адресацию. Использование же только указателей лишает его второй возможности.

Сообщение отредактировал Dmitro25 - Apr 14 2010, 13:53
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Apr 14 2010, 13:56
Сообщение #10


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Цитата(Dmitro25 @ Apr 14 2010, 17:07) *
я уже наблюдал, что компилятор, когда это ему выгодно, генерирует инструкции с индексной адресацией, а в других случаях - используется прямую адресацию.

Врядли это мистика, скорее - упрощение константных выражений smile.gif
Go to the top of the page
 
+Quote Post
baralgin
сообщение Apr 14 2010, 14:09
Сообщение #11


Частый гость
**

Группа: Участник
Сообщений: 92
Регистрация: 23-12-08
Из: Кишинёв
Пользователь №: 42 680



Dmitro25 Пишем в lowlevel функцию типа "void* getBuffer(void)", в main полученный указатель кастуем в структуру и используем. Чтобы не было оверхеда заводим статический указатель на структуру, которую инициализируем после входа в main. А далее в программе уже используем этот указатель.

ps: всё равно это всё неправильно и небезопасно, особенно вкупе с такими умными словами как "инкапсуляция" smile.gif, имхо конечно.

Сообщение отредактировал baralgin - Apr 14 2010, 14:10
Go to the top of the page
 
+Quote Post
Палыч
сообщение Apr 14 2010, 15:15
Сообщение #12


Гуру
******

Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954



Цитата(Dmitro25 @ Apr 14 2010, 17:07) *
Я просто для инкапсуляции хотел, чтобы модуль lowlevel ничего не знал об интерпретации полей своего буфера.

Ну если так хочется, то можно сделать тупо
Код
extern char buffer[10];

#define NT  (buffer[0])
#define mask (buffer[1])
#define ........
#define data(i) (buffer[5+i])
#define crc (*(int *)(void *)(&buffer[8]))
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Apr 14 2010, 20:10
Сообщение #13


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



Цитата(Dmitro25 @ Apr 14 2010, 15:01) *
Но при таком способе снижается производительность и возрастает объём кода, поскольку при статическом размещении переменных компилятор мог ещё на этапе компиляции вычислить адрес члена структуры, а теперь каждый раз он будет вычислять место расположения переменной в памяти, прибавляя смещение к базовому адресу.
За все надо платить. Можете подойти с другой стороны:
Код
extern void send(void * buffer, uint_fast8_t size);
void test()
{
    mystruct Data;
    Data.NT = 12;
    Data.echo = 'A';
    send(&Data, sizeof(Data));
}


Код
extern void send_driver(void * buffer, uint_fast8_t size);
#define send(object) send_driver(&object, sizeof(object))
void test()
{
    mystruct Data;
    Data.NT = 12;
    Data.echo = 'A';
    send(Data);
}


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
Dmitro25
сообщение Apr 15 2010, 05:56
Сообщение #14


Участник
*

Группа: Свой
Сообщений: 60
Регистрация: 4-04-07
Пользователь №: 26 770



2baralgin
2Сергей Борщ
Спасибо, я, наверное, что-то подобное и сделаю, если по-нормальному не получается. Только я, скорее, склоняюсь к тому, чтобы в самом начале программы из "main" вызвать функцию
Код
char lowlevel_init(char *buffer, char size)

которая, помимо других действий по инициализации модуля, запомнит адрес и размер буфера.

2Палыч
В случае со множеством директив #define потом трудновато менять адреса при изменении структуры.

Сообщение отредактировал Dmitro25 - Apr 15 2010, 05:57
Go to the top of the page
 
+Quote Post
XVR
сообщение Apr 15 2010, 09:37
Сообщение #15


Гуру
******

Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847



Цитата(Сергей Борщ @ Apr 14 2010, 16:45) *
Работать с указателями.
Код
typedef struct
{
    char NT;
...
    int crc;
} mystruct;
extern char *buffer;
void test()
{
    mystruct *pData = (mystruct *)buffer;
    pData->NT = 12;
    pData->echo = 'A';
}
Тут ошибка - должно быть extern char buffer[];
А если сделать так -
Код
typedef struct
{
    char NT;
...
    int crc;
} mystruct;
extern char buffer[];
#define pData ((mystruct*)buffer)
void test()
{
    pData->NT = 12;
    pData->echo = 'A';
}
то и лишних поинтеров и сложений скорее всего не будет (если компилятор достаточно умный)
Go to the top of the page
 
+Quote Post
MrYuran
сообщение Apr 15 2010, 09:38
Сообщение #16


Беспросветный оптимист
******

Группа: Свой
Сообщений: 4 640
Регистрация: 26-12-07
Из: Н.Новгород
Пользователь №: 33 646



Цитата(XVR @ Apr 15 2010, 13:52) *
Тут ошибка - должно быть extern char buffer[];

Без разницы.
Имя массива - уже указатель.


--------------------
Программирование делится на системное и бессистемное. ©Моё :)
— а для кого-то БГ — это Bill Gilbert =)
Go to the top of the page
 
+Quote Post
XVR
сообщение Apr 15 2010, 10:17
Сообщение #17


Гуру
******

Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847



Цитата(MrYuran @ Apr 15 2010, 13:53) *
Без разницы.
Имя массива - уже указатель.
В этом контексте - нет. Результаты будут разные:
extern char* buffer; - означает, что в переменной buffer лежит адрес массива, а она сама является указателем.
extern char buffer[]; - означает, что buffer САМА является массивом, т.е. массив лежит начиная с ее адреса и далее.
Go to the top of the page
 
+Quote Post
MrYuran
сообщение Apr 15 2010, 10:30
Сообщение #18


Беспросветный оптимист
******

Группа: Свой
Сообщений: 4 640
Регистрация: 26-12-07
Из: Н.Новгород
Пользователь №: 33 646



Цитата(XVR @ Apr 15 2010, 14:32) *
extern char* buffer; - означает, что в переменной buffer лежит адрес массива, а она сама является указателем.
extern char buffer[]; - означает, что buffer САМА является массивом, т.е. массив лежит начиная с ее адреса и далее.

Ничего это не означает.
Можно написать
char* buffer;
а потом buffer[i] выберет нужный элемент массива
buffer[] - это фактически указатель на первый (нулевой) элемент


--------------------
Программирование делится на системное и бессистемное. ©Моё :)
— а для кого-то БГ — это Bill Gilbert =)
Go to the top of the page
 
+Quote Post
XVR
сообщение Apr 15 2010, 11:11
Сообщение #19


Гуру
******

Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847



Цитата(MrYuran @ Apr 15 2010, 14:45) *
Ничего это не означает.
Означает.
Цитата
Можно написать
char* buffer;
а потом buffer[i] выберет нужный элемент массива
buffer[] - это фактически указатель на первый (нулевой) элемент
Можно, но не для extern:
Пример для неверующих:
t1.cpp
Код
#include <stdio.h>

char* s1="abc";
char s2[]="abc";

void t();

int main()
{
printf("s1: %p %s\n",s1,s1);
printf("s2: %p\n",s2);
printf("    %s\n",s2);
t();
return 0;
}


t2.cpp
Код
#include <stdio.h>

extern char* s1;
extern char* s2;

void t()
{
printf("e s1: %p %s\n",s1,s1);
printf("e s2: %p\n",s2);
printf("      %s\n",s2);
}

Сборка: g++ t1.cpp t2.cpp
Запуск:
Цитата
s1: 0x4006ec abc
s2: 0x500a68
abc
e s1: 0x4006ec abc
e s2: 0x636261
Segmentation fault
Обратите внимание на выделенные строки, а особенно на указатель со значением 0x636261 (в ASCII это будет cba, ничего не напоминает?)
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Apr 15 2010, 11:16
Сообщение #20


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



Цитата(XVR @ Apr 15 2010, 12:32) *
В этом контексте - нет. Результаты будут разные:
Да, попутал.


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
Dmitro25
сообщение Apr 16 2010, 04:32
Сообщение #21


Участник
*

Группа: Свой
Сообщений: 60
Регистрация: 4-04-07
Пользователь №: 26 770



XVR
Спасибо за подсказку.
Ваш вариант с преобразованием типов отлично работает. Компилятор оказался "достаточно умный": одиночные обращения к полям структуры он преобразует в команды с непосредственной адресацией, а если обращений к разным полям сосредоточено много в одном месте программы - используется индексная адресация.
То, что было нужно.
Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 23rd July 2025 - 23:36
Рейтинг@Mail.ru


Страница сгенерированна за 0.01603 секунд с 7
ELECTRONIX ©2004-2016