#arrays #c #string #pointers #iar
#массивы #c #строка #указатели #iar
Вопрос:
Мне нужна небольшая помощь с массивами и указателями, если это возможно…
Я определил несколько постоянных строк:
static const Int8U item1[] = {0x4D, 0x61, 0x72, 0x74, 0x65, 0x6C, 0x20, 0x4D, 0x43, 0x50, 0x37, 0x38, 0x31, 0x30, 0x00};
static const Int8U item2[] = {0x4D, 0x61, 0x74, 0x65, 0x6C, 0x20, 0x4D, 0x43, 0x50, 0x37, 0x38, 0x31, 0x30, 0x00};
static const Int8U item3[] = {0x4D, 0x61, 0x65, 0x6C, 0x20, 0x4D, 0x43, 0x50, 0x37, 0x38, 0x31, 0x30, 0x00};
Я ссылаюсь на эти строки в массиве указателей:
const Int8U* original_sequence[] =
{
item1,
item2,
item3
}
#define SEQUENCE_ELEMENT_1 (0)
#define SEQUENCE_ELEMENT_2 (1)
#define SEQUENCE_ELEMENT_3 (2)
Я могу ссылаться на любую из строк с помощью чего-то похожего на:
original_sequence[SEQUENCE_ELEMENT1]
Пока все хорошо. Однако существует другая конфигурация, которая состоит из тех же элементов, только в другом порядке. В идеале я хотел бы сделать следующее, объявленное с глобальной областью видимости:
const CHAR *new_sequence[3] =
{
original_sequence[SEQUENCE_ELEMENT_2],
original_sequence[SEQUENCE_ELEMENT_1],
original_sequence[SEQUENCE_ELEMENT_3]
};
Однако я получаю сообщение об ошибке «выражение должно иметь постоянное значение». Пожалуйста, обратите внимание, что я не могу напрямую ссылаться на ‘item1’ и т. Д., Потому что #defines являются ключевыми в идентификации.
Я также пробовал такие варианты, как:
const CHAR **new_sequence[3] =
{
(char**)original_sequence[SEQUENCE_ELEMENT_2].
(char**)original_sequence[SEQUENCE_ELEMENT_1].
(char**)original_sequence[SEQUENCE_ELEMENT_3]
};
Кажется, ничто меня никуда не приводит. Я не могу понять, путаю ли я себя с указателями или нарушаю ограничение компилятора? То, что заставляет меня думать, что это не ограничение, заключается в том, что если бы я ссылался на строки ‘item1’ непосредственно в этом новом массиве, я не думаю, что возникла бы проблема.
Спасибо,
Роб
Комментарии:
1. У вас по какой-то причине
.
вместо,
в нескольких местах. Должны лиarray_one
иarray1
должны быть одним и тем же?2. Спасибо за ваш ответ, теперь я переименовал некоторые элементы, чтобы, надеюсь, сделать его более понятным.
Ответ №1:
К сожалению, то, что вы пытаетесь сделать, не разрешено на стандартном языке C.
В проекте C17 объясняется, что вы не можете использовать значение объекта в постоянном выражении адреса:
6.6 Константные выражения
…
- Адресная константа — это нулевой указатель, указатель на значение lvalue, обозначающее объект со статической продолжительностью хранения, или указатель на обозначение функции; он должен быть создан явно с использованием унарного
amp;
оператора или целочисленной константы, приведенной к типу указателя, или неявно с использованием выражения типа array или function. Операторы array-subscript[]
и member-access.
and->
, унарные операторы addressamp;
и indirection*
, а также приведения указателей могут использоваться при создании адресной константы, но доступ к значению объекта не должен осуществляться с помощью этих операторов.
Ваш вариант почти работает, но вам нужно добавить адрес оператора:
const CHAR **new_sequence[3] =
{
(char**)amp;original_sequence[SEQUENCE_ELEMENT_2],
(char**)amp;original_sequence[SEQUENCE_ELEMENT_1],
(char**)amp;original_sequence[SEQUENCE_ELEMENT_3]
};
Однако у него есть недостаток, заключающийся в том, что вам требуется дополнительное разыменование при использовании:
*new_sequence[index]
Я бы просто поместил константы SEQUENCE_ELEMENT_* в обычный целочисленный массив и использовал функцию, чтобы скрыть детали доступа, и вызывал эту функцию всякий раз, когда требуется элемент new_sequence:
uint8_t new_sequence[] = {
SEQUENCE_ELEMENT_2,
SEQUENCE_ELEMENT_1,
SEQUENCE_ELEMENT_3
};
char * get_new_sequence_text(int index) {
return (char*)original_sequence[new_sequence[index]];
}
Ответ №2:
Сообщение об ошибке уже сообщает вам, что не так. Элементы инициализации должны быть постоянными значениями, чего не происходит, когда вы извлекаете значения из другой переменной / массива. См. Также Инициализация
При инициализации объекта со статическим или локальным поточным хранилищем, каждое выражение в инициализаторе должно быть постоянным выражением или строковым литералом.
Таким образом, единственный способ инициализировать его — использовать постоянные значения, например
const Int8U *new_sequence[3] =
{
item2,
item1,
item3
};
или сделайте это внутри функции
void func()
{
const Int8U *new_sequence[3] =
{
original_sequence[SEQUENCE_ELEMENT_2],
original_sequence[SEQUENCE_ELEMENT_1],
original_sequence[SEQUENCE_ELEMENT_3]
};
}
или сделайте это динамически в коде
new_sequence[0] = original_sequence[SEQUENCE_ELEMENT_2];
new_sequence[1] = original_sequence[SEQUENCE_ELEMENT_1];
new_sequence[2] = original_sequence[SEQUENCE_ELEMENT_3];
или есть какая-то другая косвенная схема
int new_sequence_index[] =
{
SEQUENCE_ELEMENT_2,
SEQUENCE_ELEMENT_1,
SEQUENCE_ELEMENT_3
};
и выполняйте доступ через original_sequence[new_sequence_index[0]]
и т. Д.
Ответ №3:
Практически, есть две проблемы:
- Ваши наборы данных имеют разную длину, что означает, что вы должны использовать указатели и размеры вместо того, чтобы просто создавать большой многомерный массив всего (что было бы проще и эффективнее).
- Вы не можете ссылаться на вещи, которые не являются постоянными выражениями, из списка инициализаторов объекта со статической продолжительностью хранения («глобальный» и т.д.). Можно использовать адреса других переменных, но не значения этих переменных.
Чтобы обойти это, теоретически вы могли бы создать несколько неприглядных таблиц «указатель на указатель», но синтаксис быстро станет беспорядочным. Менее неясной альтернативой может быть создание одного единого набора данных, а затем создание таблиц индексов вместо таблиц указателей.
Например, вы могли бы сделать это:
#define ITEM1 0x4D, 0x61, 0x72, 0x74, 0x65, 0x6C, 0x20, 0x4D, 0x43, 0x50, 0x37, 0x38, 0x31, 0x30, 0x00
#define ITEM2 0x4D, 0x61, 0x74, 0x65, 0x6C, 0x20, 0x4D, 0x43, 0x50, 0x37, 0x38, 0x31, 0x30, 0x00
#define ITEM3 0x4D, 0x61, 0x65, 0x6C, 0x20, 0x4D, 0x43, 0x50, 0x37, 0x38, 0x31, 0x30, 0x00
static const uint8_t* const data[3] =
{
(const uint8_t[]) { ITEM1 },
(const uint8_t[]) { ITEM2 },
(const uint8_t[]) { ITEM3 },
};
(Я избавился от неприглядных самодельных типов в пользу стандартного C stdint.h, как и все должны.)
Это создает таблицу указателей, доступную только для чтения, которая указывает на 3 доступных только для чтения массива. Каждый массив получает ту же область видимости, что и сама таблица указателей. Вы могли бы сделать это, чтобы создать несколько таблиц с разным порядком, но это создает много служебных данных. Поэтому вместо этого, возможно, рассмотрите этот подход:
enum
{
SEQUENCE_ELEMENT_1,
SEQUENCE_ELEMENT_2,
SEQUENCE_ELEMENT_3,
};
static const size_t original_sequence[3] =
{
SEQUENCE_ELEMENT_1,
SEQUENCE_ELEMENT_2,
SEQUENCE_ELEMENT_3,
};
static const size_t new_sequence[3] =
{
SEQUENCE_ELEMENT_2,
SEQUENCE_ELEMENT_1,
SEQUENCE_ELEMENT_3,
};
Теперь у вас будет один набор данных, к которому вы можете получить доступ следующим образом:
for(size_t i=0; i<3; i )
{
for(size_t j=0; i<something; j )
{
uint8_t a = data[ original_sequence[i] ][j];
...
}
}
Однако недостающая часть головоломки заключается в том, где хранятся индивидуальные размеры каждой коллекции элементов. Я предполагаю, что это строки, и в этом случае something
выше указана длина строки, которая может быть предварительно вычислена во время компиляции.