Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.96/67: Рейтинг темы: голосов - 67, средняя оценка - 4.96
Заблокирован

Неочевидные грабли полиморфизма с++

23.11.2011, 01:05. Показов 16520. Ответов 101
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Наткнулся в интернете на любопытный код.
Спешу поделиться с сообществом.
Просто, что бы кто если не в курсе - узнал, и не попал на эти грабли:


C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
using namespace std;
 
struct A
{
    virtual void Func1(){cout<<"A::Func1"<<endl;}
};
struct B
{ 
    virtual void Func2(){cout<<"B::Func2"<<endl;}}
;
struct C: public A, public B
{ 
    virtual void Func3(){cout<<"C::Func3"<<endl;}
};
 
int main()
{
    A *ptr = new C;
    void *ptr1 = new C;
    
    ((A*)ptr)->Func1(); //вывод: A::Func1
    ((B*)ptr)->Func2(); //вывод: A::Func1 Почему?
    ((C*)ptr)->Func3(); //вывод: C::Func3
    
    A* aPtr=dynamic_cast<A*>(ptr);
    B* bPtr=dynamic_cast<B*>(ptr);
    C* cPtr=dynamic_cast<C*>(ptr)
    
    //нельзя кастовать от void*
    //void* ничего не знает ни о каких классах
    //и ни о каких таблицах виртуальных функций
 
    C* cPtr=dynamic_cast<C*>(ptr1); //ошибка компиляции
    //error C2681: void *: недопустимый тип выражения для dynamic_cast
    
    aPtr->Func1(); //вывод: A::Func1
    bPtr->Func2(); //вывод: B::Func2
    cPtr->Func3(); //вывод: C::Func3
 
    delete ptr;
    delete ptr1;
 
    return 0;
}
От себя могу добавить, что разные компиляторы по разному могут реализовывать механизм полиморфизма. Поэтому, при множественном наследовании если что и спасет - только dynamic_cast
Но в любом случае, нужно быть предельно осторожным.

А по сути, полиморфизм + множественное наследование = мина замедленного действия.

Поэтому, при проектировании полиморфных классов, лучше до последнего избегать множественного наследования.
1
Лучшие ответы (1)
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
23.11.2011, 01:05
Ответы с готовыми решениями:

Неочевидные особенности выдачи pg таблиц
Я довольно много копаюсь в pg_* таблицах, дабы получать информацию о базе данных и прочих системных фишечках. Ближе к делу: ...

Неочевидные результаты очевидных css-свойств
Добрый вечер, господа верстальщики! Столкнулся с такой неочевидной для меня проблемой: Оказывается, добавив свойство padding:1px,...

Грабли с кодировкой
Здравствуйте! В конструкторе главного окна: MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new...

101
Заблокирован
24.11.2011, 12:41  [ТС]
Студворк — интернет-сервис помощи студентам
Пример наглядно иллюстрирует, что к одному и тому же объекту можно обращаться, и вызывать его методы. И при этом компилятор будит считать что этот объект - каждый раз разный.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
struct First
{
    First(): a(333){}
    First( const First& istok) { std::cout << "Copy First\n"; }
    void View()
    {
        std::cout << "First\n"; 
        std::cout << "Size= "<< sizeof(*this)<< std::endl;
    }
    int a;
};
 
struct Second
{
    Second(): b(444){}
    Second( const Second& istok) { std::cout << "Copy Second\n"; }
    void View()
    {
        std::cout << "Second\n"; 
        std::cout << "Size= "<< sizeof(*this)<< std::endl;
    }
    int b;
};
 
 
struct Test: public First, public Second 
{
    void View()
    {
        std::cout << "Test\n"; 
        std::cout << "Size= "<< sizeof(*this)<< std::endl;
    }
};
 
