Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Портится стек вызовов непонятным образом

1.8K
28 июля 2012 года
Arkady
153 / / 18.12.2007
Доброго времени суток,

Недавно столкнулся с ситуацией, когда вызов функции класса Add(pstk, mem, 6) вместо перехода к телу функции, бросает в конструктор класса, естественно с битыми переменными. Т.е. очевидно портится стек вызовов функций. Хотя если взгляните на код (я упростил ситуацию максимально), внешне ничего нет.

<code>
#pragma hdrstop

//---------------------------------------------------------------------------
#include <windows.h>
#pragma argsused

class C_KeyDataHolder
{
private:
void* m_Data;
unsigned long m_offset;
unsigned long m_range;

public:
C_KeyDataHolder()
{
m_Data = 0;
m_offset = 0;
m_range = 0;
}
C_KeyDataHolder(void* pstk)
{
m_Data = pstk; //new C_KeyData((PSTKMPR)pstk);
m_offset = 0;
m_range = 0;
MessageBox(0, "bla", "blabla", MB_OK);
}
long Add(void* pstk, const char* Data, unsigned long DSize)
{
MessageBox(0, "bla", "blabla2", MB_OK);
m_Data = pstk;
return 0;
}

long Add(void* pstk, const C_KeyDataHolder& KDH, unsigned long offset=0, unsigned long range=0)
{
MessageBox(0, "bla", "blabla3", MB_OK);
m_Data = pstk;
return 0;
}
};

int main(int argc, char* argv[])
{
void* pstk;
C_KeyDataHolder OutData;

unsigned long memsize = 6;
unsigned char* mem = (unsigned char*)malloc(memsize);
memcpy(mem, "blabla", 6);
OutData.Add(pstk, mem, 6);
free(mem);
return 0;
}
</code>

Удивительно, что стек перестает портиться, если unsigned char* mem = (unsigned char*)malloc(memsize); заменить на char* mem = (char*)malloc(memsize);

Так же проблема уходит, если закомментировать функцию long Add(void* pstk, const C_KeyDataHolder& KDH, unsigned long offset=0, unsigned long range=0).

