Указатель на буквенное значение

Предположим, что у меня есть константа, определенная в файле заголовка

#define THIS_CONST 'A'

Я хочу записать эту константу в поток. Я делаю что-то вроде:

char c = THIS_CONST;
write(fd, &c, sizeof(c))

Однако для краткости и ясности я хотел бы сделать следующее:

write(fd, &THIS_CONST, sizeof(char)); // error
                                      // lvalue required as unary ‘&’ operand

Кто-нибудь знает какой-либо макро/другой трюк для получения указателя на литерал? Я хотел бы что-то, что можно использовать следующим образом:

write(fd, PTR_TO(THIS_CONST), sizeof(char))

Примечание. Я понимаю, что могу объявить свои константы как статические константные переменные, но тогда я не могу использовать их в операторах switch/case. то есть.

static const char THIS_CONST = 'A'
...
switch(c) {
  case THIS_CONST: // error - case label does not reduce to an integer constant
    ...
}

Если существует способ использования константной переменной в метке case?

Ответы

Ответ 1

Невозможно сделать это прямо на C89. Для создания такого выражения вам придется использовать набор макросов.

В C99 разрешено объявлять литералы struct-or-union, а инициализаторы для скаляров могут быть написаны с использованием аналогичного синтаксиса. Следовательно, есть один способ добиться желаемого эффекта:

#include <stdio.h>

void f(const int *i) {
    printf("%i\n", *i);
}

int main(void) {
    f(&(int){1});
    return 0;
}

Ответ 2

Единственный способ получить указатель - поставить литерал в переменную (ваш первый пример кода). Затем вы можете использовать переменную с write() и литералом в switch.

Ответ 3

C просто не позволяет адресовать буквенные символы, например "A". Для того, что стоит, тип символьных литералов в C есть int (char в С++, но этот вопрос помечен C). "A" будет иметь определенное значение реализации (например, 65 в системах ASCII). Взятие адреса значения не имеет никакого смысла и не представляется возможным.

Теперь, конечно, вы можете взять адрес других видов литералов, таких как строковые литералы, например, следующее:

write(fd, "potato", sizeof "potato");

Это потому, что строковый литерал "картофель" представляет собой массив, а его значение является указателем на "p" в начале.

Чтобы уточнить/уточнить, вы можете принимать только адреса объектов. т.е. оператор and (address-of) требует объекта, а не значения.

И чтобы ответить на другой вопрос, который я пропустил, C не допускает нестационарные метки case, и это включает переменные, объявленные как const.

Ответ 4

Поскольку вызов write() для записи одного символа в дескриптор файла почти наверняка является убийцей производительности, вы, вероятно, захотите просто сделать fputc (THIS_CONST, stream).

Ответ 5

#define THIS_CONST 'a'

Это просто макрос. Компилятор в основном просто вставляет 'a' везде, где вы используете THIS_CONST. Вы можете попробовать:

const char THIS_CONST = 'a';

Но я подозреваю, что не будет работать в увядке (не нужно использовать c-компилятор, чтобы попробовать, и прошло уже несколько лет с тех пор, как я написал c-код).

Ответ 6

просто используйте строковую константу, которая является указателем на символ, а затем записывает только 1 байт:

#define MY_CONST_STRING "A"

write(fd, MY_CONST_STRING, 1);

Обратите внимание, что байт '\ 0' в конце строки не записан.

Вы можете сделать это для всех видов постоянных значений, просто используйте соответствующую шестнадцатеричную строку кода, например.

#define MY_CONST_STRING "\x41"

даст также символ "A". Для многобайтовых вещей будьте осторожны, чтобы использовать правильную формулировку.

Предположим, вы хотите иметь указатель на INT_MAX, например, 0x7FFFFFFF на 32-битной системе. Затем вы можете сделать следующее:

#define PTR_TO_INT_MAX "\xFF\xFF\xFF\x7F"

Вы можете видеть, что это работает, передавая его как разыменованный указатель на printf:

printf ("max int value = %d\n", *(int*)PTR_TO_INT_MAX);