int main()
{
    Test t; //доступны a, b 
    Test* ptr= &t;
    ptr->View();
 
    First* pFirst= static_cast<First*>(ptr);
    pFirst->View(); //сейчас объекту не доступен b
 
    //компилятор реально думает, что имеет дело с объектом First
    //а не Test
    std::cout<< "SIZE = " << sizeof( *pFirst ) <<std::endl;
 
    (static_cast<Second*>(ptr))->View(); //сейчас объекту не доступен a
    
    return 0;
};
Таким образом, работая с объектом через указатель, можно интерпритировать тип объекта как угодно. Что в случае с полиморфизмом может приводить к забавным последствиям.
0
Делаю внезапно и красиво
Эксперт С++
 Аватар для Deviaphan
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
24.11.2011, 12:44
Цитата Сообщение от Bers Посмотреть сообщение
Таким образом
Ты используешь НЕ виртуальные функции. Т.е. вызов функции происходит по адресу, известному во время компиляции (выводится из типа указателя). Полиморфизма в этом примере нет в принципе.
0
Заблокирован
24.11.2011, 12:51
Цитата Сообщение от Bers Посмотреть сообщение
Пример наглядно иллюстрирует, что к одному и тому же объекту можно обращаться, и вызывать его методы. И при этом компилятор будит считать что этот объект - каждый раз разный.

Код
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
struct First
{
    First(): a(333){}
    First( const First& istok) { std::cout << "Copy First\n"; }
    void View()
    {
        std::cout << "First\n"; 
        std::cout << "Size= "<< sizeof(*this)<< std::endl;
    }
    int a;
};
 
struct Second
{
    Second(): b(444){}
    Second( const Second& istok) { std::cout << "Copy Second\n"; }
    void View()
    {
        std::cout << "Second\n"; 
        std::cout << "Size= "<< sizeof(*this)<< std::endl;
    }
    int b;
};
 
 
struct Test: public First, public Second 
{
    void View()
    {
        std::cout << "Test\n"; 
        std::cout << "Size= "<< sizeof(*this)<< std::endl;
    }
};
 
int main()
{
    Test t; //доступны a, b 
    Test* ptr= &t;
    ptr->View();
 
    First* pFirst= static_cast<First*>(ptr);
    pFirst->View(); //сейчас объекту не доступен b
 
    //компилятор реально думает, что имеет дело с объектом First
    //а не Test
    std::cout<< "SIZE = " << sizeof( *pFirst ) <<std::endl;
 
    (static_cast<Second*>(ptr))->View(); //сейчас объекту не доступен a
    
    return 0;
};


Таким образом, работая с объектом через указатель, можно интерпритировать тип объекта как угодно. Что в случае с полиморфизмом может приводить к забавным последствиям.
У вас происходит статическое связывание функций по типу указателя на этапе компиляции. Компилятор видит тип указателя и соответственно вставляет статически код вызова соответствующей функции.

Признаюсь, я не понял, что вы хотели продемонстрировать.
0
Заблокирован
24.11.2011, 12:55  [ТС]
Цитата Сообщение от Deviaphan Посмотреть сообщение
Ты используешь НЕ виртуальные функции. Т.е. вызов функции происходит по адресу, известному во время компиляции (выводится из типа указателя). Полиморфизма в этом примере нет в принципе.
Данный пример наглядно демонстрирует, как один и тот же объект может быть интерпретирован и так, и этак.


А вот в примере по сабжу уже используется полиморфизм, и вот уже он наглядно демонтирует, что интерпретация объекта в качестве объекта другого типа может иметь неочевидные последствия.

В примере по сабжу используется.
0
Делаю внезапно и красиво
Эксперт С++
 Аватар для Deviaphan
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
24.11.2011, 13:01
В принципе, можно и так писать
C++
1
2
Test * t = (Test*)123456;
t->View();
Причём, этот код даже работать будет. Причём, даже правильно. Потому что данной функции безразлично состояние объекта.)

Добавлено через 1 минуту
Некоторые гвозди микроскопом забивают, но ведь это не значит, что микроскопом не нужно пользоваться. А если будете гвозди микроскопом забивать, то это "может иметь неочевидные последствия".
0
Заблокирован
24.11.2011, 13:06  [ТС]
Цитата Сообщение от Deviaphan Посмотреть сообщение
Некоторые гвозди микроскопом забивают, но ведь это не значит, что микроскопом не нужно пользоваться. А если будете гвозди микроскопом забивать, то это "может иметь неочевидные последствия".
Плоха та архитектура, которая вынуждает программиста все время помнить о низко-уровневой работе механизмов, и все время думать: а нет ли здесь каких нибудь невидимых граблей?

Одна из фундаментальных задач ООП - гасить сложность архитектуры проекта.
А не увеличивать её.

