Вопросы по С/С++ (СИ)

Если ваш вопрос не влез ни в одну из вышеперечисленных тем, вам сюда.
lix
Опытный кот
Сообщения: 703
Зарегистрирован: Вс янв 17, 2010 15:32:19
Откуда: Курган

Re: Вопросы по С/С++ (СИ)

Сообщение lix »

сделайте условное определение символов индикатора в зависимости от наличия определенного макроса инвертированности сегментов.

Код: Выделить всё

#ifdef INVERT
#define S_CHAR(x) (~(x))
#else
#define S_CHAR(x) (x)
#endif

#define let_1 S_CHAR(seg_b| seg_c)
...
Реклама
Аватара пользователя
uldemir
Друг Кота
Сообщения: 7360
Зарегистрирован: Пт авг 28, 2009 21:34:30
Откуда: 845-й км.

Re: Вопросы по С/С++ (СИ)

Сообщение uldemir »

Эээээ. Но это то же самое, что было у меня. Только такое написание изящнее. У меня-то просто грубая переделка файла, который я ранее использовал под gpasm (это для микрочиповских пиков). Всё-равно разворачивается в

Код: Выделить всё

~(seg_a|seg_b)
И вот это ~ похоже делает неявное преобразование к int.

Пока убрал ~, а строку записал так:

Код: Выделить всё

const uint8_t charset[16] = {let_0^0xff, let_1^0xff, let_2^0xff, let_3^0xff, \
Реклама
Аватара пользователя
ks0
Прорезались зубы
Сообщения: 238
Зарегистрирован: Чт фев 28, 2013 14:16:10

Re: Вопросы по С/С++ (СИ)

Сообщение ks0 »

Ну и сделали бы просто

Код: Выделить всё

const uint8_t charset[16] = {(uint8_t) let_0, (uint8_t) let_1...
А в дефайных приведение убрать вовсе. Нафиг этот колхоз с кучей битовых операций и приведений типов.
~ делает расширение до int только для 1<<7, который в char знаковый не влазит. Фиг знает, если честно, почему, мог бы и знаковый инвертировать.
Аватара пользователя
baron_P
Нашел транзистор. Понюхал.
Сообщения: 183
Зарегистрирован: Вт сен 14, 2010 23:07:10
Откуда: Ростов

Re: Вопросы по С/С++ (СИ)

Сообщение 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);  //не знаю, что тут выведится. Подозреваю, что выше много ошибок, и одна строка совсем непонятна 
We do what we must because we can (c) GLaDOS
Реклама
Эиком - электронные компоненты и радиодетали
OKF
Это не хвост, это антенна
Сообщения: 1400
Зарегистрирован: Вт июн 07, 2011 08:03:18

Re: Вопросы по С/С++ (СИ)

Сообщение 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];
Реклама
Аватара пользователя
WiseLord
Друг Кота
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение WiseLord »

baron_P писал(а):

Код: Выделить всё

int a = 1234567890;  //целая переменная а
int* pa = &a;  //указатель pa на целую переменную, в который записан адрес переменной a
Получается в последнем куске * все таки относится к int, а не к pa. По смыслу последняя строка это pa = &a, а не *pa = &a.
По смыслу - да, pa - указатель на int, поэтому казалось бы, правильнее объявлять именно так:

Код: Выделить всё

int* pa;
И никто не запрещает. Об этом, как выше писались, и ведутся споры. Но эта запись пасует в случае объявления уже нескольких указателей. Если записать.

Код: Выделить всё

int* pa, pb, pc;
То транслятор это воспримет именно как pa - указатель на целое, но pb и pc - как переменные. Ему вообще всё равно, есть ли там пробелы возле звёздочек, и с какой они стороны.
Поэтому более надёжно в плане исключения ошибок писать именно как

Код: Выделить всё

int *pa, *pb, *pc, a, b, c;
Тут уже без вопросов понятно, что pa, pb, pc - указатели, a, b, c - переменные. Хотя с точки зрения логики звёздочка просится правее.

Если так уж хочется - можно писать

Код: Выделить всё

