реклама на сайте
подробности

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> Неожиданный результат, Прочитать два байта
alux
сообщение Jan 19 2008, 14:37
Сообщение #1


Знающий
****

Группа: Свой
Сообщений: 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;

Во втором случае дает другой (правильный) результат. Почему?
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Jan 19 2008, 14:45
Сообщение #2


Гуру
******

Группа: Модераторы
Сообщений: 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)
Go to the top of the page
 
+Quote Post
alux
сообщение Jan 19 2008, 14:53
Сообщение #3


Знающий
****

Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447



Я поражен меткости и, главное, оперативности Ваших ответов. Спасибо, Сергей.
Go to the top of the page
 
+Quote Post
Baser
сообщение Jan 19 2008, 15:44
Сообщение #4


Просто Che
*****

Группа: Свой
Сообщений: 1 567
Регистрация: 22-05-07
Из: ExUSSR
Пользователь №: 27 881



Цитата(alux @ Jan 19 2008, 16:37) *
Долго не мог выявить, почему цифровой датчик давления выдает неверный результат.
...
Во втором случае дает другой (правильный) результат. Почему?

Заглядывание в листинг при непонятной работе программы частенько помогает очень быстро найти ошибку. Особенно если кажется, что все написано правильно. smile.gif
Go to the top of the page
 
+Quote Post
Aesthete Animus
сообщение Jan 20 2008, 12:17
Сообщение #5


Местный
***

Группа: Свой
Сообщений: 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);
Go to the top of the page
 
+Quote Post
rezident
сообщение Jan 20 2008, 12:23
Сообщение #6


Гуру
******

Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882



Я не такой большой специалист в Си и могу ошибаться, но ИМХО компилятор может "оптимизировать" вызов функции и вместо двух вызовов формировать один, а все бинарные операции проводить с одним полученным результатом. Как уже заметили, просмотр листинга может прояснить этот нюанс.
Go to the top of the page
 
+Quote Post
alux
сообщение Jan 20 2008, 15:01
Сообщение #7


Знающий
****

Группа: Свой
Сообщений: 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 "має рацію" smile.gif
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Jan 20 2008, 18:59
Сообщение #8


Гуру
******

Группа: Модераторы
Сообщений: 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)
Go to the top of the page
 
+Quote Post
Aesthete Animus
сообщение Jan 20 2008, 19:43
Сообщение #9


Местный
***

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



Цитата(Сергей Борщ @ Jan 20 2008, 21:59) *
Если под бинарными понимаются && и || - определен. Для остальных (кроме ?: и оператора "запятая") - нет. Вот цитата из стандарта языка (INTERNATIONAL STANDARD ISO/IEC 9899, Programming languages — C, Second edition 1999-12-01)...

Вот оно как... Не дадите ссылочку на первоисточник - надо бы почитать для просвещения smile.gif
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Jan 20 2008, 21:34
Сообщение #10


Гуру
******

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



Цитата(Aesthete Animus @ Jan 20 2008, 21:43) *
Не дадите ссылочку на первоисточник - надо бы почитать для просвещения smile.gif
Да запросто: Google -> ISO/IEC 9899, вторая ссылка дает pdf


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
singlskv
сообщение Jan 20 2008, 22:07
Сообщение #11


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(Сергей Борщ @ Jan 20 2008, 21:59) *
Мне пришло в голову, что во избежание подобных недоразумений функцию надо бы объявить как volatile. В этом случае:
1) Компилятор был бы обязан вызвать ее дважды.
Даже без volatile компилятор обязан был вызвать эту функцию дважды, т.к. внутри
этой функции есть работа с volatile переменными, например с регистрами TWI...

P.S. Нда... , чего-то у IAR совсем плохо c volatile..., уже 2 баги за последние пару дней...
Go to the top of the page
 
+Quote Post
Dog Pawlowa
сообщение Jan 21 2008, 07:39
Сообщение #12


Гуру
******

Группа: Свой
Сообщений: 2 702
Регистрация: 14-07-06
Пользователь №: 18 823



Цитата(singlskv @ Jan 21 2008, 02:07) *
Даже без volatile компилятор обязан был вызвать эту функцию дважды, т.к. внутри
этой функции есть работа с volatile переменными, например с регистрами TWI...

P.S. Нда... , чего-то у IAR совсем плохо c volatile..., уже 2 баги за последние пару дней...


Ничего он не обязан.
Кто писал программу - Вы или компилятор? smile.gif
Все свойства функции - это типы параметров и адрес вызова, а требовать от компилятора анализа свойств используемых переменных внутри функции - эта попытка взвалить на него исправление Ваших ошибок.


--------------------
Уходя, оставьте свет...
Go to the top of the page
 
+Quote Post
singlskv
сообщение Jan 21 2008, 20:54
Сообщение #13


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(Dog Pawlowa @ Jan 21 2008, 10:39) *
Ничего он не обязан.
Кто писал программу - Вы или компилятор? smile.gif
Все свойства функции - это типы параметров и адрес вызова, а требовать от компилятора анализа свойств используемых переменных внутри функции - эта попытка взвалить на него исправление Ваших ошибок.
Атлична, раскажите мне пожалуйста, исходя из каких правил описанных в стандарте С
компилятор имеет право обработать эти две ситуации по разному:
Код
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().... ???
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Jan 21 2008, 21:10
Сообщение #14


Гуру
******

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



Цитата(singlskv @ Jan 21 2008, 22:54) *
по Вашей логике в первом случае он имеет право вызвать NegPort() один раз а во втором обязян вызвать 2 раза... и почему во втором случае он не вызовет так же один раз ?
Видимо потому, что между вызовами во втором случае стоит ";", т.е. sequence point, на момент которой все побочные эффекты должны выполниться. Побочным эффектом в данном случае является запись в PORTB. Если я все правильно понимаю.


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
singlskv
сообщение Jan 21 2008, 21:16
Сообщение #15


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(Сергей Борщ @ Jan 22 2008, 00:10) *
Побочным эффектом в данном случае является запись в PORTB. Если я все правильно понимаю.

конечно, и функция уже должна наследовать признак "volatile", те sequence point должны
быть при каждом вызове этой функции
Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 27th July 2025 - 01:58
Рейтинг@Mail.ru


Страница сгенерированна за 0.01477 секунд с 7
ELECTRONIX ©2004-2016