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

 
 
3 страниц V   1 2 3 >  
Closed TopicStart new topic
> Что означает этот код?
RW6MKA
сообщение Mar 8 2014, 15:05
Сообщение #1


Частый гость
**

Группа: Участник
Сообщений: 163
Регистрация: 25-10-10
Из: Ростовская обл.
Пользователь №: 60 401



Здравствуйте уважаемые форумчане. Возник вопрос при оптимизации кода. Было
Код

                data[0] = w1_receive_byte();//читаем два байта с температурой
        data[1] = w1_receive_byte();
            //загоняем в двух байтную переменную
        temp = data[1];
        temp = temp<<8;
        temp |= data[0];

Подсказали что лучше использовать такое выражение
Код
        *((char *)&Temp;) = w1_receive_byte();
        *((char *)&Temp; + 1) = w1_receive_byte();

Вот никак не могу понять смысла этого кода. Я так понимаю что * и & это операции над указателями , а () приведение к типу но....
Вообщем если не трудно объясните начинающему подробно смысл сей конструкции.
Go to the top of the page
 
+Quote Post
_Артём_
сообщение Mar 8 2014, 15:15
Сообщение #2


Гуру
******

Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322



Цитата(RW6MKA @ Mar 8 2014, 19:05) *
Код
        *((char *)&Temp;) = w1_receive_byte();
        *((char *)&Temp; + 1) = w1_receive_byte();

Вот никак не могу понять смысла этого кода. Я так понимаю что * и & это операции над указателями , а () приведение к типу но....

У вас этот код компилируется? Врядли...Точки с запятой лишние.

Код
unsigned short Temp;
        *((char *)&Temp) = w1_receive_byte();
        *((char *)&Temp + 1) = w1_receive_byte();

Смысл такой - берётся адрес Temp и в него пишется 1 байт.Во второй строчке - пишется ещё байт по адресу следующего байта.
Go to the top of the page
 
+Quote Post
RW6MKA
сообщение Mar 8 2014, 15:29
Сообщение #3


Частый гость
**

Группа: Участник
Сообщений: 163
Регистрация: 25-10-10
Из: Ростовская обл.
Пользователь №: 60 401



А более подробно можно. Конкретно по каждому знаку. Просто общий смысл как бы я сразу понял ( из того что предложили сделать замену, значит этот код выполняет ту же функцию). Хотелось бы полностью понимать, что бы в последствии не было проблем с использованием подобных конструкций.
Go to the top of the page
 
+Quote Post
aaarrr
сообщение Mar 8 2014, 15:36
Сообщение #4


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



Цитата(RW6MKA @ Mar 8 2014, 19:05) *
Подсказали что лучше использовать такое выражение

А чем лучше - не подсказали? А на деле оно хуже.

Цитата(RW6MKA @ Mar 8 2014, 19:29) *
А более подробно можно. Конкретно по каждому знаку.

&Temp - взяли адрес Temp
(char *)&Temp - привели к типу указателя на char
*((char *)&Temp) = w1_receive_byte() - по указателю на char, который равен адресу Temp, занесли результат w1_receive_byte().
Go to the top of the page
 
+Quote Post
_Артём_
сообщение Mar 8 2014, 15:37
Сообщение #5


Гуру
******

Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322



Цитата(RW6MKA @ Mar 8 2014, 19:29) *
А более подробно можно

Берётся адрес Temp: &Temp
приводится к указателю на char: (char *)(полученный адрес)
по указателю на char делается запись того что вернёт функция:
*(указатель на char)=w1_();

Со второй строчкой также - только ещё инкремент указателя есть.
Go to the top of the page
 
+Quote Post
RW6MKA
сообщение Mar 8 2014, 15:54
Сообщение #6


Частый гость
**

Группа: Участник
Сообщений: 163
Регистрация: 25-10-10
Из: Ростовская обл.
Пользователь №: 60 401



