|
Неожиданный результат, Прочитать два байта |
|
|
|
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, 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, 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().... ???
|
|
|
|
|
Jan 22 2008, 08:50
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(Сергей Борщ @ Jan 22 2008, 02:13)  Ведь тело функции может находиться и в другой единице компиляции. Откуда в таком случае компилятор узнает, что вы в ней обращаетесь к volatille-объектам? Я полагаю, что для получения правильного поведения функция должна быть описана как int NegPort() voltile {}. Хорошо, есть функция rand() которая определена в другой единице компиляции, в стандартной библиотеке. Определена она так int rand(void) , т.е. без всяких volatile. Вопрос: Могу ли я написать у себя в программе А = rand() + rand(); ? или компилятор может вызвать rand только один раз ?
|
|
|
|
|
Jan 22 2008, 11:57
|

Гуру
     
Группа: Модераторы
Сообщений: 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)
|
|
|
|
|
Jan 22 2008, 12:44
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(Сергей Борщ @ Jan 22 2008, 14:57)  Это заставило вновь внимательно посмотреть листинг из поста №7 - и что мы видим? Мы не видим там одного вызова i2c_read() - мы видим, что вызов(ы) i2c_read() происходят в подпрограмме ?Subroutine0, содержимое которой нам не показали. Видимо подобная конструкция используется в файле не один раз и компилятор посчитал оптимальным вынести ее в отдельную подпрограмму. Ставлю себе большую двойку за невнимательность. Так что вопрос - ошибся тут компилятор или нет остается открытым до обнародования листинга ?Subroutine0 Возможно Вы правы насчет объединения двух вызовов в отдельную подпраграмму, только тогда очень странно выглядит LDI R25,0 после подпрограммы... Автор, выложите более полный листинг.
|
|
|
|
|
Jan 22 2008, 13:31
|
Знающий
   
Группа: Свой
Сообщений: 589
Регистрация: 24-04-05
Пользователь №: 4 447

|
Цитата(singlskv @ Jan 22 2008, 16:44)  Автор, выложите более полный листинг. Пожалуйста. Не подозревал, что простой, на первый взгляд, вопрос вызовет такое бурное обсуждение  Добавлено
Прошу прощения, но теперь в данном 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); В последний раз добавлено 
Прокоментируйте, пожалуйста следующее: Код 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
|
|
|
|
Сообщений в этой теме
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
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|