Функция сортировки структуры

Лучше Вы… Заодно по данному примеру пройтись, ведь у него элементы массива это структура…
Если Вам не сильно сложно…

Я бы сказал, что не так Вы сделали когда в функции compare преобразуете указатели на к commandLine *, а зачем-то к uint16_t *. Оно понятно, что первый элемент структуры там имеет тип uint16_t *, но это говнокод. Преобразовывать надо к указателю на структуру и брать её элемент.

1 лайк

А вот это у меня и не получилось… Постоянно ошибки при компиляции…
Без Вашего разъяснения никак… :slight_smile:

За основу брать код из №85 или другой какой?

Думаю да.

Сразу хотел написать что @ЕвгенийП за такой код всем по ж…е надаёт …

2 лайка

Хорошо, сегодня сделаю. Но без объяснения как работает qsort - не прокатит, так что это займёт некоторое время. Возможно, я отдельным постом оформлю.

2 лайка

До этого я дошёл)) Но это же не всё… Впрочем, тут уже поговорили об этом

Я верю, что пятница «захлестнула» Евгения Петровича положительными эмоциями, а не то чтобы он забыл )

С другой стороны «сегодня» еще не закончилось ))

ЗЫ: Просто жду с нетерпением, простите.)

Как обещал.

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

Но, надо понимать, что она понятия не имеет, что именно она сортирует и ей просто неоткуда знать какой элемент должен в результате оказаться ближе к началу массива, а какой – к концу. Для этого программист должен предоставить функцию сравнения. Прототип функции выглядит так:

int <function name> (const void * a, const void * b);

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

Функция сравнения должна вернуть:

  • число меньшее нуля, если элемент a должен стоять раньше элемента b;
  • число большее нуля, если элемент a должен стоять позже элемента b;
  • ноль, если пофиг как будут стоять элементы (они равны).

Это всё. Больше никакой магии. Дайте qsort такую функцию сравнения и она отсортирует всё, что угодно.

Несколько примеров функций сравнения:

Пример 1. сортируем массив чисел типа int по возрастанию
static int compare(const void * _a, const void * _b) {
	const int a = * reinterpret_cast<const int *>(_a);
	const int b = * reinterpret_cast<const int *>(_b);
	
	if (a < b) return -1;
	if (a > b) return 1;
	return 0;
}

Для сортировки по убыванию достаточно поменять местами a и b в сравнениях

Пример 2. сортируем массив чисел типа int по убыванию
static int compare(const void * _a, const void * _b) {
	const int a = * reinterpret_cast<const int *>(_a);
	const int b = * reinterpret_cast<const int *>(_b);
	
	if (b < a) return -1;
	if (b > a) return 1;
	return 0;
}

Кстати, в последнем стандарте С++ появился новый оператор сравнения <=>, который сразу возвращает то, что нам нужно, но у нас пока этот стандарт не поддерживается.

Для сравнения строк есть замечательная функция strcmp, которая сравнивает строки и возвращает именно то, что нужно qsort. Поэтому программы сравнения строк выглядят проще, чем чисел.

При сравнении объектов типа структур, классов и т.п. просто преобразуем переданные указатели к указателям на нужные объекты и сравниваем объекты так как мы понимаем их сравнение (повторюсь, qsort неоткуда знать, что у нас больше, а что меньше - она полностью полагается на нашу функцию сравнения). В примере программы будет четыре функции сравнения для структуры.

Вот

немного переделанная программа из поста №85
//
//	Структура - как была у вас, только я добавил текстовое поле name,
//	чтобы сортировать было веселее - хотел показать функцию сравнения строк.
//
struct commandLine {
	uint16_t timeFromStart;
	uint8_t deviceName;
	bool onDevice;
	uint16_t timeDeviceOn;
	const char * name;
};

//
//	Функция печати commandLine с указанием её индекса
//
void printCommandLine(const size_t index, const commandLine & cl, Print& p) {
	p.print("cL[");
	p.print(index);
	p.print("]: ");
	p.print("timeFromStart: ");
	p.print(cl.timeFromStart);
	p.print("; deviceName: ");
	p.print(cl.deviceName);
	p.print("; onDevice: ");
	p.print(cl.onDevice);
	p.print("; timeDeviceOn: ");
	p.print(cl.timeDeviceOn);
	p.print("; name: ");
	p.println(cl.name);
}

//
//	Функция сравнения для сортировки по полю name по алфавиту по возрастанию
//	Для преобразования типа лучше использовать непосредственные преобразования
//	а не универсальное в С-стиле, как использовали Вы. Я уже объяснял почему
//
static int compare1(const void * _a, const void * _b) {
	const commandLine * a = reinterpret_cast<const commandLine *>(_a);
	const commandLine * b = reinterpret_cast<const commandLine *>(_b);
	return strcmp(a->name, b->name);
}

//
//	Функция сравнения для сортировки по полю name по алфавиту по убыванию
//
static int compare2(const void * _a, const void * _b) {
	const commandLine * a = reinterpret_cast<const commandLine *>(_a);
	const commandLine * b = reinterpret_cast<const commandLine *>(_b);
	
	return strcmp(b->name, a->name);
}