Полиморфизм + множественное наследование значительно увеличивают сложность понимания архитектуры, и вынуждают думать сразу о многом.

На мой взгляд, это плохая практика.
Гораздо выгоднее использовать неглубокие, и неширокие деревья наследований + композиция.

Вместо потенциально опасных глубоких и запутанных иерархий
0
 Аватар для taras atavin
4226 / 1796 / 211
Регистрация: 24.11.2009
Сообщений: 27,562
24.11.2011, 14:03
Цитата Сообщение от Bers Посмотреть сообщение
Плоха та архитектура, которая вынуждает программиста все время помнить о низко-уровневой работе механизмов, и все время думать: а нет ли здесь каких нибудь невидимых граблей?
Где грабли? Решил сделать типы не совместимыми - не приводи один к другому. Плоха технология намеренного отказа от гибкости из-за якобы абстрактной опасности. Например, указатель опасен не абстрактно, а только при выдаче на чужой уровень. И необходимо его просто иметь. Использовать лучше всего только внутри классов, но при большом желании можно даже глобально. Но при работе с каким либо уровнем абстракции любая технология должна позволять ограничиваться рассмотрением только этого одного уровня. Больше того, эта возможность должна всегда и использоваться. Ну так вот, с какими бы классами ты ни работал, их отношение вида предок-потомок - именно тот уровень, с которым ты при этом работаешь. Ну так вспоминай иерархию и смотри, какой класс от кого происходит. Если классы не связаны, то нельзя приводить. И не надо кивать на динамический каст. Эта технология продвинутая, она умеет превращать даже автомобиль в лодку и назад, если уже изобретены амфибии. Нет амфибий - лодка и автомобиль не совместимы даже для этой технологии. Старая же технология приведения работает только с указателем без обращения к таблицам. И для неё лодка с автомобилем остаются несовместимыми в не зависимости от изобретения гибридного транспорта. В частности поэтому, кстати, опасны любые библиотеки классов, пока не изучишь их иерархию. Да и просто семантика методов и их параметров. Вот взять строки. С какого символа они начинаются? С первого, или с нулевого? Если класс строк не делает какой нибудь разбор сам, то тебе может понадобиться сделать его как посимвольный. Какой символ последний? Измерить длину и от неё? Если ты думаешь, что строка начинается нулевым символом, а на самом деле первым, то ты никогда не обработаешь последний символ, если в нём важная информация, то ты её никогда не учтёшь, прога просто повиснет. Даже утечка далеко не так страшна. Ну кончится память. Перезагрузи прогу и может быть ось за ней подчистит. Не помогло? Перезагрузи ось и память вся вернётся. А за висяк ты не пройдёшь никогда. И тестить бесполезно, пока не догадаешься, куда смотреть. К тому же даже если ты допустил ошибку, провоцирующую утечку, её можно найти просто внимательным чтением только своего исходника и больше ничего. Даже тест не нужен в принципе. То же самое, если важен первый символ, ты его считаешь первым, а он нулевой. Висяк слепишь и даже не увидишь. Вот попроюбуй bmp файл переименованием превратить в метафайл, а потом исполнить. А!!! Винда суперопасна!!! В ней под водой плавают грабли переименования!!! Не делай фигни - не изобретёшь грабли. Причём, они твои, а не полиморфизма. Ещё опаснее шаблон, если не понимаешь его назначения и начинаешь скармливать классы банковских счетов, или каких нибудь интерфейсов шаблону строк с отдельным полем числа символов, конкретизируемым unsigned small int для строк до 255-ти символов, unsigned short int - до 65535 и unsigned long int - до 4-х гигасимволов.
0
Заблокирован
24.11.2011, 17:54  [ТС]
Итак. Я уже почти было убедился, что приведение типов вызывает конструктор копирования.

То есть, "привести тип A к типу B" фактически означает "создать временный объект типа B, при помощи конструктора копирования этого типа B, аргументом которого будит являться объект типа А".

То бишь, временный объект типа B будит создан при помощи копирующего конструктора по прототипу типа A.


На самом деле, для меня это было открытием. Я то думал, что приведение типов меняет лишь интерпретацию типа объекта, и никаких расходов на копирование не произойдёт!