который должен печатать 2147483647.

Ответ 7

Эти ответы устарели, и кроме комментария никто не ссылается на последние обновления языка.

В компиляторе C99-C11, используя составной литерал, http://en.cppreference.com/w/c/language/compound_literal, можно создать  указатель на безымянную константу, например:

int *p = &((int){10});

Ответ 8

Для символов можно использовать дополнительные глобальные статические переменные. Может быть, что-то вроде:

#define THIS_CONST 'a'
static char tmp;
#define PTR_TO(X) ((tmp = X),&tmp)

write(fd,PTR_TO(THIS_CONST),sizeof(char));

Ответ 9

Нет причин, по которым компилятор должен помещать литерал в любое место памяти, поэтому ваш вопрос не имеет смысла. Например, выражение типа

int a;
a = 10;

вероятно, просто будет непосредственно переведен на "положить значение десять в регистр". В выводе компилятора на языке ассемблера значение 10 само по себе даже не существует как что-то в памяти, на которое можно было бы обратить внимание, кроме как часть фактического текста программы.

Вы не можете взять указатель на него.

Если вы действительно хотите, чтобы макрос получал указатель,

#include <stdio.h>
static char getapointer[1];

#define GETAPOINTER(x)  &getapointer, getapointer[0] = x

int main ()
{
    printf ("%d\n",GETAPOINTER('A'));
}

Ответ 10

Я вижу, что вы пытаетесь сделать здесь, но вы пытаетесь использовать здесь две принципиально разные вещи. Суть в том, что операторы case должны использовать значения, которые присутствуют во время компиляции, но указатели на данные в памяти доступны только во время выполнения.

Когда вы это сделаете:

#define THIS_CONST 'A'
char c = THIS_CONST;
write(fd, &c, sizeof(c))

вы делаете две вещи. Вы делаете макрос THIS_CONST доступным для остальной части кода во время компиляции, и вы создаете новый char во время выполнения, который инициализируется этим значением. В момент, когда выполняется строка write(fd, &c, sizeof(c)), концепция THIS_CONST больше не существует, поэтому вы правильно определили, что вы можете создать указатель на c, но не указатель на THIS_CONST.

Теперь, когда вы это сделаете:   static const char THIS_CONST = 'A';   переключатель (c) {     case THIS_CONST://метка ошибки - case не сводится к целочисленной константе       ...   }

вы пишете код, где значение аргумента case должно оцениваться во время компиляции. Однако в этом случае вы указали THIS_CONST таким образом, что это переменная, и поэтому ее значение доступно только во время выполнения, несмотря на то, что вы "знаете", что оно будет иметь определенное значение. Конечно, другие языки допускают разные вещи с утверждениями case, но это правила с C.

Вот что я предлагаю:

1) Не вызывайте переменную THIS_CONST. Там нет технической причины, но соглашение предполагает, что это макрос времени компиляции, и вы не хотите путать своих читателей.

2) Если вы хотите, чтобы одни и те же значения были доступны во время компиляции и времени выполнения, найдите подходящий способ сопоставления макросов времени компиляции во временных переменных. Это может быть так же просто, как:

#define CONST_STAR '*'
#define CONST_NEWLINE '\n'

static const char c_star CONST_STAR;
static const char c_newline CONST_NEWLINE;

Затем вы можете сделать:

switch(c) {
  case CONST_STAR:
    ...
    write(fd, &c_star, sizeof(c_star))
    ...
}

(Заметим также, что sizeof(char) всегда по определению. Вы уже знаете это, но это не так широко оценивается, как возможно, это должно быть.)

Ответ 11

Хорошо, я придумал немного взлома, только для символов - но я поставлю его здесь, чтобы узнать, вдохновляет ли он какие-либо лучшие решения от кого-либо еще.

static const char *charptr(char c) {
    static char val[UCHAR_MAX + 1];
    val[(unsigned char)c] = c;
    return &val[(unsigned char)c];
}

...

write(fd, charptr(THIS_CONST), sizeof(char));