int* pa;
int* pb;
int* pc;
int a, b, c;
но это более громоздко.
Реклама
OKF
Это не хвост, это антенна
Сообщения: 1400
Зарегистрирован: Вт июн 07, 2011 08:03:18

Re: Вопросы по С/С++ (СИ)

Сообщение OKF »

Ежели мне не изменяет память, в букварях рекомендуют объявление каждой переменной/указателя делать по одной на строке. Не?
lix
Опытный кот
Сообщения: 703
Зарегистрирован: Вс янв 17, 2010 15:32:19
Откуда: Курган

Re: Вопросы по С/С++ (СИ)

Сообщение lix »

OKF писал(а): Так не пойдёт? Не нужно кучи условий для препроцессора, ежели много инверсий.

Код: Выделить всё

#define INV_ATTR ~  //space, if not inverted
LED_DATA = INV_ATTR led_buf[0];
пойдет. только препроцессор один раз отработает, а МК постоянно надо инвертировать. может Вы препроцессор жалеете? дак не надо его жалеть, он для того и придуман, чтобы облечать работу.
OKF
Это не хвост, это антенна
Сообщения: 1400
Зарегистрирован: Вт июн 07, 2011 08:03:18

Re: Вопросы по С/С++ (СИ)

Сообщение OKF »

Я жалею внешний вид программы, коль вы не поняли.
Аватара пользователя
Siarzhuk
Потрогал лапой паяльник
Сообщения: 353
Зарегистрирован: Вс янв 19, 2014 22:41:55

Re: Вопросы по С/С++ (СИ)

Сообщение 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 указывают на одну и ту-же область памяти. :-) Только доходят они до нёё по-разному - напрямую либо через абсолютное смещение в памяти.
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! ;-)
Аватара пользователя
ks0
Прорезались зубы
Сообщения: 238
Зарегистрирован: Чт фев 28, 2013 14:16:10

Re: Вопросы по С/С++ (СИ)

Сообщение 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];
}
Компилируется в

Код: Выделить всё

ldi	r24, 0x28
out	0x17, r24
А вот это:

Код: Выделить всё

#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];
}
Компилируется в

Код: Выделить всё

ldi	r24, 0xD7
out	0x17, r24
И видно, что, хотя я добавил инвертирование, нет никакой разницы в объеме кода
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18637
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение ARV »

к теме про указатели.

нет никакого смысла приводить константу к какому-либо конкретному типу указателя, например (char *)0 - для этого вполне разумно будет использовать приведение к void* - типу указателя "куда-то там", он для этого и придуман. тип данных все равно будет определяться разименованием указателя, т.е. даже если я напишу int *ptr = (char*)0; - ничего страшнее warning я не получу
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
lix
Опытный кот
Сообщения: 703
Зарегистрирован: Вс янв 17, 2010 15:32:19
Откуда: Курган

Re: Вопросы по С/С++ (СИ)

Сообщение lix »

OKF писал(а):Я жалею внешний вид программы, коль вы не поняли.
В вашем примере меньше кода, но он требует больше умственных усилий, в отличии от моего.
В LED_DATA = INV_ATTR led_buf[0]; есть лишняя сущность это INV_ATTR; она нужна только для контроллера, прграммисту он ней можно не знать, ему достаточно знать, что мы выводим такой-то символ.
как он представлен, прямо или инверсно, здесь это не важно. это важно только когда мы определяем представления символов. в моем коде это выражено более наглядно, и из него видно что к чему, он самодокументирован.
В вашем случае, придется дописать коментарий о том что для инверсии символов необходимо определить INV_ATTR как ~, иначе оставить пустым. INV_ATTR должен быть определен в любом случае, иначе будет ошибка, или нужно править код вывода символов, что не является хорошей практикой.

ks0, рано думать о ней тоже плохо. может быть и не придется оптимизировать вовсе, если задача будет выполнена надлежащим образом.
Кстати ваш код скомпилировался одинаково лишь потому что он прост как 3 копейки, и оптимизатор сделал за вас очевидные вещи.
Аватара пользователя
Chip115
Сверлит текстолит когтями
Сообщения: 1132
Зарегистрирован: Пт фев 16, 2007 14:18:20
Откуда: Новосибирск
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение 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 байт. Не произойдет ли "затёрка" каких либо данных при таком выделении памяти?
Теория — это когда все известно, но ничего не работает. Практика — это когда все работает, но никто не знает почему. Мы же объединяем теорию и практику: ничего не работает… и никто не знает почему!
© Альберт Эйнштейн
lix
Опытный кот
Сообщения: 703
Зарегистрирован: Вс янв 17, 2010 15:32:19
Откуда: Курган

