|
Что означает этот код? |
|
|
|
Mar 8 2014, 15:05
|
Частый гость
 
Группа: Участник
Сообщений: 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(); Вот никак не могу понять смысла этого кода. Я так понимаю что * и & это операции над указателями , а () приведение к типу но.... Вообщем если не трудно объясните начинающему подробно смысл сей конструкции.
|
|
|
|
|
Mar 8 2014, 15:15
|
Гуру
     
Группа: Свой
Сообщений: 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 байт.Во второй строчке - пишется ещё байт по адресу следующего байта.
|
|
|
|
|
Mar 8 2014, 15:36
|
Гуру
     
Группа: Свой
Сообщений: 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().
|
|
|
|
|
Mar 8 2014, 15:37
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Цитата(RW6MKA @ Mar 8 2014, 19:29)  А более подробно можно Берётся адрес Temp: &Temp приводится к указателю на char: (char *)(полученный адрес) по указателю на char делается запись того что вернёт функция: *(указатель на char)=w1_(); Со второй строчкой также - только ещё инкремент указателя есть.
|
|
|
|
|
Mar 8 2014, 15:54
|
Частый гость
 
Группа: Участник
Сообщений: 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. то есть получаем эти два байта записанные в соседних адресах или я что то путаю?
|
|
|
|
|
Mar 8 2014, 15:57
|
Гуру
     
Группа: Свой
Сообщений: 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. то есть получаем эти два байта записанные в соседних адресах или я что то путаю? Да, так.
|
|
|
|
|
Mar 8 2014, 15:58
|
Частый гость
 
Группа: Участник
Сообщений: 163
Регистрация: 25-10-10
Из: Ростовская обл.
Пользователь №: 60 401

|
Цитата(_Артём_ @ Mar 8 2014, 19:37)  Берётся адрес Temp: &Temp приводится к указателю на char: (char *)(полученный адрес) по указателю на char делается запись того что вернёт функция: *(указатель на char)=w1_();
Со второй строчкой также - только ещё инкремент указателя есть. Вот этого не могу понять. Что это - приводиться к указателю? Приводится к типу понятно, а это выражение не пойму.
|
|
|
|
|
Mar 8 2014, 16:01
|
Местный
  
Группа: Участник
Сообщений: 338
Регистрация: 1-02-06
Из: Королев, М.О.
Пользователь №: 13 846

|
Ужас какой-то. Это как раз тот случай, когда достоинства языка превращают в недостатки кода  Вполне достаточно такого кода, все понятно и ничего лишнего: Код temp = w1_receive_byte(); temp <<= 8; temp |= w1_receive_byte(); Хотя, если data[] - локальная переменная и больше нигде не используется, компилятор ее сам выкинет.
--------------------
-Да как так-то?/-Да как-то так/-Ну так-то да
|
|
|
|
|
Mar 8 2014, 16:13
|
Частый гость
 
Группа: Участник
Сообщений: 163
Регистрация: 25-10-10
Из: Ростовская обл.
Пользователь №: 60 401

|
Цитата(aaarrr @ Mar 8 2014, 20:02)  Есть тип char, есть тип "указатель на char" - вот к последнему и приводится. Да, здесь в моих знаниях пробел.(( Спасибо всем за столь подробные объяснения. Чувствую, мне еще учить и учить)))) Еще раз всем спасибо.
|
|
|
|
|
Mar 8 2014, 16:25
|
Местный
  
Группа: Участник
Сообщений: 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].
--------------------
-Да как так-то?/-Да как-то так/-Ну так-то да
|
|
|
|
|
Mar 8 2014, 16:33
|
Профессионал
    
Группа: Свой
Сообщений: 1 719
Регистрация: 13-09-05
Из: Novosibirsk
Пользователь №: 8 528

|
Цитата(Harvester @ Mar 8 2014, 23:01)  Ужас какой-то. Это как раз тот случай, когда достоинства языка превращают в недостатки кода  Вполне достаточно такого кода, все понятно и ничего лишнего: Код 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.
|
|
|
|
|
Mar 9 2014, 03:47
|
Частый гость
 
Группа: Участник
Сообщений: 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; }
|
|
|
|
|
Mar 9 2014, 04:09
|
Гуру
     
Группа: Свой
Сообщений: 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); }
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|