Естественно, я обеспокоился, и начал обшаривать существующую архитектуру своего прожекта.
Поскольку я пихал приведение типов везде где только можно. Я ж не знал, что это может привести к падению производительности из-за запусков копирующих конструкторов.

Я решил, что если такие места есть, то можно избавиться от ненужного копирования за счёт приведение типов указателей, а не самих объектов.
То есть изменить интерпретацию типа объекта, за счет приведения указателя на нужный тип, и подсовывание указателя вместо объекта.

Я почти не сомневался в том, что предстоит довольно кропотливая работа по чистке всего кода, и спасению производительности (так то оно конечно не тормозит. Так что можно было и не заострятся. Но я ж не зря свой скилл прокачиваю. Надо использовать полученное знание).

И я был поражон....

Объясните мне, почему так?

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
struct Base
{
    Base():a(333){}
    Base(const Base& istok): a(istok.a){ std::cout<< "Copy\n"; }
    void Print(const Base& istok )  {    std::cout<< "Print: a="<<istok.a<<std::endl;  }
    int a;
};
 
struct Derrived: public Base
{
    void Print(const Derrived& istok ) 
    { 
        std::cout<< "Derrived Print\n";
        Base::Print( istok ); //здесь происходит приведение типа
                              //от потомка к базовому
                              //почему при этом не срабатывает 
                              //копирующий конструктор?
    }
};
 
 
 
int main()
{
    Derrived test;
    test.Print(test);
    return 0;
};
Что я не так понимаю? Методу Print Derrived скармливается объект типа Derrived
А он в свою очередь проталкивает этот объект своему базовому классу.

Но базовый класс в качестве аргумента принимает только объекты своего класса!
Таким образом в момент передачи типа Derrived методу класса Base должно произойти приведение типа!

И соответственно, должен быть вызван копирующий конструктор. Почему этого не произошло?
Неужели сменилась просто интерпретация типа?
0
Делаю внезапно и красиво
Эксперт С++
 Аватар для Deviaphan
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
24.11.2011, 18:00
Цитата Сообщение от Bers Посмотреть сообщение
И соответственно, должен быть вызван копирующий конструктор. Почему этого не произошло?
Не должен. Ведь ты передаёшь по ссылке. При передаче по ссылке или по указателю приведение типа объекта не происходит, происходит приведение типа ссылки. Соответственно и срезки не будет. Именно поэтому по ссылке и передают, чтобы избежать ненужного копирования.
1
Заблокирован
24.11.2011, 18:05  [ТС]
Цитата Сообщение от Deviaphan Посмотреть сообщение
Не должен. Ведь ты передаёшь по ссылке. При передаче по ссылке или по указателю приведение типа объекта не происходит, происходит приведение типа ссылки. Соответственно и срезки не будет. Именно поэтому по ссылке и передают, чтобы избежать ненужного копирования.
Хм... ну я у себя везде где ток можно юзал ссылки. А где не можно - юзал указатели.
Я то собрался код чистить, а он у меня оказывается такой... итак чистенький)

А тут значится, что ссылка - это не просто псевдоним объекта. Это вообще как бы и не объект вовсе.. гм гм... Это именно указатель на объект. Только замаскированный так, словно "другое имя объекта".
0
Делаю внезапно и красиво
Эксперт С++
 Аватар для Deviaphan
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
24.11.2011, 19:44
Цитата Сообщение от Bers Посмотреть сообщение
А тут значится, что ссылка - это не просто псевдоним объекта
Именно так. Фактически, это полиморфный указатель, но без указателя. Т.е. полиморфный адрес объекта, который используется непосредственно, а не посредством указателя. Как-то так.
0
84 / 57 / 8
Регистрация: 07.08.2010
Сообщений: 185
18.12.2011, 05:03
Цитата Сообщение от ValeryLaptev Посмотреть сообщение
Спустимся немного вглубь.
Все указатели на уровне реализации - абсолютно одинаковы. Поэтому явное преобразование типа указателя - это просто для порядку на уровне С++. На самом деле никаких преобразований не производится. Как и было в С.
Очевидно, это не так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <cstdio>
#include <cassert>
 
class A { int a; };
class B { int b; };
class C : public A, public B { int c; };
 