Ну вроде экономия памяти при компиляции. Можно конечно оставить и свой код, места мне хватает, но задело, не сталкивался с подобным и поэтому хочу понять. Народ, не сочтите за труд, поэтапно объясните. &Temp - выдаст адрес переменной. (char*)&Temp - приведение типа адреса переменной Temp к типу char(не пойму зачем знак *) и наконец зачем знак * в самом начале выражения?

Цитата(aaarrr @ Mar 8 2014, 19:36) *
А чем лучше - не подсказали? А на деле оно хуже.


&Temp - взяли адрес Temp
(char *)&Temp - привели к типу указателя на char
*((char *)&Temp) = w1_receive_byte() - по указателю на char, который равен адресу Temp, занесли результат w1_receive_byte().

Ага, понял. А в следующем выражении заносят по адресу Temp+1. то есть получаем эти два байта записанные в соседних адресах или я что то путаю?
Go to the top of the page
 
+Quote Post
aaarrr
сообщение Mar 8 2014, 15:57
Сообщение #7


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



* - указатель

Без лишней памяти лучше будет написать так:
Код
temp = (char)w1_receive_byte();
temp = (temp << 8) | (char)w1_receive_byte();

(char) нужны только на случай, если w1_receive_byte() возвращает что-то другое.

Вариант
Код
*((char *)&Temp) = w1_receive_byte();
*((char *)&Temp + 1) = w1_receive_byte();

подразумевает определенное расположение байтов в слове, которое может отличаеться на разных архитектурах.

Цитата(RW6MKA @ Mar 8 2014, 19:54) *
Ага, понял. А в следующем выражении заносят по адресу Temp+1. то есть получаем эти два байта записанные в соседних адресах или я что то путаю?

Да, так.
Go to the top of the page
 
+Quote Post
RW6MKA
сообщение Mar 8 2014, 15:58
Сообщение #8


Частый гость
**

Группа: Участник
Сообщений: 163
Регистрация: 25-10-10
Из: Ростовская обл.
Пользователь №: 60 401



Цитата(_Артём_ @ Mar 8 2014, 19:37) *
Берётся адрес Temp: &Temp
приводится к указателю на char: (char *)(полученный адрес)
по указателю на char делается запись того что вернёт функция:
*(указатель на char)=w1_();

Со второй строчкой также - только ещё инкремент указателя есть.

Вот этого не могу понять. Что это - приводиться к указателю? Приводится к типу понятно, а это выражение не пойму.
Go to the top of the page
 
+Quote Post
Harvester
сообщение Mar 8 2014, 16:01
Сообщение #9


Местный
***

Группа: Участник
Сообщений: 338
Регистрация: 1-02-06
Из: Королев, М.О.
Пользователь №: 13 846



Ужас какой-то. Это как раз тот случай, когда достоинства языка превращают в недостатки кода sm.gif
Вполне достаточно такого кода, все понятно и ничего лишнего:
Код
temp = w1_receive_byte();
temp <<= 8;
temp |= w1_receive_byte();

Хотя, если data[] - локальная переменная и больше нигде не используется, компилятор ее сам выкинет.


--------------------
-Да как так-то?/-Да как-то так/-Ну так-то да
Go to the top of the page
 
+Quote Post
aaarrr
сообщение Mar 8 2014, 16:02
Сообщение #10


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



Цитата(RW6MKA @ Mar 8 2014, 19:58) *
Вот этого не могу понять. Что это - приводиться к указателю? Приводится к типу понятно, а это выражение не пойму.

Есть тип char, есть тип "указатель на char" - вот к последнему и приводится.
Go to the top of the page
 
+Quote Post
RW6MKA
сообщение Mar 8 2014, 16:13
Сообщение #11


Частый гость
**

Группа: Участник
Сообщений: 163
Регистрация: 25-10-10
Из: Ростовская обл.
Пользователь №: 60 401



Цитата(aaarrr @ Mar 8 2014, 20:02) *
Есть тип char, есть тип "указатель на char" - вот к последнему и приводится.

Да, здесь в моих знаниях пробел.(( Спасибо всем за столь подробные объяснения. Чувствую, мне еще учить и учить))))
Еще раз всем спасибо.
Go to the top of the page
 