Re: Вопросы по С/С++ (СИ)

Сообщение lix »

Chip115, а может быть лучше передавать в функцию не массив, а односвязный список? Когда нашли новое устройство, добавиляем элемент в список. после этой функции по списку можно узнать сколько и какие устройства нашлись.

А какой у вас контроллер?
OKF
Это не хвост, это антенна
Сообщения: 1400
Зарегистрирован: Вт июн 07, 2011 08:03:18

Re: Вопросы по С/С++ (СИ)

Сообщение OKF »

lix писал(а): В вашем примере меньше кода, но он требует больше умственных усилий, в отличии от моего.
В LED_DATA = INV_ATTR led_buf[0]; есть лишняя сущность это INV_ATTR; она нужна только для контроллера, прграммисту он ней можно не знать, ему достаточно знать, что мы выводим такой-то символ.
как он представлен, прямо или инверсно, здесь это не важно. это важно только когда мы определяем представления символов. в моем коде это выражено более наглядно, и из него видно что к чему, он самодокументирован.
В вашем случае, придется дописать коментарий о том что для инверсии символов необходимо определить INV_ATTR как ~, иначе оставить пустым. INV_ATTR должен быть определен в любом случае, иначе будет ошибка, или нужно править код вывода символов, что не является хорошей практикой.
В целом согласен, но не думаю что здесь может быть однозначный ответ. В данном случае всё зависит от кол-ва инверсий в тексте. Если их много, тогда уже ваш пример потребует больше умственных усилий, ввиду нагромождения директив препроцессора. Либо же придётся дублировать участки программы для наглядности, что тоже имеет недостатки.
lix
Опытный кот
Сообщения: 703
Зарегистрирован: Вс янв 17, 2010 15:32:19
Откуда: Курган

Re: Вопросы по С/С++ (СИ)

Сообщение lix »

оперировать понятиями проще, нежели кусками кода. макрос должен определять какое-то действие, а не вырванный из контекста кода кусок.
MOHCTEP
Опытный кот
Сообщения: 768
Зарегистрирован: Вс янв 19, 2014 00:55:09

Re: Вопросы по С/С++ (СИ)

Сообщение 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 байт...
Извиняюсь за разбитое форматирование.
Аватара пользователя
baron_P
Нашел транзистор. Понюхал.
Сообщения: 183
Зарегистрирован: Вт сен 14, 2010 23:07:10
Откуда: Ростов

Re: Вопросы по С/С++ (СИ)

Сообщение baron_P »

Спасибо. Было некогда отвечать, только плюсов успел накидать.

Если исходить из того, что p[pa] == *(p+pa) (о чем я постоянно забываю), то становится понятнее смысл этого:

Код: Выделить всё

char* pc = &p[(int)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
Извиняюсь за такой тупизм на ровном месте, но что-то туго идет. Наверное, лучшим решением для меня будет обход стороной неочевидных операций с указателями. Может когда-нибудь потом оно в голове устаканится.
We do what we must because we can (c) GLaDOS
Аватара пользователя
Siarzhuk
Потрогал лапой паяльник
Сообщения: 353
Зарегистрирован: Вс янв 19, 2014 22:41:55

Re: Вопросы по С/С++ (СИ)

Сообщение Siarzhuk »

baron_P писал(а):Наверное, лучшим решением для меня будет обход стороной неочевидных операций с указателями. Может когда-нибудь потом оно в голове устаканится.
Обязательно устаканятся. Неочевидные операции просто прорабатывайте тщательно - например создайте тест-програмку на PC и погоняйте её в отладчике - чтобы убедиться что у вас сложилась правильное понимание того как работает та или иная конструкция. А после прохождения такого "квеста" ещё и удовольствие получите. ;-)
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! ;-)
Ответить

Вернуться в «Разные вопросы по МК»