Размер времени компиляции строкового литерала в массиве строковых литералов

Я хочу найти длину литерала C-строки во время компиляции. Учитывая определения:

static const char * const   header_left[] =
{
    "         |          |  Raw  |  Raw  |",
    "         |          | Start |  End  |",
    "Interval#| Duration | Point | Point |",
    "---------+----------+-------+-------+",
};
const unsigned int  rows_in_header = sizeof(header_left) / sizeof(header_left[0]);

Как найти длину строкового литерала header_left[2] без использования strlen?

В этом вопросе Определение длины строкового литерала есть комментарий для объявления массива как header_left[][4]. Я предпочитаю не использовать такое объявление, так как существует тенденция к изменению количества строк без изменения константы количества. Мне нравится, когда компилятор вычисляет количество строк (см. определение rows_in_header) и длину каждой строки.

Это для встроенной системы, и строки записываются блоками в последовательный порт. Функция последовательного порта принимает указатель на данные и длину данных в качестве параметров. Код последовательного порта оптимизирован для блочной записи. Предпочтительно не использовать strlen, потому что это тратит впустую время производительности.

Я использую C99 с IAR Embedded Workshop на платформе ARM7TDMI.
Я включил тег c++, потому что это также касается C++, и мы будем переносить код на C++ после первого запуска продукта.


person Thomas Matthews    schedule 01.05.2013    source источник
comment
То есть вы вообще не можете изменить определение? Например. магия макросов или магия С++..   -  person dyp    schedule 01.05.2013
comment
Для static const char * const header_left[] длина не имеет значения, так как это массив указателей. Возможно, вы хотели static const char header_left[][sizeof "Interval#| Duration | Point | Point |"] = { ... };?   -  person Daniel Fischer    schedule 01.05.2013
comment
@DanielFischer: Нужно ли мне дублировать текст в вашем случае, один раз для sizeof и другое время между { и }?   -  person Thomas Matthews    schedule 01.05.2013
comment
@DyP: у меня были проблемы с отладкой, вызванные тем, что емкость массива не соответствовала количеству элементов, объявленных в массиве. Например, я могу объявить массив как [][64], а каждый элемент будет [][32]. Это оставит 32 слота неопределенных значений. Или 32 нулевых символа, отправленных на последовательный порт.   -  person Thomas Matthews    schedule 01.05.2013
comment
Да, вы можете использовать для этого макрос, чтобы избежать дублирования.   -  person Daniel Fischer    schedule 01.05.2013


Ответы (4)


На самом деле совет в связанном ответе неверен. так как индексы перевернуты. Объявление должно быть больше похоже на это:

static const char header_left[][40] =
{
    "         |          |  Raw  |  Raw  |",
    "         |          | Start |  End  |",
    "Interval#| Duration | Point | Point |",
    "---------+----------+-------+-------+",
};

Самый левый индекс по-прежнему может быть предоставлен компилятором и указывает количество строк. Сами строки должны быть фиксированным массивом символов, для которого вы можете указать верхнюю границу (40 в этом примере). Вы получите ошибку компиляции, если какая-либо строка превышает эту длину (включая нулевой терминатор). Потенциальным недостатком для ваших целей является потраченное впустую пространство.

В любом случае компилятор не может вывести за вас оба размера — размер обоих массивов — и зубчатые массивы не поддерживаются в C++.

person Scott Jones    schedule 01.05.2013
comment
Если каждый элемент в массиве имеет фиксированную длину, считается ли он по-прежнему зубчатым массивом? - person Thomas Matthews; 01.05.2013
comment
Нет — зубчатый массив — это массив массивов с вложенными массивами переменного размера. В С++ у вас действительно есть только одномерные массивы, элементы которых должны быть фиксированного размера (время компиляции). Они могут быть вложены друг в друга, чтобы обеспечить аппроксимацию многомерных массивов. Но только самый значимый (самый внешний) массив может иметь свой размер. - person Scott Jones; 01.05.2013

Класс stringref может справиться с этим, если вы хотите. Это кажется проще, чем большинство других ответов, и обрабатывает ситуации, когда строки имеют разную длину:

struct stringref {
    //this is for convenience, but isn't used in this sample
    stringref(const char* p, size_t l=0) :ptr(p), len(l?l:strlen(p)) {}
    //construct from string literals
    template<size_t l> stringref(const char(&p)[l]) :ptr(p), len(l) {}
    //convert to const char*
    operator const char*() const {return ptr;}
    const char* get() const {return ptr;}
    //retrieve the length
    size_t length() const {return len;}
private:
    const char* ptr;
    size_t len;
};

stringref header_left[] =
{
    "         |          |  Raw  |  Raw  |   ",
    "         |          | Start |  End  | ",
    "Interval#| Duration | Point | Point |      ",
    "---------+----------+-------+-------+",
};

int main()
{
    const char* ptr = header_left[0]; //conversion possible
    printf("%d\n", header_left[0].length());
    printf("%d\n", header_left[1].length());
    printf("%d\n", header_left[2].length());
    printf("%d\n", header_left[3].length());
}

http://coliru.stacked-crooked.com/view?id=e244267379f84e21409db9ec39da5765-50d9cfc8a1d350e7409e81e87c2653ba

person Mooing Duck    schedule 01.05.2013

Магия макросов! (>= C99) Требуется как минимум 2 строки.

Примечание. Здесь я использую не char const*, а char const[...], т. е. массивы, потому что это возможно и гарантирует, что все строки имеют одинаковую длину.

Изменить: вычесть -1 из length_of_row, чтобы избавиться от '\0'.

#include<cstddef>
#define CREATE_HEADER(X, ...) \
  static const size_t length_of_row = sizeof(X)/sizeof(X[0]) - 1; \
  static const char header_left[][length_of_row+1] = { X, __VA_ARGS__ }; \
  static const size_t rows_in_header = sizeof(header_left) / sizeof(header_left[0]); \

CREATE_HEADER(
    "         |          |  Raw  |  Raw  |",
    "         |          | Start |  End  |",
    "Interval#| Duration | Point | Point |",
    "---------+----------+-------+-------+",
);

  • sizeof(X)/sizeof(X[0]) дает длину первой строки (строки)
  • static const char header_left[][length_of_row] — неограниченный массив массивов из length_of_row символов; то же, что typedef char const row[length_of_row]; row header_left[] = {...};
person dyp    schedule 01.05.2013
comment
coliru.stacked-crooked.com/ работает лучше, чем я ожидал. - person Mooing Duck; 01.05.2013
comment
@MooingDuck То есть -1 для \0 я полагаю? - person dyp; 01.05.2013
comment
Нет, я думал, что ваш ответ был неправильным, поэтому закодировал его, и он оказался правильным. Поэтому я разместил код для будущих сомневающихся. - person Mooing Duck; 01.05.2013
comment
@MooingDuck Хорошо :) тем не менее, я вычел -1 и изменил имя переменной, column на самом деле вводит в заблуждение, поскольку это таблица. - person dyp; 01.05.2013

Вы можете извлечь строку header_left[2] и использовать sizeof(...) - 1

static const char header_left_2[] = "Interval#| Duration | Point | Point |";
static const char * const   header_left[] =
{
    "         |          |  Raw  |  Raw  |",
    "         |          | Start |  End  |",
    header_left_2,
    "---------+----------+-------+-------+",
};
const unsigned int  rows_in_header = sizeof(header_left) / sizeof(header_left[0]);
const size_t header_left_2_len = sizeof(header_left_2) - 1;
person Olaf Dietsche    schedule 01.05.2013