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

 
 
> Неожиданный результат, Прочитать два байта
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
singlskv
сообщение Jan 22 2008, 17:53
Сообщение #15


дятел
*****

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



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

Еще раз добавлено
Вот , восстановил (asdx-2). Возможно я был не внимательный, когда говорил, что функция вызывается один раз.
К сожалению Вы востановили не до конца,
перед обсуждаемыми строками у Вас еще стоят:
44 HiTemp=i2c_read(ACK);
45 LowTemp=i2c_read(NACK);
которые и мешают отработать оптимизатору так же как и было в самом первом листинге...
Цитата
Кстати, вопросик по ходу: как лучше писать
Код
Accumulator = ((unsigned int)i2c_read(ACK)<<8) | i2c_read(ACK);
или
Accumulator = ((unsigned int)i2c_read(ACK)<<8) + i2c_read(ACK);
ИМХО, в данном случае пофиг, но в общем и в целом лучше в таких случаях
пользоваться |, так и вероятность ошибок меньше и скорость работы может быть выше,
все зависит от конкретного компилятора.
Цитата
Прокоментируйте, пожалуйста следующее:
Код
     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 бит, затем сложить с прочитанным младшим байтом.
Порядок то здесь наверняка нарушен(наверное мы чуть запутали Вас нашим обсуждением),
в Вашем случае все равно нужно делить эти вызовы на два разных оператора, те первый ответ
данный Сергей Борщ был абсолютно правильный.
Тока вот все равно какая то фигня творится, LDI R25,0 здесь просто не должно быть...
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
|- - 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 Текстовая версия Сейчас: 22nd August 2025 - 22:34
Рейтинг@Mail.ru


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