Страница 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 (это для микрочиповских пиков). Всё-равно разворачивается в

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

~(seg_a|seg_b)
И вот это ~ похоже делает неявное преобразование к 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, поэтому казалось бы, правильнее объявлять именно так:

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

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;
но это более громоздко.

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

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

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
И видно, что, хотя я добавил инвертирование, нет никакой разницы в объеме кода

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) (о чем я постоянно забываю), то становится понятнее смысл этого:

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

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
Извиняюсь за такой тупизм на ровном месте, но что-то туго идет. Наверное, лучшим решением для меня будет обход стороной неочевидных операций с указателями. Может когда-нибудь потом оно в голове устаканится.

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

Добавлено: Ср мар 04, 2015 09:27:58
Siarzhuk
baron_P писал(а):Наверное, лучшим решением для меня будет обход стороной неочевидных операций с указателями. Может когда-нибудь потом оно в голове устаканится.
Обязательно устаканятся. Неочевидные операции просто прорабатывайте тщательно - например создайте тест-програмку на PC и погоняйте её в отладчике - чтобы убедиться что у вас сложилась правильное понимание того как работает та или иная конструкция. А после прохождения такого "квеста" ещё и удовольствие получите. ;-)