int main()
{
    C obj;
    A * a = &obj;
    B * b = &obj;
    C * c = &obj;
 
    std::printf("a != b                       : a = %p, b = %p, c = %p\n", a, b, c);
    std::printf("(C *)a == (C *)b             : (C *)a = %p, (C *)b = %p\n", (C *)a, (C *)b);
    std::printf("static cast (C *)a == (C *)b : (C *)a = %p, (C *)b = %p\n", static_cast<C *>(a), static_cast<C *>(b));
 
    assert((C *) a == (C *) b);
    assert(static_cast<C *>(a) == static_cast<C *>(b));
}
При преобразовании (C*)b происходит поправка значения указателя.

А вот при наличии в классе виртуальных функциях нужно знать тип ВО ВРЕМЯ ВЫПОЛНЕНИЯ. Чтобы вызвать нужную функцию. Это довольно сложная операция. Это и есть механизм RTTI.
* RTTI не имеет отношения к виртуальным функциям.
* dynamic_cast нужен для преобразования между типами, между которыми отсутствует наследственная связь. Иначе работает простой static_cast.
0
Делаю внезапно и красиво
Эксперт С++
 Аватар для Deviaphan
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
18.12.2011, 07:41
Цитата Сообщение от alexzak Посмотреть сообщение
между которыми отсутствует наследственная связь.
Нет. Для преобразования от родительского к дочернему static_cast не подходит, только dynamic_cast.
0
В астрале
Эксперт С++
 Аватар для ForEveR
8049 / 4806 / 655
Регистрация: 24.06.2010
Сообщений: 10,562
18.12.2011, 12:19
Deviaphan, Если нету множественного наследования и есть уверенность, что преобразование корректно - reinterpret_cast.
0
Делаю внезапно и красиво
Эксперт С++
 Аватар для Deviaphan
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
18.12.2011, 12:31
Цитата Сообщение от ForEveR Посмотреть сообщение
Если нету множественного наследования и есть уверенность, что преобразование корректно - reinterpret_cast.
Я к тому, что со static_cast вообще не скомпилируется.
0
DU
1500 / 1146 / 165
Регистрация: 05.12.2011
Сообщений: 2,279
18.12.2011, 12:40
статик каст подойдет и это скомпилится. только результат каста может быть не таким, какой ожидается. А компилится это потому, что компилятор видит наследственную связь между базой и производным классом. При множественном наследовании это возможно со своими хитростями будет работать или не работать, но в простом случае статик_каст от родителя к потомку компилится:

C++
1
2
3
4
5
6
7
8
9
10
11
class Base {};
class Der1 : public Base {};
class Der2 : public Base {};
 
int main()
{
  Der1 d1;
  Base* pBase = &d1;
  Der2* pDer2 = static_cast<Der2*>(pBase); // сюрприз!
  return 0;
}
0
Делаю внезапно и красиво
Эксперт С++
 Аватар для Deviaphan
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
18.12.2011, 12:52
Цитата Сообщение от DU Посмотреть сообщение
статик каст подойдет и это скомпилится.
Честно говоря, я потрясён. Заведомо ошибочный код компилируется - это не есть хорошо.

Добавлено через 4 минуты
Впрочем, это лишний повод не использовать static_cast там, где его использовать не следует.)
0
DU
1500 / 1146 / 165
Регистрация: 05.12.2011
Сообщений: 2,279
18.12.2011, 13:01
Компилябельность объяснить можно тем, что не везде используют RTTI по разным причинам. Но в то же время хочется иметь возможность каститься с хоть какой-то диагностикой на этапе компиляции. Ну и как я уже сказал, поскольку компилятор видит связь между родителем и дочерним классом, он разрешает такой каст.
0
Делаю внезапно и красиво
Эксперт С++
 Аватар для Deviaphan
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
18.12.2011, 13:07
Никогда так не писал, поэтому был уверен, что компилятор будет ругаться.) MFC не в счёт.
0
В астрале
Эксперт С++
 Аватар для ForEveR
8049 / 4806 / 655
Регистрация: 24.06.2010
Сообщений: 10,562
18.12.2011, 13:20
Выдержки из стандарта на эту тему.

An lvalue of type “cv1 B,” where B is a class type, can be cast to type “reference to cv2 D,” where D is a class
derived (Clause 10) from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists (4.10),
cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and B is neither a virtual base class
of D nor a base class of a virtual base class of D. The result has type “cv2 D.” An xvalue of type “cv1 B” may
be cast to type “rvalue reference to cv2 D” with the same constraints as for an lvalue of type “cv1 B.” If the
object of type “cv1 B” is actually a subobject of an object of type D, the result refers to the enclosing object
of type D. Otherwise, the result of the cast is undefined.

