«значение указателя используется там, где ожидалось число с плавающей запятой», но переменная имеет тип void *

Меня смущает следующее сообщение об ошибке. Я не знаю, откуда здесь взялся floating-point.

error: pointer value used where a floating-point was expected
  279 |       ret_value = (void *)((IntFuncWithThreeArg)closure->bound_func)((int)args_values[0], (double)args_values[1], (int)args_values[2]);

Вышеприведенное выражение представляет собой вызов замыкания ffi, которое возвращает int следующего типа:

typedef int (*IntFuncWithThreeArg)(int, double, int);

Переменная ret_value определяется как void *. И args_values, очевидно, является массивом void *, который содержит разные типы.

Что-то не так или проблема скорее всего где-то в другом месте кода?

Обновлено:

Я постараюсь свести к минимуму важные части кода (опуская детали реализации несвязанных вещей), но компиляция минимального примера реализации полиморфного объекта не будет чем-то близким к «минимуму».

Замыкания вызываются и сохраняются внутри объектов:

typedef void (*GeneralFunc)();

typedef struct {
  char *name;
  char *ret_t;
  char **args_t;
  uint8_t args_n;
  void *bound_func;
} ClosureBinding;

ClosureBinding *bind_closure(char *ret_t, char *name, GeneralFunc closure_p, char *args_t[], const uint8_t args_n, void *stream) {
  ffi_cif cif;
  ffi_type *args[args_n];
  ffi_closure *closure;
  void *bound_func;
  _Bool failed;

  failed = 1;
  closure = NULL;

  closure = ffi_closure_alloc(sizeof(ffi_closure), &bound_func);
  if (closure) {
    for (uint8_t i = 0; i < args_n; ++i)
      args[i] = strcmp(args_t[i], "int") == 0 ? &ffi_type_sint : strcmp(args_t[i], "double") == 0 ? &ffi_type_double : &ffi_type_schar;

    if (ffi_prep_cif (&cif, ABI, args_n, strcmp(ret_t, "int") == 0 ? &ffi_type_sint : strcmp(ret_t, "double") == 0 ? &ffi_type_double : &ffi_type_schar, args) == FFI_OK)
      if (ffi_prep_closure_loc(closure, &cif, closure_p, stream, bound_func) == FFI_OK)
        failed = 0;
  }

  return new_closure_binding(name, ret_t, args_t, args_n, bound_func);
}

[...]

typedef struct Object_t {
  ClosureBinding **closures;
  uint8_t closures_n;

  void *(*call)(struct Object_t *self, int index, ...);
} Object;

void *call(Object *self, int index, ...);

Object *new_object(const uint8_t closures_n) {
  Object *self     = (Object *)malloc(sizeof(Object));

  self->call       = call;
  self->closures   = malloc(sizeof(ClosureBinding) *closures_n);
  self->closures_n = closures_n;

  return self;
}

Вероятно, наиболее важными являются детали реализации функции call.

typedef int (*IntFuncWithThreeArg)(int, double, int);

void *call(Object *self, int index, ...) {
  va_list args;
  va_start(args, index);

  ClosureBinding *closure = self->closures[index];

  printf("Call the '%s' closure\n", closure->name);

  // Get the closure argument values from variadic arguments
  void *args_values[closure->args_n];

  for (int i = 0; i < closure->args_n; ++i) {
    char *arg;

    printf("Before argument allocation\n");
    arg = closure->args_t[i];
    if (!strcmp(arg, "int")) {
      int *p = malloc(sizeof(int));
      *p = va_arg(args, int);
      args_values[i] = p;
    } else if (!strcmp(arg, "double") || !strcmp(arg, "float")) {
      double *p = malloc(sizeof(double));
      *p = va_arg(args, double);
      args_values[i] = p;
    }
  }

  void *ret_value;

  if (!strcmp(closure->ret_t, "int")) {
    if (closure->args_n == 3) {
      ret_value = (void *)((IntFuncWithThreeArg)closure->bound_func)(*(int *)args_values[0], *(double *)args_values[1], *(int *)args_values[2]);
    } else {
      ret_value = (void *)-1;
      fprintf(stderr, "Warning: Attempt to execute an INT type closure but argument's type is unregistered\n");
    }
  } else {
    ret_value = (void *)-1;
    fprintf(stderr, "Warning: Return type unregistered\n");
  }

  va_end(args);
  return ret_value;
}

Пожалуйста, покажите код вместо его описания.

n. m. could be an AI 09.04.2024 20:31

Вы говорите, что целевая переменная — void *ret_value, но функция возвращает int, а не указатель.

Weather Vane 09.04.2024 20:41

Кроме того: я бы обычно называл приведение int к void * красным флагом (ret_value). Ваш вопрос: если «args_values ​​— это... массив void *», а индекс 1 на самом деле является указателем на double, то ваш второй аргумент должен быть: *(double *)args_values[1]. Я могу ошибаться, но у меня сложилось впечатление, что вы допускаете множество ошибок из-за того, что забываете разницу между указателем и тем, на что он указывает.

Avi Berger 09.04.2024 20:43

У ваших первого и третьего аргументов одна и та же проблема, компилятор просто не предупредил вас об этом. Таким образом, мне кажется, что обработка возвращаемого значения и всех трех аргументов fcn страдает от одной и той же проблемы.

Avi Berger 09.04.2024 20:48

