Как отличить константную строку от char * в макросе C - PullRequest
8 голосов
/ 10 ноября 2011

Я хочу написать макрос для записи строки, используя оптимизацию во время компиляции, чтобы узнать длину строкового литерала.Но мне нужно обнаружить неправильное использование, используя указатели.

Вот что я имею в виду:

void transmit(const char *data, int length);
#define tx_string(x) transmit((x), sizeof(x) -1)
void func(char *badmsg) {
  tx_string("GO");    // ok
  tx_string(badmsg);  // not OK
}

При втором вызове размер будет бессмысленным (размер указателя).

Я хочу выдать ошибку во время компиляции, если я попытаюсь использовать tx_string для чего угодно, кроме строкового литерала.Это использует gcc;Есть ли какая-нибудь gcc вещь, которую я могу использовать для этого?

Редактировать: я работаю с буферами данных, которые могут содержать нули или не заканчиваться нулями.Я ДЕЙСТВИТЕЛЬНО хочу запретить использование указателей для этого и предотвратить использование strlen() во время выполнения.

Edit 2:

Вот пример, который может вызвать проблемы.Я придумаю воображаемый протокол, в котором я должен сообщить 16-разрядному микроконтроллеру адрес, используя команду GO, за которой следует адрес как 16-разрядный необработанный (два 8-разрядных символа), и я хочу перейти от адреса 0.

#define GOSEQ "GO\000\000"

void func(void) {
  char *bad = GOSEQ;
  tx_string(GOSEQ); // ok, sends 4 bytes: GO and two zero bytes
  tx_string(bad);   // bad, using runtime strlen sends two characters "GO"
}

Я уверен, что для этого должна быть какая-то встроенная проверка gcc.Я вижу исходные коды ядра Linux, использующие подобные трюки во время компиляции, но не могу быстро взять в руки конкретный.

Пока идея «программиста Windows» выглядит хорошо, ноболее значимая ошибка времени компиляции была бы бонусом.

Ответы [ 3 ]

9 голосов
/ 10 ноября 2011

В общем, поскольку вы не можете использовать конкатенацию строк с указателями и т. Д., Возможно, вы можете использовать:

#define STRLIT(x)  x ""

Если аргумент STRLIT не является строковым литералом, вы получите ошибку компиляции.

Адаптация общего к вашему конкретному макросу:

#define tx_string(x) transmit((x ""), sizeof(x) - 1)
4 голосов
/ 10 ноября 2011

Вы можете сделать это:

#define tx_string(x) transmit(x, strlen(x)) // No need for extra parentheses

GCC оптимизирует вызов strlen, и я уверен, что другие компиляторы тоже.Не тратьте свое время на эти вещи.

Тестовый файл:

#include <string.h>
void transmit(const char *, size_t);
#define tx_string(x) transmit(x, strlen(x))
void func(void)
{
    tx_string("abcdef");
}

Итоговая сборка:

Iурезанный шум из сборки, это важная вещь.Обратите внимание, что он никогда не звонит strlen, а просто использует номер 6:

.LC0:
    .string "abcdef"

func:
    movl    $6, %esi
    movl    $.LC0, %edi
    jmp     transmit
1 голос
/ 10 ноября 2011
#define tx_string(x) ("i came, i concatenated, i discarded " x, transmit((x), sizeof(x) - 1))

Не совсем идеально, потому что какой-то джокер может вызвать tx_string (+1)

...