|
Неожиданный результат, Прочитать два байта |
|
|
|
Jan 19 2008, 14:37
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Долго не мог выявить, почему цифровой датчик давления выдает неверный результат. Код //Первый случай; resultat = ((unsigned int)i2c_read(ACK)<<8) | i2c_read(ACK);
//Второй случай: HiTemp=i2c_read(ACK); LowTemp=i2c_read(ACK); resultat = ((unsigned int)HiTemp<<8) | LowTemp; Во втором случае дает другой (правильный) результат. Почему?
|
|
|
|
|
Jan 19 2008, 14:45
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(alux @ Jan 19 2008, 16:37)  Во втором случае дает другой (правильный) результат. Почему? Потому что стандарт языка С не определяет порядок вычисления подвыражений. Какой из i2c_read() будет вызван первым зависит в общем случае от положения звезд на небе. Ваша функция обладает побочными эффектами, поэтому вызывать ее дважды в одном выражении не рекомендуется во избежание таких неожиданностей.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jan 20 2008, 12:17
|

Местный
  
Группа: Свой
Сообщений: 222
Регистрация: 9-06-07
Пользователь №: 28 317

|
Цитата(Сергей Борщ @ Jan 19 2008, 17:45)  Потому что стандарт языка С не определяет порядок вычисления подвыражений. Что значит, стандарт не определяет? Вот строчка из "K&R C": "Все бинарные операции и условная операция (прим. Перевод.: условная операция группируется справа налево; это изменение внесено в язык в 1978 г.) группируются слева направо" Это следут проверить таким образом: Код resultat = ((HiTemp = (unsigned int)i2c_read(ACK))<<8) | (LowTemp = i2c_read(ACK));
printf("High byte 0x%02X\n", HiTemp); printf("Low byte 0x%02X\n", LowTemp);
|
|
|
|
|
Jan 20 2008, 15:01
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(rezident @ Jan 20 2008, 16:23)  ...компилятор может "оптимизировать" вызов функции и вместо двух вызовов формировать один, а все бинарные операции проводить с одним полученным результатом. Как уже заметили, просмотр листинга может прояснить этот нюанс. Вот что выдал листинг: Код 49 Accumulator = ((unsigned int)i2c_read(ACK)<<8) | i2c_read(ACK); \ 00000024 .... RCALL ?Subroutine0 \ ??CrossCallReturnLabel_0: \ 00000026 2F80 MOV R24, R16 \ 00000028 E090 LDI R25, 0 50 i2c_stop(); \ 0000002A ........ CALL i2c_stop Как говорят у нас, на Украине, rezident "має рацію"
|
|
|
|
|
Jan 20 2008, 18:59
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(Aesthete Animus @ Jan 20 2008, 14:17)  Вот строчка из "K&R C": "Все бинарные операции и условная операция (прим. Перевод.: условная операция группируется справа налево; это изменение внесено в язык в 1978 г.) группируются слева направо" Если под бинарными понимаются && и || - определен. Для остальных (кроме ?: и оператора "запятая") - нет. Вот цитата из стандарта языка (INTERNATIONAL STANDARD ISO/IEC 9899, Programming languages — C, Second edition 1999-12-01): Цитата 6.5 Expressions .... 3 The grouping of operators and operands is indicated by the syntax. Except as specified later (for the function-call (), &&, ||, ?:, and comma operators), the order of evaluation of subexpressions and the order in which side effects take place are both unspecified. Это дает гораздо больше свободы оптимизатору. Цитата(alux @ Jan 20 2008, 17:01)  Вот что выдал листинг: Мне пришло в голову, что во избежание подобных недоразумений функцию надо бы объявить как volatile. В этом случае: 1) Компилятор был бы обязан вызвать ее дважды. 2) IAR выдал бы предупреждение о неопределенности при использовании более одной volatile в выражении. GCC такое предупреждение не выдает, это ему минус. Хотя когда в IAR появилась эта проверка - многих предупреждение ставило в тупик. Вот, видимо, наглядный пример, когда это предупреждение обосновано.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jan 20 2008, 21:34
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(Aesthete Animus @ Jan 20 2008, 21:43)  Не дадите ссылочку на первоисточник - надо бы почитать для просвещения  Да запросто: Google -> ISO/IEC 9899, вторая ссылка дает pdf
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jan 20 2008, 22:07
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(Сергей Борщ @ Jan 20 2008, 21:59)  Мне пришло в голову, что во избежание подобных недоразумений функцию надо бы объявить как volatile. В этом случае: 1) Компилятор был бы обязан вызвать ее дважды. Даже без volatile компилятор обязан был вызвать эту функцию дважды, т.к. внутри этой функции есть работа с volatile переменными, например с регистрами TWI... P.S. Нда... , чего-то у IAR совсем плохо c volatile..., уже 2 баги за последние пару дней...
|
|
|
|
|
Jan 21 2008, 07:39
|
Гуру
     
Группа: Свой
Сообщений: 2 702
Регистрация: 14-07-06
Пользователь №: 18 823

|
Цитата(singlskv @ Jan 21 2008, 02:07)  Даже без volatile компилятор обязан был вызвать эту функцию дважды, т.к. внутри этой функции есть работа с volatile переменными, например с регистрами TWI...
P.S. Нда... , чего-то у IAR совсем плохо c volatile..., уже 2 баги за последние пару дней... Ничего он не обязан. Кто писал программу - Вы или компилятор? Все свойства функции - это типы параметров и адрес вызова, а требовать от компилятора анализа свойств используемых переменных внутри функции - эта попытка взвалить на него исправление Ваших ошибок.
--------------------
Уходя, оставьте свет...
|
|
|
|
|
Jan 21 2008, 20:54
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(Dog Pawlowa @ Jan 21 2008, 10:39)  Ничего он не обязан. Кто писал программу - Вы или компилятор? Все свойства функции - это типы параметров и адрес вызова, а требовать от компилятора анализа свойств используемых переменных внутри функции - эта попытка взвалить на него исправление Ваших ошибок. Атлична, раскажите мне пожалуйста, исходя из каких правил описанных в стандарте С компилятор имеет право обработать эти две ситуации по разному: Код int NegPort() { PORTB~=0xFF; return 1; }
....... a = NegPort() + NegPort(); и Код a = NegPort(); a+=NegPort(); по Вашей логике в первом случае он имеет право вызвать NegPort() один раз а во втором обязян вызвать 2 раза... и почему во втором случае он не вызовет так же один раз ? О порядке вызова функций при сложении речи не идет... P.S. Пример конечно сильно упрощен, но смысл я надеюсь понятен... P.P.S А так же в догонку очень хотелось бы понять, имеет ли право на жизнь следующая запись в языке С: Z = X * K1 * rand() + Y * K2 * rand(); или я обязан разделить ее на 2 подвыражения для того чтобы не получить одинаковый результат на выходе rand().... ???
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|