C++
1
2
3
4
5
6
struct B { };
struct D : public B { };
D d;
B &br = d;
static_cast<D&>(br);
// produces lvalue to the original d object
A prvalue of type “pointer to cv1 B,” where B is a class type, can be converted to a prvalue of type “pointer
to cv2 D,” where D is a class derived (Clause 10) from B, if a valid standard conversion from “pointer to D” to
“pointer to B” exists (4.10), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and B
is neither a virtual base class of D nor a base class of a virtual base class of D. The null pointer value (4.10)
is converted to the null pointer value of the destination type. If the prvalue of type “pointer to cv1 B” points
to a B that is actually a subobject of an object of type D, the resulting pointer points to the enclosing object
of type D. Otherwise, the result of the cast is undefined.
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
18.12.2011, 13:20

Xmega грабли
Так понимаю, мало кто в форуме xmega занимается, но вдруг кому полезно будет. Они, конечно, описаны в даташитах, но... Мелкие отличия...

Грабли с USART_FLAG_RXNE
Решил тут создать сообщение, чтобы поделиться ошибкой, что я ловил пол дня. Есть у арма регистр, куда падает приходящая инфа в УАРТ. ...

Наступлю на те же грабли?
Здравствуйте. Регистрировал доменное имя через jino.ru, а они согласно whois использовали R01-REG-RIPN. Потом выбрал другой хостинг....

Грабли с WM_DEVICECHANGE
Потратил кучу времени на изучение структур связанных с WM_DEVICECHANGE. Все работает как надо wParam == DBT_DEVICEARRIVAL , отлавливает...

Грабли malloc/free
С динамической памятью впервой работаю, от сюда и грабли Есть структура typedef struct { uint8_t data_size; uint8_t *data;...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
100
Ответ Создать тему
Новые блоги и статьи
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр.
Jin X 06.09.2026
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр. Работая с форумом и нейросетями в браузере часто хочется что-то подкорректировать или добавить какого-то функционала. Ниже прикреплён. . .
Программа опроса у.з. расходомера SLS-720F
Argus19 02.09.2026
Программа опроса у. з. расходомера SLS-720F Программа опрашивает один раз в минуту три ультразвуковых расходомера SLS-720F через интерфейс RS-485 по протоколу Modbus RTU. Опрашиваются регистры. . .
Hyper-V: Компьютер должен поддерживать доверенный платформенный модуль 2.0.
Maks 31.08.2026
При установке Windows 11 на виртуальную машину Hyper-V 2-го поколения вылезла такая ошибка: Решение: в параметрах виртуальной машины, в разделе "Безопасность" (Security) активировать флаг. . .
Архитектура биовида Стива в Майнкрафте: Зачем бонобо кубический каннибализм
anaschu 30.08.2026
Кубический Вагинокапитализм в Minecraft: Математический инвариант ОДУ и рок Стивов-бонобо Главная задача разработанной «Модели Всего» — наглядно продемонстрировать наличие системной «судьбы». . .
Оттачиваю умение писать js программы.
russiannick 30.08.2026
Проектом выходного дня стало написание Книги шифров Виженера. Итогом стала версия 200, синий туман. Синий туман назван так, потому что замораживает текст под собой. Нажатие синих кнопок управляют. . .
мат медиц модель 30. презентация проекта
anaschu 27.08.2026
хоп хоп хоп хидахоп, а я кладую))
Как у меня протекала болезнь
zorxor 27.08.2026
Здравствуйте, друзья! Эта запись блога предназначена именно для вас - для моих дорогих друзей, которые знали меня лично. Чтобы ответить на вопрос - а что же со мной произошло на самом деле? Я учился. . .
Нашел вот забавное видео о измерениях. Лучшее что я видел на эту тему
kumehtar 26.08.2026
ILETXiw9bMQ Основная суть и тезисы по измерениям: 0D (Нулевое измерение): точка, не имеющая длины, ширины, высоты или объема. Объект не может перемещаться в 0D. 1D (Первое измерение):. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru