2016-02-04 2 views
-1

прошли неправильный типа У меня есть следующая программаWeird поведения функции, когда параметры

void swap(float * x, float * y) 
{ 
    float aux 
    aux = *x; 
    *x = *y; 
    *y = aux; 
} 

int main(void) 
{ 
    double a = 3.5, b = 5.6; 
    swap(&a, &b); 
    printf("%g %g\n", a, b); 
    return 0; 
} 

Программа компилируется, он явно бросает некоторые предупреждения, но она работает, и значение a, b не получает местами. Я не понимаю, что происходит, я бы подумал, что это не скомпилировано, или это сработает, но это совсем другое.

Что происходит?

+0

'swap (& a, &b);' '' '' '' '' '' '' '' '' 'удваиваются! –

+0

Да, я знаю, это то, что я спрашиваю, почему функция выполняется правильно, но ничего не делает? это катастрофа или что-то в этом роде – YoTengoUnLCD

+0

Возможно, прислушайтесь к предупреждениям - они есть по какой-то причине (например, ум ваш) –

ответ

2

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

Вероятная причина, по которой он не падает, заключается в том, что код, созданный для функции swap, действительно не заботится о значениях в переменных, поскольку он не выполняет никаких арифметических действий. Так как это просто копирование из одной переменной в другую, он просто копирует память в байтах - это на самом деле не сильно отличается от:

memcpy(&aux, x, sizeof(float)); 
memcpy(x, y, sizeof(float)); 
memcpy(y, &auz, sizeof(float)); 

Что это делает, замена 32, если 64 бит двух дублей. Причина, по которой вы не видите никаких изменений, состоит в том, что это младшие разряды значения, и они округляются.

В этом демонстрационном примере показано внутреннее представление двух значений в шестнадцатеричном формате до и после замены (с использованием более точного ввода с printf).

http://ideone.com/boN3Df

+0

, хотя интересно исследовать, почему вызов функции, кажется, оставляет a и b незатронутыми. Я предлагаю, чтобы ОП попытался это обработать – pm100

+0

@ pm100 Это именно то, что я прошу ... Я знаю, что это «неопределенное поведение», но в большинстве случаев, что происходит, есть объяснение! – YoTengoUnLCD

+0

Я предлагаю вам попробовать разобраться в себе. Сначала посмотрите, как представлены парные и поплавки - начните здесь https://en.wikipedia.org/wiki/Floating_point – pm100

1

Чтобы расширить ответ Barmar в: неопределенное поведение вы удара из-за строгого наложения. Строгое сглаживание - это требование, чтобы указатель на тип A не мог быть привязан к указателю типа B, если B не имеет размера байта (или некоторого магии со строковыми полями, см. What is the strict aliasing rule?). Мне было бы интересно узнать, будет ли он заменять элементы, если вы скомпилируете -fno-strict-aliasing.

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

0

Если вы попытаетесь удвоить с более длинной десятичной частью, вы увидите разницу.

void swap(float * x, float * y) 
{ 
    float aux = *x; 
    *x = *y; 
    *y = aux; 
} 

int main(void) 
{ 
    double a = 3.0, b = 5.987654321; 
    printf("%f %f\n", a, b); 
    swap(&a, &b); 
    printf("%f %f\n", a, b); 
    return 0; 
} 

Выход:

3.000000 5.987654 
3.000001 5.987652 

Стандартные парный 8 байт, и поплавки 4 байта. С вашим компилятором своп эффективно обменивает 4 бита каждого поплавка (это все еще неопределенное поведение).

Смежные вопросы