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

Если ваш вопрос не влез ни в одну из вышеперечисленных тем, вам сюда.
Ответить
OKF
Это не хвост, это антенна
Сообщения: 1452
Зарегистрирован: Вт июн 07, 2011 08:03:18

Сообщение OKF »

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

Сообщение 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

Сообщение 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
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18787
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

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

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

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

Сообщение lix »

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

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

Сообщение 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
Откуда: Курган

Сообщение lix »

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

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

Сообщение OKF »

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

Сообщение lix »

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

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

Сообщение 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
Потрогал лапой паяльник
Аватара пользователя
Сообщения: 353
Зарегистрирован: Вс янв 19, 2014 22:41:55

Сообщение Siarzhuk »

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

Сообщение slavokhire5 »

Здравствуйте:) Нубский вопрос по Си :)
У меня есть задача - отображать на меню дисплея. каждому элементу меню соответствует своя переменная. меню на дисплей вывожу так:

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

lcd_str(0x80, &menu[menu_index]);
переменную вывожу так:

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

if(menu_index == 0) {lcd_var(var_0);}
else if(menu_index == 1) {lcd_var(var_1);}
....
и таких строк прилично. реально переменные названы более удобными именами :) как бы их так упаковать, чтобы можно было их передавать в функцию вывода на дисплей по номеру элемента меню, без всяких проверок, но при этом чтобы сохранялись удобные для меня имена этих переменных для других случаев. сделать упакованную структуру и обращаться к переменной для вывода по адресу + приращение? но это как-то сильно через попу)
Осилит дорогу идущий
--------------------------
Пишу на Си за еду
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18787
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

я верно понял, что переменная - это как бы связанный с пунктом меню параметр? ну так и сделайте структуру, описывающую пункт меню, в нее поместите и название пункта, и переменную (или указатель на переменную), и указатель на функцию изменения этой переменной, и что хотите еще... а потом сделайте массив этих структур, и тогда все действия будут только через индекс этого массива...
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Друг Кота
Аватара пользователя
Сообщения: 4468
Зарегистрирован: Вс янв 24, 2010 19:19:52
Откуда: Главный Улей России (Moscow)

Сообщение DX168B »

Господа коты. Есть вопрос по С++.

Имеем стандартный связанный список и некий класс под именем MyClass
Структура (элемент списка) типа такой:

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

typedef struct _tag_ListObject
{
    pListOpject next;
    pListObject prev;
    int listId;
    MyClass *myClass;
} ListObject, *pListObject;
Я создаю элемент списка и объект класса, выделяя память из кучи:

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

pListObject listObject = new ListObject;
listObject->myClass = new MyClass;
// и т.д.
Как корректно освободить память, если в дальнейшем нужно будет этот элемент удалить?
Сначала удалить объект класса и за тем элемент списка, или достаточно будет удалить только элемент списка?

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

delete listObject->myClass; // сносим объект класса MyClass
delete listObject; // сносим элемент списка
И вызовется ли при удалении объекта его деструктор, если вместо поля MyClass* в структуре ListObject, будет стоять указатель void*?

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

MyClass::~MyClass();
I am DX168B and this is my favourite forum on internet!
Контактная информация:
Модератор
Аватара пользователя
Сообщения: 4618
Зарегистрирован: Чт мар 18, 2010 23:09:57
Откуда: Планета Земля

Сообщение Аlex »

Сначала удалить объект класса и за тем элемент списка, или достаточно будет удалить только элемент списка?
Удалять нужно всегда в обратном направлении созданию.
И вызовется ли при удалении объекта его деструктор, если вместо поля MyClass* в структуре ListObject, будет стоять указатель void*?
Даже если там будет не void, он всё равно не вызовется. Там же находится, всего-лишь, указатель на объект, а не сам объект. Т.е. какая-то циферка :)
С какой стати ему удалять объект по этому указателю. Может Вы его где уже скопировали и будете потом по нему удалять.
Но если никаких ссылок на этот объект не остаётся, то, само собой разумеется, нужно удалить объект по ссылке, а потом уже удалять её. Иначе потом не будет возможности "добраться" до него, и он так и останется валяться в куче. Пока какой-нибудь сборщик мусора не дойдёт до него...
Друг Кота
Аватара пользователя
Сообщения: 4468
Зарегистрирован: Вс янв 24, 2010 19:19:52
Откуда: Главный Улей России (Moscow)

Сообщение DX168B »

Ну дык в С++ оператор delete - это не аналог Сишного free()
Мы же передаем оператору delete указатель на класс. То есть, передаем объект имеющий некий общий тип. (класс, а не массив)
А это значит, что оператор автоматом должен вызвать сначала MyClass::~MyClass() и и только потом free()
Если бы оператору передали обычный массив, то оператор вызвал бы только free()
Или я чего-то не понимаю?
I am DX168B and this is my favourite forum on internet!
Контактная информация:
Модератор
Аватара пользователя
Сообщения: 4618
Зарегистрирован: Чт мар 18, 2010 23:09:57
Откуда: Планета Земля

Сообщение Аlex »

DX168B писал(а):Мы же передаем оператору delete указатель на класс.
Ну вот и вызывайте его, передавая указатель. У Вас вопрос то в другом - вызовется ли деструктор автоматом, при удалении ссылки (указателя) на класс. Ответ - нет, не вызовется. Т.к. Вы будете удалять из памяти всего-лишь указатель, а не сам объект.
Друг Кота
Аватара пользователя
Сообщения: 4468
Зарегистрирован: Вс янв 24, 2010 19:19:52
Откуда: Главный Улей России (Moscow)

Сообщение DX168B »

Я имел в виду на счет этого:
Alex писал(а): Даже если там будет не void, он всё равно не вызовется.
Когда статья в MSDN говорит совсем о другом.
MSDN писал(а): Если оператор delete используется для отмены выделения памяти объекту класса C++, то перед отменой выделения памяти объекта вызывается деструктор этого объекта (если у объекта имеется деструктор).
На счет void* я имел в виду это:

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

typedef struct _tag_ListObject
{
    pListOpject next;
    pListObject prev;
    int listId;
    void *listData;
} ListObject, *pListObject;
Интуиция подсказывает, что сносить объект в случае с void надо так:

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

pListObject listObject = ...... //находим нужную структуру и присваиваем ее адрес
...
MyClass *myClass = (MyClass*)listObject->listData;
delete myClass;
delete listObject;
 
UPD:
Ладно. Сам проверю.
I am DX168B and this is my favourite forum on internet!
Контактная информация:
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18787
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

я, конечно, в плюсах не силен, но по-моему, правильно будет так:

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

delete (myClass*)listObject->listData;
а еще правильнее вместо структуры ListObject делать класс ListObject, а в его деструкторе освобождать память из-под listData. кстати, если listData указывает на экземпляр какого-то класса, то и соответствующее поле должно иметь правильный тип - указатель на этот самый класс, а не void*

имхо, либо ООП, либо обычный Си, помесь всегда уродлива...
Последний раз редактировалось ARV Вс мар 15, 2015 19:17:45, всего редактировалось 2 раза.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Ответить

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