#c #function
#c #функция
Вопрос:
Я пишу это, потому что я действительно понятия не имею, почему это происходит. Я знаю, как это исправить / обойти, но я хотел бы знать причину, по которой это происходит. Я использую C, и мой компилятор — gcc 4.4.1 (TDM), работающий на компьютере Intel.
Предположения о плавающих значениях: -Соответствует стандарту IEEE 754 — Хранятся в виде большого конечного значения
Давайте предположим, что у нас есть функция, принимающая массив из 4 байт и возвращающая их в виде числа с плавающей запятой. Это цель функции. Давайте также скажем, что, например, все, что будет делать функция, это получать байты в «правильном порядке», а поскольку система имеет небольшой конечный код, она просто поменяет их местами и поместит их в число с плавающей точкой, чтобы вернуть значение. Для простоты я не включаю никаких проверок для NaN или INF, поскольку это не является целью этого вопроса.
float testFunction(char* arr)
{
//this will be the float we return
float ret;
//let's just get a char pointer to the float so we can alter its byte values
char* c = (char*)amp;ret;
//just swap them so they conform with little endian byte order
c[0] = arr[3];
c[1] = arr[2];
c[2] = arr[1];
c[3] = arr[0];
//up to here if you debug and watch ret's value it is correct as it is supposed to be
return ret;
}
Проблема везде, где я использую функцию… допустим, как показано ниже
float f = testFunction(arr);
Тогда значение с плавающей запятой f имеет совершенно неуместное значение с плавающей запятой для байтов, которые вы передаете в качестве параметров.
Способ успешно обойти это — объявить функцию, которая принимает значение с плавающей точкой в качестве параметра, и присвоить ей значение внутри функции следующим образом:
void testFunction(char* arr,float* f)
{
char* c = ((char*)f)
c[0] = arr[3];
c[1] = arr[2];
c[2] = arr[1];
c[3] = arr[0];
}
Но все же мой вопрос в том, почему это происходит, когда я пытаюсь вернуть значение? Я понимаю, что float ret — это временное значение внутри области действия функции, но оператор return должен копировать его значение за пределы функции. Разве это не правильно?
Чего мне не хватает? Я думаю, это должно быть что-то действительно очевидное.
Комментарии:
1. Какой массив байтов вы передаете и какое
float
значение вы ожидаете?2. Если мы считаем
float
, что это 32 бита (иchar
8 бит), то то, что вы делаете, правильно.3. @Charles: любой массив байтов, и я ожидаю, что значение с плавающей точкой соответствует стандарту IEEE 754. Просто используйте любой конвертер, чтобы увидеть, что вы должны указать в виде байтов и какого числа с плавающей точкой вы должны ожидать. Очень хороший здесь: h-schmidt.net/FloatApplet/IEEE754.html
4. Я попробовал вашу тестовую функцию, и она работает нормально для меня. По крайней мере, байты, которые я предоставил функции и в возвращаемом значении с плавающей запятой, были одинаковыми и имели правильный порядок. Поэтому попробуйте напечатать возвращаемое значение на вашем компьютере байт за байтом.
5. @Lefteris Так
arr
и естьBigEndian
, верно? Потому что, если это ужеLittleEndian
так, вам не нужна замена.
Ответ №1:
На самом деле в обоих случаях значение с плавающей точкой должно иметь «совершенно нерелевантное» значение, если вы не тщательно обработаете этот массив символов (например memcpy
, из a float
).
Вы не можете просто установить байты и надеяться, что они волшебным образом будут соответствовать представлению на вашей платформе.
Комментарии:
1. Что вы имеете в виду? Я предполагаю, что представление с плавающей запятой соответствует стандарту одинарной точности IEEE 754, хранящемуся в формате little endian. Может ли это быть еще более разнообразным? -И второй случай, передача по ссылке, похоже, всегда работает в моей системе. Вот почему я немного запутался
2. @Lefteris Давайте скажем так: есть места, где есть C и нет IEEE 754. Когда вы задаете вопросы о C, всегда лучше написать в начале что-то вроде «Мне нужно, чтобы это работало на gcc (компилятор VC / Intel C) на Intel». Таким образом, никто не сможет бросить вам «стандарт C не говорит о том или ином». Затем вы вводите страну «конкретной платформы и конкретного компилятора». Чтобы понять, насколько «общими» являются стандарты C / C , прочитайте это parashift.com/c -faq-lite/intrinsic-types.html#faq-26.6
3. Да, вы правы, ксанатос. Я должен был указать это. Предоставленная вами статья представляет интерес для чтения. Потратит некоторое время на его чтение. Но вопрос все еще остается, кто-нибудь знает, почему это будет работать так, а не иначе?
Ответ №2:
Я скажу что-нибудь глупое. Но попробуйте инициализировать ret
, например float ret = 0;
.
Я не уверен, что вы можете инициализировать переменную «piecemail» char
по одному и считать ее «инициализированной» для стандарта C (и компилятора)
Ответ №3:
WAG. Из-за приведения типов компилятор не распознает, что f
переменная была обновлена. Для компилятора нет связи между f
переменной и вашим c
указателем. Он не знает, что это псевдоним. Поскольку в ABI значение float возвращается в регистре, оно должно генерировать загрузку из стека, когда оно выполняет return
, но поскольку оно видит, что f
оно не инициализировано, оно ничего не делает и возвращает случайное содержимое используемого для этого регистра. Если вы объявите float
, как volatile
это должно делать то, что вы ожидаете.
Как уже было сказано, это дикий a.. угадайте.
Комментарии:
1. ЭТО была хорошая догадка, но, к сожалению, по крайней мере, в моей системе это не сработало. Спасибо, что немного подумали об этом. Просто интересно узнать о двух ваших сокращениях: WAG — > ? ABI -> Двоичный интерфейс приложения?
2. WAG: догадка о дикой заднице ABI: двоичный интерфейс приложения соглашение, используемое операционной системой для передачи параметров в функцию и из функции, которой должен следовать компилятор.