При этом если закомментировать long Add(void* pstk, const char* Data, unsigned long DSize), то проект собирается! Такое ощущение, что автоматическое приведение типов приводит unsigned char к &.... Это подтверждается тем, что проблема уходит при прямом переводе типов.
Почему это происходит? Никогда не думал, что беззнаковый чар может автоматически привестись к ссылке =(

Однако при релизной сборке, вылетают два сообщения: MessageBox(0, "bla", "blabla", MB_OK); и MessageBox(0, "bla", "blabla3", MB_OK); в этом случае, это вообще выносит мне мозг, потому что даже если он вызывает не тот Add из-за странного автоматического приведения типов, то каким образом он попадает в конструктор C_KeyDataHolder(void* pstk)???

Могу прислать архив проекта, хотя тут все, что в нем есть.
446
28 июля 2012 года
Meander
487 / / 04.09.2011
1. Приведением типов по умолчанию лучше не злоупотреблять.
2. Компилятору привести char к unsigned char проще чем наоборот.
3. Вычислить адрес первого элемента массива проще, чем приводить типы.
Учитывая эти пункты приходим к следующему:
Вы перегрузили Add очень неудачно, поэтому когда компилятор сравнил, решил
вызвать второй Add. Эта функция хотела вычислить адрес вашего объекта, но
вместо этого разыменовала указатель на первый элемент массива беззнаковых
символов. При разыменовывании указателя мы получаем сам объект! Но какой?
Этот объект был создан наиболее подходящим конструктором - конструктором,
принимающим в качестве параметра указатель. Если бы Вы добавили месседж
в конструктор по умочанию, то увидели бы вызов аж 3-х сообщений. Первое - когда
впервые создали объект класса, второе - когда функция разыменовала указатель, и
третье - вызвала сама функция.
Исправление - это написать прототипы функций под все планируемые типы.
260
28 июля 2012 года
Ramon
1.1K / / 16.08.2003
explicit, нэ.
446
28 июля 2012 года
Meander
487 / / 04.09.2011
[QUOTE]Удивительно, что стек перестает портиться, если unsigned char* mem = (unsigned char*)malloc(memsize); заменить на char* mem = (char*)malloc(memsize);
[/QUOTE]
Ничего удивительного, если прототип:
[QUOTE]long Add(void* pstk, const char* Data, unsigned long DSize)
{
[/QUOTE]
446
28 июля 2012 года
Meander
487 / / 04.09.2011
Вот, что после исправления все нормально работать начинает, это, на самом деле, удивительно!
446
28 июля 2012 года
Meander
487 / / 04.09.2011
А конструктор не тот вызывается, как-раз, потому что другой Add вызывается.
1.8K
28 июля 2012 года
Arkady
153 / / 18.12.2007
Цитата: Meander
[QUOTE]Удивительно, что стек перестает портиться, если unsigned char* mem = (unsigned char*)malloc(memsize); заменить на char* mem = (char*)malloc(memsize);
[/QUOTE]
Ничего удивительного, если прототип:
[QUOTE]long Add(void* pstk, const char* Data, unsigned long DSize)
{
[/QUOTE]


Не понимаю, почему, unsigned char к char и обратно всегда приводится без проблем. Приведение типа может вызвать приведение unsigned char к ссылке на объект - это нормально? Просто я впервые такое вижу, мало того, никаким кастом, при желании, такого не добиться..

И, тем более, не понимаю, почему же конструктор вызывается не тот при вызове не той функции? Почему он вообще вызывается, если есть лишь один вызов функции?

Странно, что после какого исправления все работает, почему? :)
Что при этом происходит в стеке вызовов, можете показать?

65K
28 июля 2012 года
verholom
48 / / 29.08.2011
Цитата: Arkady
[quote=Meander;74251][QUOTE]
Не понимаю, почему, unsigned char к char и обратно всегда приводится без проблем.



Потому что у того и у того по 8 бит данных.

Цитата: Arkady
[quote=Meander;74251][QUOTE]Приведение типа может вызвать приведение unsigned char к ссылке на объект - это нормально?



Не то, чтоб не нормально, это олигофрения со стороны компилятора

Итог: Я не понимаю, какая разница между размером указателя на знаковый и беззнаковый чары - те же самые 4 байта...

Это явный глюк...

1.8K
28 июля 2012 года
Arkady
153 / / 18.12.2007
Цитата: Meander
1. Приведением типов по умолчанию лучше не злоупотреблять.
2. Компилятору привести char к unsigned char проще чем наоборот.
3. Вычислить адрес первого элемента массива проще, чем приводить типы.
Учитывая эти пункты приходим к следующему:
Вы перегрузили Add очень неудачно, поэтому когда компилятор сравнил, решил
вызвать второй Add. Эта функция хотела вычислить адрес вашего объекта, но
вместо этого разыменовала указатель на первый элемент массива беззнаковых
символов. При разыменовывании указателя мы получаем сам объект! Но какой?
Этот объект был создан наиболее подходящим конструктором - конструктором,
принимающим в качестве параметра указатель. Если бы Вы добавили месседж
в конструктор по умочанию, то увидели бы вызов аж 3-х сообщений. Первое - когда
впервые создали объект класса, второе - когда функция разыменовала указатель, и
третье - вызвала сама функция.
Исправление - это написать прототипы функций под все планируемые типы.



Спасибо. Никогда бы не подумал, что компилятор может сделать такое приведение типа, которого делать нельзя, а потому вызвать не ту функцию. А то, что не сумев разыменовать указатель в тип, в который тот нельзя разыменовывать, но оказалось, что можно - он попытается сам создать нужный тип, вызвав конструктор (т.е. сам, без ведома программиста, создаст объект, имея ссылку, которая лишь должна указывать на существующий объект) - для меня настолько неожиданно, что я даже не смог диагностировать этот этап вообще. Век живи, век учись :) Спасибо еще раз.

1.8K
28 июля 2012 года
Arkady
153 / / 18.12.2007
Цитата: verholom
Цитата: Arkady
[quote=Meander;74251][QUOTE]
Не понимаю, почему, unsigned char к char и обратно всегда приводится без проблем.


Потому что у того и у того по 8 бит данных.


Между "почему" и "unsigned char к char и обратно всегда приводится без проблем" стояла запятая =) Т.е. я не понимал, почему он так неудачно привел типы ВЕДЬ - то, что после запятой.

Цитата: verholom

Цитата: Arkady
[quote=Meander;74251][QUOTE]Приведение типа может вызвать приведение unsigned char к ссылке на объект - это нормально?


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


Размер ссылки такой же. Тот факт, что компилятор выбирает привести к ссылке, а не к другому чару - странный для меня, но - что есть :)

446
28 июля 2012 года
Meander
487 / / 04.09.2011
привести тип он не смог, потому что для этого надо изменить объкт, а у Вас там const стоит, который препятствует модификации объекта
1.8K
28 июля 2012 года
Arkady
153 / / 18.12.2007
Цитата: Meander
привести тип он не смог, потому что для этого надо изменить объкт, а у Вас там const стоит, который препятствует модификации объекта



Но так и ссылка на тип тоже const ("const C_KeyDataHolder& KDH"), тем не менее, в этом случае проблемы не возникло :)

446
28 июля 2012 года
Meander
487 / / 04.09.2011
видимо, константный указатель и константная ссылка - это разные вещи
1.8K
29 июля 2012 года
Arkady
153 / / 18.12.2007
Цитата: Ramon
explicit, нэ.


Ну да, технически грамотное решение, которое я вывел для себя:
1) explicit на все конструкторы с одним обязательным параметром
2) Add() и для char* и для unsigned char*

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог