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

 
 
> Неожиданный результат, Прочитать два байта
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
 
Start new topic
Ответов
Сергей Борщ
сообщение 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
Aesthete Animus
сообщение Jan 20 2008, 12:17
Сообщение #3


Местный
***

Группа: Свой
Сообщений: 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
Сергей Борщ
сообщение Jan 20 2008, 18:59
Сообщение #4


Гуру
******

Группа: Модераторы
Сообщений: 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
singlskv
сообщение Jan 20 2008, 22:07
Сообщение #5


дятел
*****

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


Гуру
******

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


дятел
*****

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


Гуру
******

Группа: Модераторы
Сообщений: 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
Сообщение #9


дятел
*****

Группа: Свой
Сообщений: 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
Сергей Борщ
сообщение Jan 21 2008, 23:13
Сообщение #10


Гуру
******

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



Цитата(singlskv @ Jan 21 2008, 23:16) *
конечно, и функция уже должна наследовать признак "volatile"
Кому должна? Ведь тело функции может находиться и в другой единице компиляции. Откуда в таком случае компилятор узнает, что вы в ней обращаетесь к volatille-объектам? Я полагаю, что для получения правильного поведения функция должна быть описана как int NegPort() voltile {}. "И сразу польется вода" © Тот самый Мюнхгаузен.


--------------------
На любой вопрос даю любой ответ
"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 22 2008, 08:50
Сообщение #11


дятел
*****

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



Цитата(Сергей Борщ @ Jan 22 2008, 02:13) *
Ведь тело функции может находиться и в другой единице компиляции. Откуда в таком случае компилятор узнает, что вы в ней обращаетесь к volatille-объектам? Я полагаю, что для получения правильного поведения функция должна быть описана как int NegPort() voltile {}.

Хорошо, есть функция rand() которая определена в другой единице компиляции,
в стандартной библиотеке.
Определена она так int rand(void) , т.е. без всяких volatile.

Вопрос:
Могу ли я написать у себя в программе
А = rand() + rand(); ?
или компилятор может вызвать rand только один раз ?
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Jan 22 2008, 11:57
Сообщение #12


Гуру
******

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