//
//	Функция сравнения для сортировки по полю timeFromStart по возрастанию
//
static int compare3(const void * _a, const void * _b) {
	const commandLine * a = reinterpret_cast<const commandLine *>(_a);
	const commandLine * b = reinterpret_cast<const commandLine *>(_b);
	
	if (a->timeFromStart < b->timeFromStart) return -1;
	if (a->timeFromStart > b->timeFromStart) return 1;
	return 0;
}

//
//	Функция сравнения для сортировки по полю timeFromStart по убыванию
//
static int compare4(const void * _a, const void * _b) {
	const commandLine * a = reinterpret_cast<const commandLine *>(_a);
	const commandLine * b = reinterpret_cast<const commandLine *>(_b);
	
	if (b->timeFromStart < a->timeFromStart) return -1;
	if (b->timeFromStart > a->timeFromStart) return 1;
	return 0;
}


void setup() {
	Serial.begin(9600);

	//
	//	Структуру лучше инициализировать по именам полей
	//	инициализация по порядку следования (как было у вас) чревата тем,
	//	что Вы в какой-то момент чуть поменяете структуру а про это место забудете
	//	вот и "приехали"
	//	
	commandLine formovka[] = {
		{ .timeFromStart = 17,	.deviceName = 2,	.onDevice = true,	.timeDeviceOn = 170,	.name = "Dedka" },
		{ .timeFromStart = 8,	.deviceName = 3,	.onDevice = true,	.timeDeviceOn = 88,	.name = "Babka" },
		{ .timeFromStart = 5,	.deviceName = 2,	.onDevice = true,	.timeDeviceOn = 55,	.name = "Vnuchka" },
		{ .timeFromStart = 10,	.deviceName = 3,	.onDevice = true,	.timeDeviceOn = 100,	.name = "Zhuchka" },
		{ .timeFromStart = 21,	.deviceName = 2,	.onDevice = true,	.timeDeviceOn = 210,	.name = "Koshka" },
		{ .timeFromStart = 13,	.deviceName = 3,	.onDevice = true,	.timeDeviceOn = 130,	.name = "Myshka" }
	};
	//
	//	Размер элемента и количество элементов
	//
	constexpr size_t nElementSize = sizeof(commandLine);
	constexpr size_t nElements = sizeof(formovka) / nElementSize;

	//
	//	Печать массива структур до всякой сортировки
	//
	Serial.println("No Sorting");
	for (size_t i = 0; i < nElements; i++) printCommandLine(i, formovka[i], Serial);
	Serial.println();

	//
	//	Сортировка по полю name по возрастанию и печать массива структур
	//
	qsort(formovka, nElements, nElementSize, compare1);
	Serial.println("Sort by name ascending");
	for (size_t i = 0; i < nElements; i++) printCommandLine(i, formovka[i], Serial);
	Serial.println();

	//
	//	Сортировка по полю name по убыванию и печать массива структур
	//
	qsort(formovka, nElements, nElementSize, compare2);
	Serial.println("Sort by name decending");
	for (size_t i = 0; i < nElements; i++) printCommandLine(i, formovka[i], Serial);
	Serial.println();

	//
	//	Сортировка по полю timeFromStart по возрастанию и печать массива структур
	//
	qsort(formovka, nElements, nElementSize, compare3);
	Serial.println("Sort by timeFromStart ascending");
	for (size_t i = 0; i < nElements; i++) printCommandLine(i, formovka[i], Serial);
	Serial.println();

	//
	//	Сортировка по полю timeFromStart по убыванию и печать массива структур
	//
	qsort(formovka, nElements, nElementSize, compare4);
	Serial.println("Sort by timeFromStart decending");
	for (size_t i = 0; i < nElements; i++) printCommandLine(i, formovka[i], Serial);
	Serial.println();
}

void loop() { }
}
Результат её работы в мониторе порта
No Sorting
cL[0]: timeFromStart: 17; deviceName: 2; onDevice: 1; timeDeviceOn: 170; name: Dedka
cL[1]: timeFromStart: 8; deviceName: 3; onDevice: 1; timeDeviceOn: 88; name: Babka
cL[2]: timeFromStart: 5; deviceName: 2; onDevice: 1; timeDeviceOn: 55; name: Vnuchka
cL[3]: timeFromStart: 10; deviceName: 3; onDevice: 1; timeDeviceOn: 100; name: Zhuchka
cL[4]: timeFromStart: 21; deviceName: 2; onDevice: 1; timeDeviceOn: 210; name: Koshka
cL[5]: timeFromStart: 13; deviceName: 3; onDevice: 1; timeDeviceOn: 130; name: Myshka

Sort by name ascending
cL[0]: timeFromStart: 8; deviceName: 3; onDevice: 1; timeDeviceOn: 88; name: Babka
cL[1]: timeFromStart: 17; deviceName: 2; onDevice: 1; timeDeviceOn: 170; name: Dedka
cL[2]: timeFromStart: 21; deviceName: 2; onDevice: 1; timeDeviceOn: 210; name: Koshka
cL[3]: timeFromStart: 13; deviceName: 3; onDevice: 1; timeDeviceOn: 130; name: Myshka
cL[4]: timeFromStart: 5; deviceName: 2; onDevice: 1; timeDeviceOn: 55; name: Vnuchka
cL[5]: timeFromStart: 10; deviceName: 3; onDevice: 1; timeDeviceOn: 100; name: Zhuchka

