Возврат поплавка в функцию void * - PullRequest
4 голосов
/ 24 мая 2019

Я изучаю, как работают указатели, но я ничего не понимаю в этом коде. Возврат int в void * функция работает как талисман, но возврат float не делает.

#include <stdio.h>

void* square (const void* num);

int main() {
  int x, sq_int;
  x = 6;
  sq_int = square(&x);
  printf("%d squared is %d\n", x, sq_int);

  return 0;
}

void* square (const void *num) {
  int result;
  result = (*(int *)num) * (*(int *)num);
  return result;
}
#include <stdio.h>

void* square (const void* num);

int main() {
  float x, sq_int;
  x = 6;
  sq_int = square(&x);
  printf("%f squared is %f\n", x, sq_int);

  return 0;
}

void* square (const void *num) {
  float result;
  result = (*(float *)num) * (*(float *)num);
  return result;
}

Ответы [ 3 ]

6 голосов
/ 24 мая 2019

Обе ваши функции вызывают неопределенное поведение , так как тип возвращаемого значения и тип выражения в операторе return не совпадают.

Что касается того, почему кажется для работы, прочитайте о неопределенном поведении.

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

Примечания по теме:

Цитата из C11, глава §6.8.6.4

[...] Если выражение имеет тип, отличный от типа возврата функции, в которой оно появляется, значение преобразуется, как если бы оно было присвоено объекту, имеющему тип возврата функции.

и относительно простого назначения, из главы §6.5.16.1, простое назначение :

Ограничения

Должно выполняться одно из следующих:

  • левый операнд имеет атомарный, квалифицированный или неквалифицированный арифметический тип, а правый имеет арифметический тип;
  • левый операнд имеет атомарную, квалифицированную или неквалифицированную версию структуры или типа объединения, совместимого с типом правого;
  • левый операнд имеет атомарный, квалифицированный или неквалифицированный тип указателя, и (учитывая тип, который левый операнд будет иметь после преобразования lvalue) оба операнда являются указателями на квалифицированные или неквалифицированные версии совместимых типов, а указанный типслева находятся все квалификаторы того типа, на который указывает справа;
  • левый операнд имеет атомный, квалифицированный или неквалифицированный тип указателя, и (учитывая тип, который левый операнд будет иметь после преобразования в lvalue) один операнд является указателем на тип объекта, а другой - указателем наквалифицированная или неквалифицированная версия void, и тип, на который указывает слева, имеет все квалификаторы типа, на который указывает право;
  • левый операнд является атомарным, квалифицированным или неквалифицированным указателем, а правый - константой нулевого указателя;или
  • левый операнд имеет тип атомарный, квалифицированный или неквалифицированный _Bool, а правый - указатель.

Таким образом, вы не можете юридически назначить int илиfloat к типу указателя - это нарушение ограничения.

2 голосов
/ 24 мая 2019

Проблема в том, что вы пытаетесь преобразовать число с плавающей точкой в ​​указатель.

C не указывает ничего о формате указателя.Это потому, что C пытается абстрагировать все возможные архитектуры, а некоторые архитектуры имеют весьма необычные способы представления указателей.Например, на машинах lisp места памяти, следовательно, указатели представлены в сегменте: смещение).То же самое и в гарвардских архитектурах - это зона кода зоны данных и способы, которыми указатель кодирует эти разные зоны.C делает только различие между указателем на функцию и указателем на объект, но ничего не говорит о значении каждого бита объекта указателя.

Тот факт, что целое число на вашем компьютере хорошо преобразовано, является просто случайностью.Если ваш компьютер имел 64 адреса шины и целые числа на 32, а возвращенный указатель имеет некоторый смысл и кажется «равным» целому числу, это означает только то, что архитектура может иметь такое же представление целочисленных значений, что и для указателей.

Теперь в вашем коде float преобразуется в указатель, а из указателя обратно в float.Представление с плавающей точкой определяется как знак / экспонента / мантисса, но представление указателя не определено, и может произойти неопределенное поведение.

1 голос
/ 24 мая 2019

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

#include <stdio.h>
#include <stdlib.h>


void* square (const void* num);

int main() {
  float *x, sq_int;
  float value = 6;
  x = square(&value);
  printf("%f squared is %f\n", value, *x);
  free(x);
  return 0;
}

void* square (const void *num) {
  float *result = malloc(sizeof(float));
  *result = (*(float *)num) * (*(float *)num);
  return ((float*)result);
}
...