Метод передачи double в аргумент void * - PullRequest
1 голос
/ 13 января 2020

Мой вопрос вызван необходимостью правильной передачи переменной double через аргумент функции void *. Я спрашиваю, потому что на моей машине sizeof(void *) равно 4, а sizeof(double) равно 8, приведение одного к другому, похоже, должно привести к проблеме, но мой компилятор (CLANG, со ВСЕМИ предупреждениями.) Дает нет признаков проблемы, и код, кажется, работает нормально.

Обратите внимание, что я видел это и это . Они имеют похожие слова в своих заголовках, но не отвечают на этот конкретный c вопрос.

Приведет ли следующее к строгой ошибке нарушения псевдонима? Или неопределенному поведению?

// some calling function
double a = 0.000234423;
func1(&a);

...

void func1(void *var)
{
    double a = *(double *)(var);
}

Ответы [ 3 ]

8 голосов
/ 13 января 2020

Размер указателя не имеет ничего общего с размером элемента, на который он указывает!

4-байтовый указатель может без проблем указывать на элемент размером 8 или 32 байта или любой другой размер.

Я спрашиваю, потому что на моей машине sizeof (void *) равен 4, а sizeof (double) равен 8, приведение одного к другому, похоже, должно привести к проблеме

Что ж, если вы приведете 4-байтовый указатель к 8-байтовому двойному символу, это будет серьезной проблемой. Однако это , а не , что делает ваш код.

Это то, что делает код:

double a = * (double *)(var);
           | \--------------/
           |  This casts a void pointer to a double pointer
           |
           --> This dereferences the double pointer, i.e. reads the value of the pointed
               to element (aka the pointed to double)

Поскольку указатель void был создан из адреса двойное, совершенно законно привести его к двойному указателю и совершенно законно, чтобы прочитать значение указанного элемента. Другими словами - во время вызова функции у вас есть неявное приведение двойного указателя к пустой указатель , а внутри функции пустой указатель возвращается к двойной указатель . Совершенно законный C код.

С дополнительным шагом ваш код эквивалентен:

void func1(void *var)
{
    double *pd = (double *)var;
    double a = *pd;
}

, что делает его немного более понятным.

4 голосов
/ 13 января 2020

Нормальным решением действительно является передача адреса двойной переменной, поэтому double*. Это имеет размер, который не больше, чем void*.

1 голос
/ 16 января 2020

Если функция, которая получает аргумент типа void*, должна получить двойное значение, можно определить структуру, функцию и макрос:

struct doubleWrapper { double d[1]; };
struct doubleWrapper doWrapDouble(double d)
{      
  return (struct doubleWrapper){d};
}
#define wrapDouble(x) (doWrapDouble((x)).d)

, а затем использовать эту функцию на сайтах вызовов которым нужно передать значение double:

void functionTakingVoid(int mode, void *param)
{
  if (mode==0)
  {
    double myDouble = *(double*)param;
    ...
  }
}

void passDoubleToFunctionTakingVoid(double myValue)
{
  functionTakingVoid(0, wrapDouble(myValue));
}

В C99 макрос wrapDouble вернет указатель на двойник, время жизни которого продлится за счет вычисления полного выражения, включая сделанные вызовы функций таким образом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...