Sort by name decending
cL[0]: timeFromStart: 10; deviceName: 3; onDevice: 1; timeDeviceOn: 100; name: Zhuchka
cL[1]: timeFromStart: 5; deviceName: 2; onDevice: 1; timeDeviceOn: 55; name: Vnuchka
cL[2]: timeFromStart: 13; deviceName: 3; onDevice: 1; timeDeviceOn: 130; name: Myshka
cL[3]: timeFromStart: 21; deviceName: 2; onDevice: 1; timeDeviceOn: 210; name: Koshka
cL[4]: timeFromStart: 17; deviceName: 2; onDevice: 1; timeDeviceOn: 170; name: Dedka
cL[5]: timeFromStart: 8; deviceName: 3; onDevice: 1; timeDeviceOn: 88; name: Babka

Sort by timeFromStart ascending
cL[0]: timeFromStart: 5; deviceName: 2; onDevice: 1; timeDeviceOn: 55; name: Vnuchka
cL[1]: timeFromStart: 8; deviceName: 3; onDevice: 1; timeDeviceOn: 88; name: Babka
cL[2]: timeFromStart: 10; deviceName: 3; onDevice: 1; timeDeviceOn: 100; name: Zhuchka
cL[3]: timeFromStart: 13; deviceName: 3; onDevice: 1; timeDeviceOn: 130; name: Myshka
cL[4]: timeFromStart: 17; deviceName: 2; onDevice: 1; timeDeviceOn: 170; name: Dedka
cL[5]: timeFromStart: 21; deviceName: 2; onDevice: 1; timeDeviceOn: 210; name: Koshka

Sort by timeFromStart decending
cL[0]: timeFromStart: 21; deviceName: 2; onDevice: 1; timeDeviceOn: 210; name: Koshka
cL[1]: timeFromStart: 17; deviceName: 2; onDevice: 1; timeDeviceOn: 170; name: Dedka
cL[2]: timeFromStart: 13; deviceName: 3; onDevice: 1; timeDeviceOn: 130; name: Myshka
cL[3]: timeFromStart: 10; deviceName: 3; onDevice: 1; timeDeviceOn: 100; name: Zhuchka
cL[4]: timeFromStart: 8; deviceName: 3; onDevice: 1; timeDeviceOn: 88; name: Babka
cL[5]: timeFromStart: 5; deviceName: 2; onDevice: 1; timeDeviceOn: 55; name: Vnuchka

Как видите, в каждом из четырёх случаев сортируется именно так, как прописано в функции сравнения.

Всё понятно? Или остались вопросы?

2 лайка

Нет не забыл, просто сегодня у меня было занятие в внучкой, как закончили и она ушла (около 19:20), я занялся этим.

Сегодня она из вот такой головки, горстки резисторов и батарейки делала “омметр” до 10кΩ. Второй день уж расчёт мучила. В прошлую пятницу так и не рассчитала, сегодня, вроде, получилось.

2 лайка

Отчаянно. Батарея же разряжается, за неделю так точно. Надеюсь в норму радиолюбителей (+/-15%) уложилась :slight_smile:

Ну, она же не собирается этим прибором реально пользоваться - собрала на макетке, попробовала подсунуть несколько резисторов, убедилась, что работает. На это батарейки хватило.

И да, кстати, Ц-шки всю жизнь так работали (головка-то от Ц-шки) и ничего, “диды пользовались” – меняли батарейки когда надо.

1 лайк

Я почему об этом написал.
Купил весной НОВЫЕ батареи типа «крона». Две штуки. Распечатал, проверил напряжение - на первый было 8.8В, на второй 8.7В. Хорошо. Одну (что 8.8В) отложил, вторую поставил в прибор (пользуюсь по 15-20 секунд в неделю, да даде есть 5 минут). Потребления в простое нет вообще (выключатель полностью разрывающий сразу оба контакта). Прошло время - прибор не запускается. Меряю напряжение - 6.7В (не достаточно прибору). Беру «запасную» - на ней 5.9В …. (((

ЗЫ: Батареи марки «Космос», покупал на озон.

Да, я тоже сталкивался с проблемами с этим брэндом. В последнее время стараюсь брать GP – нормальные и цена не космическая.

2 лайка

А я, видимо дурак, для таких целей 18650 на 2200ма*ч купил и плату к нему (сразу с dc-dc)…)))

Правда еще не монтировал. Только получил сегодня акб. Думаю «большеват», но не выкидывать же?))

https://aliexpress.ru/item/1005005767845332.html

Не сказать, что всё понятно. На данный момент достаточно того, что изящно и при этом работает)
Спасибо! Закрываю тему?

Так именно про это я и говорю “рассчитала” и “получилось”. Просто заставить стрелку качаться много ума не надо.

2 лайка

А что это был за прибор? Я головку не узнал…