Страница 216 из 386
Re: Вопросы по С/С++ (СИ)
Добавлено: Вс мар 01, 2015 18:54:53
lix
сделайте условное определение символов индикатора в зависимости от наличия определенного макроса инвертированности сегментов.
Код: Выделить всё
#ifdef INVERT
#define S_CHAR(x) (~(x))
#else
#define S_CHAR(x) (x)
#endif
#define let_1 S_CHAR(seg_b| seg_c)
...
Re: Вопросы по С/С++ (СИ)
Добавлено: Вс мар 01, 2015 21:25:46
uldemir
Эээээ. Но это то же самое, что было у меня. Только такое написание изящнее. У меня-то просто грубая переделка файла, который я ранее использовал под gpasm (это для микрочиповских пиков). Всё-равно разворачивается в
И вот это
~ похоже делает неявное преобразование к int.
Пока убрал
~, а строку записал так:
Код: Выделить всё
const uint8_t charset[16] = {let_0^0xff, let_1^0xff, let_2^0xff, let_3^0xff, \
Re: Вопросы по С/С++ (СИ)
Добавлено: Пн мар 02, 2015 04:55:32
ks0
Ну и сделали бы просто
Код: Выделить всё
const uint8_t charset[16] = {(uint8_t) let_0, (uint8_t) let_1...
А в дефайных приведение убрать вовсе. Нафиг этот колхоз с кучей битовых операций и приведений типов.
~ делает расширение до int только для 1<<7, который в char знаковый не влазит. Фиг знает, если честно, почему, мог бы и знаковый инвертировать.
Re: Вопросы по С/С++ (СИ)
Добавлено: Пн мар 02, 2015 08:20:43
baron_P
Выходные закончились, есть время С поковырять
Я имел в виду, что с числом, которое записано в указатель, обращаться нужно не совсем как с обычным числом (
WiseLord об этом написал).
Но своим примером вы меня опять разубедили в понимании.
Код: Выделить всё
char a; //символьная переменная а
char *b; //указатель на символьную переменную
b = &a; //указатель b содержит адрес переменной а, то бишь какое-то двухбайтовое (для ATMega) число
Все понятно. А в вашем как-то не то чтобы очень.
Код: Выделить всё
int a = 1234567890; //целая переменная а
int* pa = &a; //указатель pa на целую переменную, в который записан адрес переменной a
Получается в последнем куске * все таки относится к int, а не к pa. По смыслу последняя строка это pa = &a, а не *pa = &a.
Код: Выделить всё
char* p = (char*)0; //указатель p на символьную переменную, в который записан 0, т.е. указатель на нулевую ячейку памяти.
//Зачем перед 0 операция приведия типа?
char* pc = &p[(int)pa]; //указатель pc на символьную переменную, в который записан адрес указателя p, смещенный на адрес в указателе pa.
//Здесь приведение типа т.к. для разных архитектур тип указателя будет разным. Для ATMega приведение не нужно, т.к. он тут и так двухбайтовый.
//Но, с другой стороный, зачем вообще приводить тип индекса к int, разве нельзя в качестве индекса использовать char или long int?
//В общем, что должно записатся в pc я не понял. & дает адрес переменной, а что она даст, если вместо переменной стоит другой адрес?
int* pi = (int*)pc; //в указатель pi записано значение указателя pc. Приведение типа т.к. pc объявлен был как char*.
printf("%d или %d\n", *pa, *pi); //не знаю, что тут выведится. Подозреваю, что выше много ошибок, и одна строка совсем непонятна
Re: Вопросы по С/С++ (СИ)
Добавлено: Пн мар 02, 2015 08:33:10
OKF
lix писал(а):сделайте условное определение символов индикатора в зависимости от наличия определенного макроса инвертированности сегментов.
Код: Выделить всё
#ifdef INVERT
#define S_CHAR(x) (~(x))
#else
#define S_CHAR(x) (x)
#endif
#define let_1 S_CHAR(seg_b| seg_c)
...
Так не пойдёт? Не нужно кучи условий для препроцессора, ежели много инверсий.
Код: Выделить всё
#define INV_ATTR ~ //space, if not inverted
...
LED_DATA = INV_ATTR led_buf[0];
Re: Вопросы по С/С++ (СИ)
Добавлено: Пн мар 02, 2015 08:40:05
WiseLord
baron_P писал(а):
Код: Выделить всё
int a = 1234567890; //целая переменная а
int* pa = &a; //указатель pa на целую переменную, в который записан адрес переменной a
Получается в последнем куске * все таки относится к int, а не к pa. По смыслу последняя строка это pa = &a, а не *pa = &a.
По смыслу - да, pa - указатель на int, поэтому казалось бы, правильнее объявлять именно так:
И никто не запрещает. Об этом, как выше писались, и ведутся споры. Но эта запись пасует в случае объявления уже нескольких указателей. Если записать.
То транслятор это воспримет именно как pa - указатель на целое, но pb и pc - как переменные. Ему вообще всё равно, есть ли там пробелы возле звёздочек, и с какой они стороны.
Поэтому более надёжно в плане исключения ошибок писать именно как
Тут уже без вопросов понятно, что pa, pb, pc - указатели, a, b, c - переменные. Хотя с точки зрения логики звёздочка просится правее.
Если так уж хочется - можно писать
но это более громоздко.
Re: Вопросы по С/С++ (СИ)
Добавлено: Пн мар 02, 2015 08:48:39
OKF
Ежели мне не изменяет память, в букварях рекомендуют объявление каждой переменной/указателя делать по одной на строке. Не?
Re: Вопросы по С/С++ (СИ)
Добавлено: Пн мар 02, 2015 08:58:16
lix
OKF писал(а):
Так не пойдёт? Не нужно кучи условий для препроцессора, ежели много инверсий.
Код: Выделить всё
#define INV_ATTR ~ //space, if not inverted
LED_DATA = INV_ATTR led_buf[0];
пойдет. только препроцессор один раз отработает, а МК постоянно надо инвертировать. может Вы препроцессор жалеете? дак не надо его жалеть, он для того и придуман, чтобы облечать работу.
Re: Вопросы по С/С++ (СИ)
Добавлено: Пн мар 02, 2015 09:01:34
OKF
Я жалею внешний вид программы, коль вы не поняли.
Re: Вопросы по С/С++ (СИ)
Добавлено: Пн мар 02, 2015 10:14:01
Siarzhuk
baron_P писал(а):Все понятно. А в вашем как-то не то чтобы очень.
Код: Выделить всё
int a = 1234567890; //целая переменная а
int* pa = &a; //указатель pa на целую переменную, в который записан адрес переменной a
Получается в последнем куске * все таки относится к int, а не к pa. По смыслу последняя строка это pa = &a, а не *pa = &a.
Записи "int *" и "int*" равнозначны. Всего лишь вопрос используемого мной кодестайла, в котором "int*" это тип потому пишется вместе, однострочные объявления нескольких переменных не приветствуются а с громоздкостью объявлений борются размазыванием их по местам их непосредственного использования.
Мне кажется, вам будет проще если всякое выражение будете рассматривать целиком а не вырванным из контекста. Так например вышенаписанное "*pa = &a" - это запись адреса переменной "a" по адресу указываемому указателем "pa" а совсем не объявление указателя с инициализацией его в адрес переменной "а" (т.е. int *pa = &a) .
baron_P писал(а):Код: Выделить всё
char* p = (char*)0; //указатель p на символьную переменную, в который записан 0, т.е. указатель на нулевую ячейку памяти.
//Зачем перед 0 операция приведия типа?
Чтобы компилятор не ругался. Всякое несоответствие типов для него неожиданность и предмет для подозрений вас в недобрых намерениях либо в невнимательности. Операция непосредственного присвоения указателю численной константы исключительно опасна в рамках языка высокого уровня где пользователю в общем-то ни к чему знать и использовать такие подробности. Компилятор не может проверить корректность операции и подобно прилежному солдату ждёт приказа от вышестоящей инстанции (санкции на явное приведение типа) на исполнение этой операции.
baron_P писал(а):Код: Выделить всё
char* pc = &p[(int)pa]; //указатель pc на символьную переменную, в который записан адрес указателя p, смещенный на адрес в указателе pa.
//Здесь приведение типа т.к. для разных архитектур тип указателя будет разным. Для ATMega приведение не нужно, т.к. он тут и так двухбайтовый.
//Но, с другой стороный, зачем вообще приводить тип индекса к int, разве нельзя в качестве индекса использовать char или long int?
Приведение типа нужно по той-же причине что и выше - как компилятору не позволено неявно превращать число в указатель, так ему нельзя и указатель неявно превращать в число. Индекс массива должен быть целым числом - исопользование именно int не критично - насчёт char и long вы правы - но, подозреваю неявно они приведутся всё к тому-же int - разве что в случае long вас предупредят о "потере точности".
baron_P писал(а):Код: Выделить всё
//В общем, что должно записатся в pc я не понял. & дает адрес переменной, а что она даст, если вместо переменной стоит другой адрес?
Найдите и распечатайте табличку приоритетов операций чтобы всегда была под рукой. Операция "array subscripting" имеет более высокий приоритет чем "address of" - следовательно сначала будет "извлечено" значение по индексу "pa" в массиве байтов "p" а затем взят его адрес и помещён в "pc". Операция в общем-то бессмысленная - чисто для иллюстрации. А "что она даст если вместо переменной стоит другой адрес" - если под "адрес" здесь понимается указатель - то адрес переменной-указателя. В данном случае она применяестя не к указателю типа (char*) а к переменной типа (char) - т.е. к элементу массива p[pa] который имеет тип char - потому и требуется взять адрес этого элемента.
baron_P писал(а):Код: Выделить всё
int* pi = (int*)pc; //в указатель pi записано значение указателя pc. Приведение типа т.к. pc объявлен был как char*.
Поскольку разыменование char* даст нам всего один байт а мы собираемся сравнить int-ы. Посему волюнтаристскими методами приказываем считать этот указатель типом int*. Тогда компилятор при разыменовании возьмёт соотвествующее количество байт:
Код: Выделить всё
int a = 0x12345678;
int* pi = &a;
char* pc = (char*)pi;
printf("ptr to int: %x; ptr to char: %x", *pi, *pc); // вывод: "ptr to int: 12345678; ptr to char: 12"
NB: В зависимости от endian-ности платформы последнее число может быть и 78
baron_P писал(а):Код: Выделить всё
printf("%d или %d\n", *pa, *pi); //не знаю, что тут выведится. Подозреваю, что выше много ошибок, и одна строка совсем непонятна
Постить такую эквилибристику без проверки в тест-программе было-бы с моей стороны весьма самонадеянно

Там выво́дится одно и то же число - поскольку указатели pa и pi указывают на одну и ту-же область памяти.

Только доходят они до нёё по-разному - напрямую либо через абсолютное смещение в памяти.
Re: Вопросы по С/С++ (СИ)
Добавлено: Пн мар 02, 2015 10:32:43
ks0
А кстати да, препроцессор препроцессором, а про оптимизацию при компиляции тоже не стоит забывать. Например вот это:
Код: Выделить всё
#define let_1 (seg_b | seg_c)
#define let_2 (seg_a|seg_b|seg_d|seg_e|seg_g)
const uint8_t charset[] = {let_1, let_2};
void main()
{
DDRB=charset[0];
}
Компилируется в
А вот это:
Код: Выделить всё
#define let_1 (seg_b | seg_c)
#define let_2 (seg_a|seg_b|seg_d|seg_e|seg_g)
const uint8_t charset[] = {let_1, let_2};
void main()
{
DDRB=~charset[0];
}
Компилируется в
И видно, что, хотя я добавил инвертирование, нет никакой разницы в объеме кода
Re: Вопросы по С/С++ (СИ)
Добавлено: Пн мар 02, 2015 11:41:57
ARV
к теме про указатели.
нет никакого смысла приводить константу к какому-либо конкретному типу указателя, например (char *)0 - для этого вполне разумно будет использовать приведение к void* - типу указателя "куда-то там", он для этого и придуман. тип данных все равно будет определяться разименованием указателя, т.е. даже если я напишу int *ptr = (char*)0; - ничего страшнее warning я не получу
Re: Вопросы по С/С++ (СИ)
Добавлено: Пн мар 02, 2015 17:07:32
lix
OKF писал(а):Я жалею внешний вид программы, коль вы не поняли.
В вашем примере меньше кода, но он требует больше умственных усилий, в отличии от моего.
В LED_DATA = INV_ATTR led_buf[0]; есть лишняя сущность это INV_ATTR; она нужна только для контроллера, прграммисту он ней можно не знать, ему достаточно знать, что мы выводим такой-то символ.
как он представлен, прямо или инверсно, здесь это не важно. это важно только когда мы определяем представления символов. в моем коде это выражено более наглядно, и из него видно что к чему, он самодокументирован.
В вашем случае, придется дописать коментарий о том что для инверсии символов необходимо определить INV_ATTR как ~, иначе оставить пустым. INV_ATTR должен быть определен в любом случае, иначе будет ошибка, или нужно править код вывода символов, что не является хорошей практикой.
ks0, рано думать о ней тоже плохо. может быть и не придется оптимизировать вовсе, если задача будет выполнена надлежащим образом.
Кстати ваш код скомпилировался одинаково лишь потому что он прост как 3 копейки, и оптимизатор сделал за вас очевидные вещи.
Re: Вопросы по С/С++ (СИ)
Добавлено: Пн мар 02, 2015 17:11:24
Chip115
Всем привет! В общем, вожусь с DS1820. Лень было писать ф-цию, которая ищет подключенные устройства 1-wire, считывает серийники (8 байт) у всех устройств. Взял готовую ф-цию из апноута от Maxim, подкинул свои функции для работы по 1-wire и все заработало.
Вот эта функция.
Код: Выделить всё
cnt = 0;
rslt = OWFirst(); // Тут эти 8 байт помещаются в массив ROM_NO
while (rslt)
{
// print device found
for (i = 7; i >= 0; i--) // Тут тупо в терминал выводим содержимое ROM_NO
printf("%02X", ROM_NO[i]); //
printf(" %d\n",++cnt); // тут выводим порядковый номер найденного датчика
rslt = OWNext(); // Тут ищем еще подключенные устройства. Если нашли, то опять помещаем инфу в ROM_NO
// и всё сначала... пока все 1-wire устройства не будут найдены.
}
Функция опрашивает 1-wire линию, если что-то нашел, то заносит серийник (и код семейства, и CRC, в общем все 8 байт) в массив, затем пытается найти остальные устройства на линии, если они есть.
В итоге, при последнем прохождении цикла, в массиве ROM_NO имеем содержимое ROM датчика (последнего) и его номер .
Вот что в терминале в случае с двумя датчиками.
57000002D7B2BD28 1
45000002D7AA7728 2
Вопросы.
Как сделать так, что бы серийники подключенных датчиков складывались в один массив, который будет передан этой функции в качестве аргумента? Не делать же массив, длиной в 127*8 байт (127 взял от балды ибо изначально не известно сколько датчиков будет подключено)?
Задумал заюзать динамический массив, но как его можно передать в качестве аргумента? Думаю можно, но дин. массив - это же выделение места под 8 байт, затем если нашлось еще устройство на 1-wire, значит копируем инфу от предыдущего датчика, грохаем массив и выделяем 16 байт, возвращаем данные и суём новые. И так дальше, для n устройств выделяем n*8 байт. Не произойдет ли "затёрка" каких либо данных при таком выделении памяти?
Re: Вопросы по С/С++ (СИ)
Добавлено: Пн мар 02, 2015 17:26:53
lix
Chip115, а может быть лучше передавать в функцию не массив, а односвязный список? Когда нашли новое устройство, добавиляем элемент в список. после этой функции по списку можно узнать сколько и какие устройства нашлись.
А какой у вас контроллер?
Re: Вопросы по С/С++ (СИ)
Добавлено: Пн мар 02, 2015 17:36:06
OKF
lix писал(а):
В вашем примере меньше кода, но он требует больше умственных усилий, в отличии от моего.
В LED_DATA = INV_ATTR led_buf[0]; есть лишняя сущность это INV_ATTR; она нужна только для контроллера, прграммисту он ней можно не знать, ему достаточно знать, что мы выводим такой-то символ.
как он представлен, прямо или инверсно, здесь это не важно. это важно только когда мы определяем представления символов. в моем коде это выражено более наглядно, и из него видно что к чему, он самодокументирован.
В вашем случае, придется дописать коментарий о том что для инверсии символов необходимо определить INV_ATTR как ~, иначе оставить пустым. INV_ATTR должен быть определен в любом случае, иначе будет ошибка, или нужно править код вывода символов, что не является хорошей практикой.
В целом согласен, но не думаю что здесь может быть однозначный ответ. В данном случае всё зависит от кол-ва инверсий в тексте. Если их много, тогда уже ваш пример потребует больше умственных усилий, ввиду нагромождения директив препроцессора. Либо же придётся дублировать участки программы для наглядности, что тоже имеет недостатки.
Re: Вопросы по С/С++ (СИ)
Добавлено: Пн мар 02, 2015 18:11:03
lix
оперировать понятиями проще, нежели кусками кода. макрос должен определять какое-то действие, а не вырванный из контекста кода кусок.
Re: Вопросы по С/С++ (СИ)
Добавлено: Ср мар 04, 2015 02:54:48
MOHCTEP
Добрый час! Друзья, просветите пожалуйста по системе вызова функций. Почему обращение к одной и той-же процедуре, расположенной в соседнем файле, отъедает, то 60 байт флеша, то ожидаемые несколько? К примеру:
Код: Выделить всё
void renew_screen(uint_fast8_t scr_id){// flash ram
// "тело" закомментировано 1736 58
lcd_clear(); //1740 58
switch(scr_id){
case 1:{ //1740 58
uint_fast8_t *p; //1740 58
char ico[2][2]={{2,3},{4,5}}, b[16]; //1740 58
//1 line
lcd_block(ico[0],2); //1798 58 58 байт занял вызов
p=&rom[0][0]; //1798 58
eeprom_read_block(b, (const void*)(int)*p,*(p+1)); //1816 58
b[0]=light_state&(1<<UV_TOP)?1:0; //1816 58
b[5]=light_state&(1<<UV_BOT)?1:0; //1816 58
b[10]=light_state&(1<<RED)?1:0; //1816 58
lcd_block(b,*(p+1)-1); //1862 58 а здесь - 46 байт
//2 line
lcd_cmd(0xC0); //1866 58
//lcd_gotoxy(0,1); //1884 58
lcd_block(ico[1],2); //1892 58 тут вообще всего 8 байт...
Извиняюсь за разбитое форматирование.
Re: Вопросы по С/С++ (СИ)
Добавлено: Ср мар 04, 2015 07:49:02
baron_P
Спасибо. Было некогда отвечать, только плюсов успел накидать.
Если исходить из того, что p[pa] == *(p+pa) (о чем я постоянно забываю), то становится понятнее смысл этого:
И можно проанализировать ваш пример:
Код: Выделить всё
int a = 1234567890;
int* pa = &a; //*pa == 1234567890
char* p = (char*)0; //p == 0
char* pc = &p[(int)pa]; //pc == p+pa == pa, но тут pc указатель на char, а pa указатель на int.
//И тогда *pc == 0b11010010, т.е. младшие 8 бит числа 1234567890, содержащегося в *pa
int* pi = (int*)pc; //pi == pc, но с преобразованием типа
printf("%d или %d\n", *pa, *pi); //благодаря преобразованию выведится: 1234567890 или 1234567890
Извиняюсь за такой тупизм на ровном месте, но что-то туго идет. Наверное, лучшим решением для меня будет обход стороной неочевидных операций с указателями. Может когда-нибудь потом оно в голове устаканится.
Re: Вопросы по С/С++ (СИ)
Добавлено: Ср мар 04, 2015 09:27:58
Siarzhuk
baron_P писал(а):Наверное, лучшим решением для меня будет обход стороной неочевидных операций с указателями. Может когда-нибудь потом оно в голове устаканится.
Обязательно устаканятся. Неочевидные операции просто прорабатывайте тщательно - например создайте тест-програмку на PC и погоняйте её в отладчике - чтобы убедиться что у вас сложилась правильное понимание того как работает та или иная конструкция. А после прохождения такого "квеста" ещё и удовольствие получите.