+Quote Post
Harvester
сообщение Mar 8 2014, 16:25
Сообщение #12


Местный
***

Группа: Участник
Сообщений: 338
Регистрация: 1-02-06
Из: Королев, М.О.
Пользователь №: 13 846



Цитата(RW6MKA @ Mar 8 2014, 19:58) *
Вот этого не могу понять. Что это - приводиться к указателю? Приводится к типу понятно, а это выражение не пойму.

Указатель - это просто переменная, хранящая адрес в памяти.
Temp - 2-байтная переменная. Если указатель на Temp равен A, то содержимое переменной лежит по адресам [A] и [A+1]. Если мы просто инкрементируем указатель, он станет равным A+2, т.е. будет указывать на следующую 2-байтную переменную. А поскольку нам надо писать "внутрь" переменной, мы приводим тип к указателю на char. Тем самым говорим компилятору, что при инкременте указателя, его значение нужно увеличить не на 2, а на 1. Это позволит нам выполнить запись по адресу [A+1].


--------------------
-Да как так-то?/-Да как-то так/-Ну так-то да
Go to the top of the page
 
+Quote Post
SSerge
сообщение Mar 8 2014, 16:33
Сообщение #13


Профессионал
*****

Группа: Свой
Сообщений: 1 719
Регистрация: 13-09-05
Из: Novosibirsk
Пользователь №: 8 528



Цитата(Harvester @ Mar 8 2014, 23:01) *
Ужас какой-то. Это как раз тот случай, когда достоинства языка превращают в недостатки кода sm.gif
Вполне достаточно такого кода, все понятно и ничего лишнего:
Код
temp = w1_receive_byte();
temp <<= 8;
temp |= w1_receive_byte();

Немного позанудствую.
С первой и второй строкой всё нормально, а вот с третьей...
Результат выполнения этого может быть разным в зависимости от типов данных переменной temp и типа, возвращаемого функцией w1_receive_byte(). Даже если он определён как char, это ещё ничего не гарантирует, в С char может быть как знаковым так и беззнаковым, отдано на откуп реализации.
Для конкретного компилятора может и прокатит, как правило сейчас char считается беззнаковым.
Но если заботиться о переносимости, то надёжнее явно привести к беззнаковому типу:

temp |= (unsigned char)w1_receive_byte();

или, ещё лучше и короче c использованием типов из stdint.h :
temp |= (uint8_t)w1_receive_byte();


--------------------
Russia est omnis divisa in partes octo.
Go to the top of the page
 
+Quote Post
RW6MKA
сообщение Mar 9 2014, 03:47
Сообщение #14


Частый гость
**

Группа: Участник
Сообщений: 163
Регистрация: 25-10-10
Из: Ростовская обл.
Пользователь №: 60 401



Цитата(Harvester @ Mar 8 2014, 20:25) *
Указатель - это просто переменная, хранящая адрес в памяти.
Temp - 2-байтная переменная. Если указатель на Temp равен A, то содержимое переменной лежит по адресам [A] и [A+1]. Если мы просто инкрементируем указатель, он станет равным A+2, т.е. будет указывать на следующую 2-байтную переменную. А поскольку нам надо писать "внутрь" переменной, мы приводим тип к указателю на char. Тем самым говорим компилятору, что при инкременте указателя, его значение нужно увеличить не на 2, а на 1. Это позволит нам выполнить запись по адресу [A+1].

То есть если бы мы сделали вот так
Код
((char)&Temp) = w1_receive_byte();//здесь просто адрес переменной привели к типу char и производим по нему запись т.к. переменная двух байтовая то адрес занимает грубо две ячейки и мы одну из них заняли.
((char)&Temp + 1) = w1_receive_byte();//в этой строке следующий адрес переменной приводим к типу char производим по нему запись, но запись получается уже по другим двум ячейкам.

