Вопрос
В моей встроенной программе на языке Си у меня есть структура:
struct var{
unsigned long value;
unsigned long length;
+ More
}
Массив этих структур используется для хранения переменных.Большинство сохраненных переменных просто сохраняются в 'value', и поэтому длина устанавливается равной 1.
Однако некоторые из этих переменных являются массивами, и я пытаюсь сохранить начальный адрес в 'value'.
unsigned long lookup[10];
variables[x].length = 10;
Тогда я не совсем уверен, как сохранить адрес...
variables[x].value = lookup;
// lookup is a pointer so I cant put it into value
или
variables[x].value = (unsigned long)lookup;
// value reads back through sprintf+uart as '536874692'
// which couldnt be a valid memory address!
Я мог бы просто сдаться и добавить переменную-указатель в структуру
Редактировать:
Я хотел избежать добавления указателя в структуру, потому что мне пришлось бы вернуться назад и переписать функции чтения / записи flash, чтобы также сохранить указатель.Они довольно сложные и в настоящее время работают, поэтому я бы предпочел их не трогать!
Решение
Ты мог бы сохраните адрес в value
путем приведения его к unsigned long
, как вы демонстрируете.(Я думаю, что ценность, которую вы получаете является действительный адрес...в шестнадцатеричном формате это получается как 0x20000EC4...похоже, сегмент данных вашей встроенной системы начинается с 0x20000000, да?)
Однако приведение указателей к целым числам (или длинным числам) никогда не бывает "чистым".Почему бы не добавить
unsigned long *starting_address;
к вашей структуре?Тогда вы сможете избежать приведений типов.Если вы обеспокоены тем, что для этого потребуется больше памяти, вы можете использовать объединение, в котором хранится либо unsigned long *starting_address
или "длинное значение без знака`, которое все еще чище, чем приведение.
Другие советы
Более чистым вариантом было бы использовать союз.
struct var{
union {
unsigned long numeric;
void *ptr;
} value;
unsigned long length;
+ More
}
При желании вы могли бы также включить перечисление типов, как это часто делается с элементами, использующими объединение.
Предполагая, что размер системного указателя такой же, как у unsigned long
(никаких гарантий, проверьте это с sizeof
) вы бы использовали актерский состав.Но это грязно и чревато ошибками.Рассмотрите возможность использования вместо этого объединения.
struct var{
unsigned short type;
union {
unsigned long i;
void *p;
} value;
unsigned long length;
+ More
}
где вы храните тип в type
.
Я не вижу недостатка в том, чтобы помещать указатель в вашу структуру.Вы хотите, чтобы память была в виде непрерывного блока?Вы могли бы просто сделать
struct var
{
unsigned long *value;
unsigned long length;
unsigned long othervalue1;
unsigned long othervalue2;
};
Но не забудьте выделить память для значения отдельно, используя malloc.Или, если вы хотите использовать фиксированный объем пространства
unsigned long value[50];
Что произойдет, если вы выполните поиск sprintf + uart.Какую ценность это дает вам?
Если не было задействовано приведение или расширение знака, фактические данные те же.Просто sprintf по-другому интерпретирует это.
Ваше решение просто использовать указатель является хорошим.Если вы действительно хотели использовать int, вам следует использовать intptr_t , который гарантированно будет достаточно большим, чтобы вместить указатель.
Почему бы не сделать значение массивом длиной в один элемент, когда длина равна 1?Вы все равно не будете использовать больше места.
Я не уверен, почему вы не определяете значение как unsigned long* .
Вместо этого сделайте что-то вроде:
struct var{
union{
unsigned long solo_value;
unsigned long* multi_values;
}
unsigned long length;
+ More
};
Кроме того, правила выравнивания не применяются так строго * в стеке, как в куче.Хотя, если вы используете какой-либо новейший компилятор, они должны быть в любом случае...вы, вероятно, что-то разбиваете с помощью sprintf.Извлеките значение с помощью отладчика или чего-то более надежного.
*Технически malloc() и др.эл.применяйте правила выравнивания в 8 байт, а не язык.
Ваш лучший выбор - использовать перечисление, как уже было сказано:
typedef struct var{
union {
unsigned long numeric;
void *ptr;
} value;
unsigned long length;
// + More
} composition_t;
затем используйте длина участник для определения типа данных:
composition_t array[2];
unsigned long lookup[10];
// Just a plain unsigned long value
array[0].value.numeric = 100;
array[0].length= 1;
// An array of 10 long values
array[0].value.ptr= lookup;
array[x].length = 10;
Имейте в виду, что беззнаковый long не гарантирует, что он будет содержать указатель, хотя это вероятно во встроенных системах.Сделайте стандартную вещь и используйте объединение, как предлагают другие ответы.Объединение будет делать Правильные вещи в любой реализации, соответствующей стандарту, а не только в той, которую вы используете прямо сейчас.Кроме того, у него есть преимущество в том, что он говорит то, что вы имеете в виду, что всегда ценно, когда с этим приходится иметь дело кому-то, кто не сразу грокнет код (возможно, вам, год спустя).
Знаете ли вы о offsetof?http://en.wikipedia.org/wiki/Offsetof
Я не уверен, что вы на самом деле пытаетесь сделать, но сохранение адреса одного элемента структуры в одной структуре кажется ненужным.
Существует также макрос container_of (погуглите его), который создан с использованием offsetof и который также может быть полезен.