C не определяет преобразования между void * и плавающими типами, поэтому (double)args_values[1] имеет неопределенное поведение. Думаю, это то, на что жалуется диагност.

John Bollinger 09.04.2024 21:39

Кроме того, хотя C допускает преобразования из void * в целочисленные типы и обратно, это не значит, что это хорошая или полезная вещь для вас, которую можно делать в своем коде. void * не является типом «Любой».

John Bollinger 09.04.2024 21:41

@AviBerger Спасибо, я дважды ошибся с кастингом, это правда. Хотя я знаю, что приведение void * к int сомнительно, но как вы думаете, что мне следует делать, когда у меня есть функция, которая вызывает общие замыкания из массива объектов? Библиотека Linux C делает то же самое для реализации буфера, например. для функции read.

siery 09.04.2024 22:53

@RetiredNinja Но, господа, этот вопрос не столько о коде, сколько о том, как правильно привести эти значения.

siery 09.04.2024 23:11

@JohnBollinger Да, C не определяет преобразование из void * в float. Но это происходит с void * по double.

siery 09.04.2024 23:37

Нет, @siery, спецификация языка C не определяет преобразования между void * и любым плавающим типом, float, double или long double. Ваша реализация может предоставлять такие преобразования в качестве расширения, но спецификация языка не определяет их как разрешенные преобразования, в отличие от преобразований между void * и целочисленными типами или между void * и другими типами указателей на объекты.

John Bollinger 09.04.2024 23:46

@JohnBollinger Хм… возможно, это проблема, с которой я сталкиваюсь при переносе этого фрагмента кода на другую платформу. Можете подсказать источник этой информации. В C можно присвоить int *void * без приведения, верно? Вот почему компилятор не жалуется на int?

siery 10.04.2024 00:07

@siery, вы можете конвертировать любые типы указателей объектов. Сюда входят void *, int *, double *, float *, struct any_struct * и другие. Вы не можете конвертировать типы указателей и плавающие типы, такие как double. Тип double * — это тип указателя, а не плавающий тип. Возможно, вы не осознаете, что типы указателей отличаются от типов, на которые они указывают.

John Bollinger 10.04.2024 00:10
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
12
126
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

И args_values, очевидно, представляет собой массив void *, который содержит разные типы.

Вы применяете void * к double, как показано ниже:

int main( void ) {
   void *vp;
   double d = (double)vp;
}
<source>:3:4: error: pointer value used where a floating-point was expected
    3 |    double d = (double)vp;
      |    ^~~~~~

Это неопределенное поведение, поэтому предупреждение.


Действительно ли args_values[1] является указателем на двойной номер? Если это так, вам следует использовать

*(double *)args_values[1]

Если нет, вам следует использовать тип union, а не void *.

Аналогичные изменения следует внести и для значений int.

Да, args_values[1] — это указатель на двойной номер. Я должен был упомянуть об этом. Спасибо за вывод, я забыл про разыменование. Что ж, я многому учусь, работая над этим проектом.

siery 09.04.2024 23:22

Что ж, я знаю, что в идеале мне следует использовать объединение для типов аргументов, но сейчас я попытался изменить его на: ret_value = (void *)((IntFuncWithThreeArg)closure->bound_func)(*(int *)args_values[0], *(double *)args_values[1], *(int *)args_values[2]). Теперь это дает мне ошибку сегментации во время выполнения.

siery 10.04.2024 00:29

Ошибка сегментации @siery имела бы смысл, если бы подозрение Джона Боллинджера относительно типа «Любой» было верным. Я предполагал, что ваши указатели на самом деле являются указателями. Является ли ваш args_values[1] указателем на то, где в памяти находится настоящий двойник, или фрагмент памяти, который, как вы говорите компилятору, — это одно, а вы пытаетесь вставить что-то другое?

Avi Berger 10.04.2024 04:03

@Ави Бергер, ОП подтвердил, что «args_values[1] является указателем на двойник». Но да, им следует перепроверить, действительно ли это указатель. (например, double d = 1.2; args_values[1] = &d;, а не args_values[1] = d;.) В любом случае информации для дальнейшего обсуждения недостаточно.

ikegami 10.04.2024 04:06

@ikegami, я знаю. Так и должно быть, но комментарий ОП к ошибке сегментации заставляет меня усомниться в этом. И MS делала подобные вещи с помощью Windows API.

Avi Berger 10.04.2024 04:13

@Ави Бергер, я не говорил, что это должен быть указатель на двойной номер; Я сказал, что ОП уже сказал, что это так.

ikegami 10.04.2024 04:27

@AviBerger Я обновил код, отвечающий за вызов замыканий. КСТАТИ. Я думаю, что окончание средней школы — хорошее время, чтобы повзрослеть и перестать чувствовать себя главным героем. Я согласен, что мне следует меньше сосредотачиваться на гриферах и больше на ценных комиссионных, моя вина, но вы почти согласны с тем, что множество комментаторов в основном распространяют ненависть и неверную информацию. Но что ж, я думаю, сегодня это Интернет.

siery 10.04.2024 05:32

@siery вообще-то, я виноват. Судя по этому коду, это похоже на икегами, и вы правы, и теперь у вас есть правильные аргументы, и они не являются причиной ошибки сегмента, как я думал, они могут быть. Не знаю, что такое.

Avi Berger 10.04.2024 06:30

Другие вопросы по теме