в результате получаем запись двух байтов по двум разным адресам переменной.
А если приводить не к типу, а указателю на тип
Код
(char*)&Temp
и производить запись в указатель всего этого безобразия
Код
*((char*)&Temp)
, то мы записываем данные по, скажем, первой ячейке адреса, предназначенной для младшего байта, а при
Код
*((char*)&Temp+1)
по второй ячейке этого же адреса, предназначенной для старшего байта.
Я на правильном пути?
И ещё попутный вопрос. Правильно ли я выбрал тип переменной char если переменная это дробные числа имеющие разный знак? Может лучше использовать тип float?
вот функция целиком
Код
char temp_18b20(){//функция преобразует полученые с датчика 18b20 данные в температуру

    char temp = 0;
    if(TD_find()==1)//если устройство присутствует на шине
    {
        TD_sendcmd(0xcc);//пропустить ROM код, так ка датчик в устройстве один и не требуется идентификация
        TD_sendcmd(0x44);//команда датчику преобразовать температуру
        _delay_ms(750);//преобразование в 12 битном режиме занимает 750ms
        TD_find();//снова опрос присутствия и пропуск кода
        TD_sendcmd(0xcc);
        TD_sendcmd(0xbe);//команда датчику передать байты (у 18b20 в первых двух байтах содержится температура)
        //читаем два байта с температурой и записываем оба байта в двух байтовую переменную
        temp = (char)TD_receive_byte();
        temp = (temp << 8) | (char)TD_receive_byte();
        //переводим в градусы
        //пока кода нет, но в результате будут температурные данные
    }
    //возвращаем температуру
    return temp;
}
Go to the top of the page
 
+Quote Post
aaarrr
сообщение Mar 9 2014, 04:09
Сообщение #15


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



Цитата(RW6MKA @ Mar 9 2014, 07:47) *
То есть если бы мы сделали вот так
Код
((char)&Temp) = w1_receive_byte();//здесь просто адрес переменной привели к типу char и производим по нему запись т.к. переменная двух байтовая то адрес занимает грубо две ячейки и мы одну из них заняли.
((char)&Temp + 1) = w1_receive_byte();//в этой строке следующий адрес переменной приводим к типу char производим по нему запись, но запись получается уже по другим двум ячейкам.

в результате получаем запись двух байтов по двум разным адресам переменной.

Некоторая каша получилась.
1. По приведенному к типу char адресу переменной записать ничего нельзя, это и не скомпилируется.
Это примерно как написать: 5 = 10;
2. Размерность адреса никак не связана с размерностью переменной.
Нужно понимать, что адрес, полученный через "&", является просто числом, а не новой переменной.

Цитата(RW6MKA @ Mar 9 2014, 07:47) *
А если приводить не к типу, а указателю на тип
Код
(char*)&Temp
и производить запись в указатель всего этого безобразия
Код
*((char*)&Temp)
, то мы записываем данные по, скажем, первой ячейке адреса, предназначенной для младшего байта, а при
Код
*((char*)&Temp+1)
по второй ячейке этого же адреса, предназначенной для старшего байта.
Я на правильном пути?

Ага, на правильном.

Цитата(RW6MKA @ Mar 9 2014, 07:47) *
И ещё попутный вопрос. Правильно ли я выбрал тип переменной char если переменная это дробные числа имеющие разный знак? Может лучше использовать тип float?
вот функция целиком
Код
char temp_18b20(){//функция преобразует полученые с датчика 18b20 данные в температуру
char temp = 0;
if(TD_find()==1)//если устройство присутствует на шине
{
    ...
    temp = (char)TD_receive_byte();
    temp = (temp << 8) | (char)TD_receive_byte();
    //переводим в градусы
    //пока кода нет, но в результате будут температурные данные
    ...
    //возвращаем температуру
    return temp;
}

Переменная temp должна иметь тип как минимум short, чтобы в нее поместилось два char'а.
Float имеет смысл использовать на более высоком уровне, например:
Код
short temp_18b20()
{
    ...
}

int main(void)
{
    float t;

    t = (float)temp_18b20() / 16;
    pritnf("Temp = %1.2f\n", t);
}
Go to the top of the page
 
+Quote Post

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

 


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


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