Цитата(singlskv @ Jan 22 2008, 10:50) *
Вопрос:
Могу ли я написать у себя в программе
А = rand() + rand(); ?
или компилятор может вызвать rand только один раз ?
Проконсультировался с ReAl - в С функция не может иметь атрибута volatile, это я с плюсами попутал:
Цитата
Но в любом случае функция "по определению" может иметь побочные эффекты (как та же getchar() - её побочный эффект - изменение некоего файлового об'екта, аппаратного окружения, ...) и компилятор вообще говоря не имеет права заменить два вызова функции одним.
Ну, за исключением работы оптимизатора, "видящего" тело функции и решившего, что это можно сделать - inline-функции в С99 или компиляция всей программы как одного файла ( --whole-program *.c для gcc ).
В gcc можно дать атрибут функции - "результат зависит только от переданных аргументов, больше никуда функция не смотрит и ничего снаружи не меняет", тогда несколько её вызовов с одинаковыми аргументами оптимизатор заменяет на один.
Так что тут вы правы - компилятор не может заменить два i2c_read() или NegPort() на один. Признаю, вводил в заблуждение.

Это заставило вновь внимательно посмотреть листинг из поста №7 - и что мы видим? Мы не видим там одного вызова i2c_read() - мы видим, что вызов(ы) i2c_read() происходят в подпрограмме ?Subroutine0, содержимое которой нам не показали. Видимо подобная конструкция используется в файле не один раз и компилятор посчитал оптимальным вынести ее в отдельную подпрограмму. Ставлю себе большую двойку за невнимательность.
Так что вопрос - ошибся тут компилятор или нет остается открытым до обнародования листинга ?Subroutine0


--------------------
На любой вопрос даю любой ответ
"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 22 2008, 12:44
Сообщение #13


дятел
*****

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



Цитата(Сергей Борщ @ Jan 22 2008, 14:57) *
Это заставило вновь внимательно посмотреть листинг из поста №7 - и что мы видим? Мы не видим там одного вызова i2c_read() - мы видим, что вызов(ы) i2c_read() происходят в подпрограмме ?Subroutine0, содержимое которой нам не показали. Видимо подобная конструкция используется в файле не один раз и компилятор посчитал оптимальным вынести ее в отдельную подпрограмму. Ставлю себе большую двойку за невнимательность.
Так что вопрос - ошибся тут компилятор или нет остается открытым до обнародования листинга ?Subroutine0
Возможно Вы правы насчет объединения двух вызовов в отдельную подпраграмму,
только тогда очень странно выглядит LDI R25,0 после подпрограммы...

Автор, выложите более полный листинг.
Go to the top of the page
 
+Quote Post
alux
сообщение Jan 22 2008, 13:31
Сообщение #14


Знающий
****

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



Цитата(singlskv @ Jan 22 2008, 16:44) *
Автор, выложите более полный листинг.

Пожалуйста. Не подозревал, что простой, на первый взгляд, вопрос вызовет такое бурное обсуждение 05.gif


Добавлено

Прошу прощения, но теперь в данном lst функция i2c_read(ACK) вызывается дважды. Хотя готов поклясться, что в посте №7 функция вызывалась один раз. Попытаюсь воспроизвести ту ситуацию еще раз. Листинги я не сохранил, к сожалению.

Еще раз добавлено

Вот , восстановил (asdx-2). Возможно я был не внимательный, когда говорил, что функция вызывается один раз. Кстати, вопросик по ходу: как лучше писать
Код
Accumulator = ((unsigned int)i2c_read(ACK)<<8) | i2c_read(ACK);
или
Accumulator = ((unsigned int)i2c_read(ACK)<<8) + i2c_read(ACK);


В последний раз добавлено smile.gif

Прокоментируйте, пожалуйста следующее:
Код
     49                Accumulator = ((unsigned int)i2c_read(ACK)<<8) | i2c_read(ACK);
   \   00000022   ........           CALL    i2c_readAck
   \   00000026   ........           CALL    i2c_readAck
   \   0000002A   2F80               MOV     R24, R16
   \   0000002C   E090               LDI     R25, 0
     50                i2c_stop();
   \   0000002E   ........           CALL    i2c_stop

Возможно, здесь нарушен порядок вызовов функций: первым должен читаться старший байт, затем сдвинуться на 8 бит, затем сложить с прочитанным младшим байтом.
Прикрепленные файлы
Прикрепленный файл  asdx.rar ( 1.38 килобайт ) Кол-во скачиваний: 29
Прикрепленный файл  asdx_2.rar ( 1.72 килобайт ) Кол-во скачиваний: 27
 
Go to the top of the page
 
+Quote Post

Сообщений в этой теме
- alux   Неожиданный результат   Jan 19 2008, 14:37
|- - alux   Я поражен меткости и, главное, оперативности Ваших...   Jan 19 2008, 14:53
|- - Aesthete Animus   Цитата(Сергей Борщ @ Jan 20 2008, 21:59) ...   Jan 20 2008, 19:43
||- - Сергей Борщ   Цитата(Aesthete Animus @ Jan 20 2008, 21...   Jan 20 2008, 21:34
|- - singlskv   Цитата(alux @ Jan 22 2008, 16:31) Прошу п...   Jan 22 2008, 17:53
|- - alux   Код 49 Accumulator = ((unsigned int...   Jan 22 2008, 19:47
|- - ReAl   Цитата(alux @ Jan 22 2008, 15:31) Кстати,...   Jan 22 2008, 21:17
|- - alux   Цитата(ReAl @ Jan 23 2008, 01:17) У IAR-а...   Jan 23 2008, 07:13
|- - ReAl   Я немного цитаты перетасую Цитата(alux @ Jan...   Jan 23 2008, 14:49
|- - alux   Цитата(ReAl @ Jan 23 2008, 18:49) в преды...   Jan 23 2008, 15:10
|- - Сергей Борщ   Цитата(alux @ Jan 23 2008, 17:10) ReAl ог...   Jan 23 2008, 15:30
|- - ReAl   Цитата(Сергей Борщ @ Jan 23 2008, 17:30) ...   Jan 24 2008, 08:04
- - Baser   Цитата(alux @ Jan 19 2008, 16:37) Долго н...   Jan 19 2008, 15:44
- - rezident   Я не такой большой специалист в Си и могу ошибатьс...   Jan 20 2008, 12:23
- - alux   Цитата(rezident @ Jan 20 2008, 16:23) ......   Jan 20 2008, 15:01


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

 


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


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