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

Ваш аккаунт

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

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

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

Утечка памяти MVSC++ 2005

307
28 августа 2008 года
Artem_3A
863 / / 11.04.2008
Вот такая вот проблема, течет память вроде и не много 24 байта и 5, но все равно не приятно, уже три дня пытаюсь перекрыть эту утечку и пока не очень приуспел. Есть метод
Код:
void CKillerDlg::OnCbnSelchangeCombo1()
{
    list.clear();//чистим списки и вектор
    m_hiddenFileList.ResetContent();
    m_Information.ResetContent();
    CString temp;
    m_DiskList.GetLBText(m_DiskList.GetCurSel(), temp);//получаем букву диска где будем искать файлы
    register TCHAR *searchAdress = new TCHAR[temp.GetLength()+2];//перегоняем в чар
    lstrcpyA(searchAdress, temp);
    searchAdress[3]='\\';
    searchAdress[4]='*';
    searchAdress[5]='\0';
    CKillerDlg::searchHiddenFile(searchAdress);//вызываем функцию поиска файлов на диске
    for(int i=0; i<list.size(); i++)
        m_hiddenFileList.AddString(list->getName());
    UpdateData(false);
}

и
Код:
void CKillerDlg::searchHiddenFile(register const char* searchAdress)
{
    register WIN32_FIND_DATAA fn;
    HANDLE hf;
    hf = FindFirstFileA(searchAdress, &fn);
    if(hf!=INVALID_HANDLE_VALUE)
    {
        do
        {
            try
            {
/*проверяем*/          if((fn.dwFileAttributes&FILE_ATTRIBUTE_HIDDEN) == FILE_ATTRIBUTE_HIDDEN)
                    throw 1;
                if((fn.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY)
                    continue;
                if((fn.dwFileAttributes&FILE_ATTRIBUTE_SYSTEM) == FILE_ATTRIBUTE_SYSTEM)
                    continue;
                if(strcmp(fn.cFileName, ".")==0)
                    continue;
                if(strcmp(fn.cFileName, "..")==0)
                    continue;
/*приводим адресс*/       register char* newAdress;
/*в пригодный вид*/        newAdress = new char[1000];
                strncpy(newAdress, searchAdress, 1000);
                for(register int i=0; i<strlen(newAdress); i++)
                {
                    if((newAdress=='*') || (newAdress=='\0'))
                    {
                        newAdress='\0';
                        break;
                    }
                }
                strcat(newAdress,fn.cFileName);
                int size = strlen(newAdress);
                newAdress[size]='\\';
                newAdress[1+size]='*';
                newAdress[2+size]='\0';
                searchHiddenFile(newAdress);//ищем дальше
                delete newAdress;
            }
            catch(int e)
            {
                if((fn.dwFileAttributes&FILE_ATTRIBUTE_SYSTEM) != FILE_ATTRIBUTE_SYSTEM)
                {
                    CKfile* temp = new CKfile;//если файл скрытый то заполняем класс
                    temp->setData(fn, searchAdress);//и длбавляем в вектор
                    list.push_back(temp);
                }
            }
        }while(FindNextFileA(hf, &fn)!=0);
        FindClose(hf);
    }
}


Класс
Код:
class CKfile
{
public:
    CKfile(void);
    CKfile(const CKfile &);
    ~CKfile(void);
    char* getName(void) const;
    int getSize(void) const;
    char* getLocation(void) const;
    CString getLastAccesTime(void) const;
    CString getCreateTime(void) const;
    bool getDirFlag(void) const;
    void setData(const WIN32_FIND_DATA&, const char*);
private:
    char* name;
    int size;
    char* location;
    CString lastAccesTime;
    CString createTime;
    bool dirFlag;

//вспомогателная функция
    CString ConvertTime(const FILETIME&) const;
};


Функции CString ConvertTime(const FILETIME&) const; и void setData(const WIN32_FIND_DATA&, const char*);
Код:
CString CKfile::ConvertTime(const FILETIME &temp) const
{
    try
    {
        SYSTEMTIME UTC, Time;
        BOOL result;
        result = FileTimeToSystemTime(&temp, &UTC);
        if(result==false)
            throw result;
        result = SystemTimeToTzSpecificLocalTime( NULL, &UTC, &Time);
        if(result==false)
            throw result;
        register char buffer[20];
        register CString tempDate;
        tempDate = itoa(Time.wDay, buffer, 10);
        tempDate += '.';
        tempDate += itoa(Time.wMonth, buffer, 10);
        tempDate += '.';
        tempDate += itoa(Time.wYear, buffer, 10);
        tempDate += '\\';
        tempDate += itoa(Time.wHour, buffer, 10);
        tempDate += ':';
        tempDate += itoa(Time.wMinute, buffer, 10);
        return tempDate;
    }
    catch(BOOL e)
    {
        return "Date is unknown.";
    }
}


/////////////////////////////////////////////////////////////

void CKfile::setData(const WIN32_FIND_DATA &temp, const char* path)
{
    for(register int i=0; i<260; i++)
    {
        name=temp.cFileName;
        if(temp.cFileName=='\0')
            break;
    }
    size = temp.nFileSizeHigh*(1+MAXDWORD) + temp.nFileSizeLow;
    strcpy(location, path);
    location[strlen(location)-1]='\0';
    strncat(location, name, (10000-strlen(location)));
    lastAccesTime = ConvertTime(temp.ftLastAccessTime);
    createTime = ConvertTime(temp.ftCreationTime);
    if((temp.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
        dirFlag = true;
    return;
}


Память течет в строчках register TCHAR *searchAdress = new TCHAR[temp.GetLength()+2];(5 байт) и CKfile* temp = new CKfile;(24 байта), при попытке удалять searchAdress выскакиевает ошибка, собственно как и при удалении temp. Как можно устранить эту утечку и в чем тут дело?
307
28 августа 2008 года
Artem_3A
863 / / 11.04.2008
Все, уже разобрался, вопрос снят.:)
14
28 августа 2008 года
Phodopus
3.3K / / 19.06.2008
Ну если в TCHAR[temp.GetLength()+2]; - 5 байт, а ты пишешь в него 6 то это уже наводит на размышления.
Ошибки при попытках удаления чаще всего результат поврежденного указателя - искать кто вредит.
Использование register а) нецелесообразно для современных компиляторов б) нужно делать с умом понимая что такое регистр.
Был в шоке от кода :)
Поправьте если не прав..
307
28 августа 2008 года
Artem_3A
863 / / 11.04.2008
Цитата: Phodopus
Ну если в TCHAR[temp.GetLength()+2]; - 5 байт, а ты пишешь в него 6 то это уже наводит на размышления.


Уже нашел эту ошибку и очень долго себя ругал за не просветную тупость!=)

Цитата: Phodopus
Был в шоке от кода


Укажите пожалуйста узкие места и прочие огрехи, дабы я мог их исправить и впредь не совершать! Всегда рад принять конструктивную критику!=)

341
28 августа 2008 года
Der Meister
874 / / 21.12.2007
[QUOTE=Phodopus]Использование register а) нецелесообразно для современных компиляторов б) нужно делать с умом понимая что такое регистр.[/QUOTE]Для VC++ разницы нет, но осторожным быть заставит[QUOTE=MSDN]Microsoft Specific

The Microsoft C/C++ compiler does not honor user requests for register variables. However, for portability all other semantics associated with the register keyword are honored by the compiler. For example, you cannot apply the unary address-of operator (&) to a register object nor can the register keyword be used on arrays.
[/QUOTE]:)
255
28 августа 2008 года
Dart Bobr
1.4K / / 09.04.2004
Навскидку:
 
Код:
newAdress = new char[1000];

что такое 1000? Почему именно 1000, а не 1024, не 1001, не 42?
От "магических чисел" нужно избавляться. Задефайни константу с таким значением и нормальным именем и используй ее.

 
Код:
if(result==false)

тут ты написал лишнее сравнение. сначала сравнивается переменная result с false, а потом полученый булевый результат проверяется на истинность. Гораздо лаконичнее писать так:
 
Код:
if (!result)

Функция:
 
Код:
CString CKfile::ConvertTime(const FILETIME &temp) const

Писать так, конечно можно, но обьясню, почему не очень хорошо. При вызове этой функции на стеке создается объект класса CString, куда помещается результат. Контроль за освобождением этого объекта целиком ложится на плечи компилятора, + никакой гарантии, что объект(если он достаточно большой) просто не вылезет за пределы стека + это замедляет работу программы. Вообщем строки лучше всего передавать через указатели. Я например бы предидущую функцию обьявил так:
 
Код:
int CKfile::ConvertTime(const FILETIME &temp, CString &res)

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

 
Код:
strcpy(location, path);
location[strlen(location)-1]='\0';

Вторая строка явно лишняя..

+небольшой советик по написанию кода. Лучше всего развить в себе привычку инициализировать переменные в той же строке, где их и объвил. Потом - можно забыть и потратить пару лишних часиков на поиск примитивнейшей ошибки.. А так: объявил - проинициализируй, даже если ты ее через 3 строчки переприсвоишь.
341
28 августа 2008 года
Der Meister
874 / / 21.12.2007
Цитата: Dart Bobr
 
Код:
if(result==false)

тут ты написал лишнее сравнение. сначала сравнивается переменная result с false, а потом полученый булевый результат проверяется на истинность. Гораздо лаконичнее писать так:
 
Код:
if (!result)

Блин, ну с таким ходом мыслей, то на то: bool operator!(bool) работает так же :)[QUOTE=Dart Bobr]Функция:

 
Код:
CString CKfile::ConvertTime(const FILETIME &temp) const

Писать так, конечно можно, но обьясню, почему не очень хорошо.[/QUOTE]Могу ошибаться, но щас, по-моему, глупо беспокоиться из-за каких-то там пятен: подавляющее большинство компиляторов реализует понятие оптимизации возвращаемого значения (а уж майкростофтовый - подавно). То есть, этот вариант, в принципе, работает эквивалентно приведённому тобой.
255
28 августа 2008 года
Dart Bobr
1.4K / / 09.04.2004
Цитата: Der Meister
Могу ошибаться, но щас, по-моему, глупо беспокоиться из-за каких-то там пятен: подавляющее большинство компиляторов реализуют понятие оптимизации возвращаемого значения (а уж майкростофтовый - подавно). То есть, этот вариант, в принципе, работает эквивалентно приведённому тобой.


Не эквивалентно.
В одном случае выделяется еще один объект - то-есть происходит выделение памяти + вызов конструктора.
Во втором - ничего такого не происходит.

Цитата:

Блин, ну с таким ходом мыслей, то на то: bool operator!(bool) работает так же


С чего это вдруг?

341
28 августа 2008 года
Der Meister
874 / / 21.12.2007
[QUOTE=Dart Bobr]С чего это вдруг?[/QUOTE]operator! - операция раз. if () - операция два. Для компилятора: jz (либо иная вариация на тему jump) - операция "только раз" в обоих случаях.[QUOTE=Dart Bobr]Не эквивалентно.
В одном случае выделяется еще один объект - то-есть происходит выделение памяти + вызов конструктора.
Во втором - ничего такого не происходит.[/QUOTE]Эту проблему и её решение в компиляторах раскрывает товарищ Scott Meyers в своей книге More Effective C++. Каюсь: не проверял, но и не слышал о том, чтобы кто-то жаловался на MSVC++ в отношении возврата из функии экземпляров объектов...
14
28 августа 2008 года
Phodopus
3.3K / / 19.06.2008
Цитата: Artem_3A
Укажите пожалуйста узкие места и прочие огрехи, дабы я мог их исправить и впредь не совершать! Всегда рад принять конструктивную критику!=)


Ну так как C/C++ не мой конек, пусть лучше спецы напишут, но лично меня удивило вот что:

 
Код:
list.clear();//чистим списки и вектор

что за list? Если это поле класса не лучше ли его предварить m_ ?
 
Код:
CKillerDlg::searchHiddenFile(searchAdress);//вызываем функцию поиска файлов на диске

почему с префиксом? так задумано?

Наконец, путь delete и не работает (временно), написать его закомментированным нужно (вообще мое правило - пишешь new, сразу пиши delete а код затем вставляй между ними - как и со скобками). еще и какое-нить слово типа BUUUUGGG рядом, чтобы сразу заметно было тебе самому..

 
Код:
register WIN32_FIND_DATAA fn;
FindFirstFileA(searchAdress, &fn);

Опять же register со структурой выглядит коряво, и зачем использовать именно ANSI версии функций/структур, хотя кое-где проглядывают TCHAR? Какие-то проблемы?
 
Код:
catch(int e)
{
  if((fn.dwFileAttributes&FILE_ATTRIBUTE_SYSTEM) != FILE_ATTRIBUTE_SYSTEM)
  {
    CKfile* temp = new CKfile;//если файл скрытый то заполняем класс
    temp->setData(fn, searchAdress);//и длбавляем в вектор
    list.push_back(temp);
  }
}

Очень оригинальное использование исключений.. Лучше бы переделал без них..

Ну вот то что наскоряк
14
28 августа 2008 года
Phodopus
3.3K / / 19.06.2008
Цитата: Der Meister
nor can the register keyword be used on arrays.



слушай, получается на массивах не позволяет, а на структурах позволяет что-ли? Интересно почему..

341
28 августа 2008 года
Der Meister
874 / / 21.12.2007
Цитата: Phodopus
слушай, получается на массивах не позволяет, а на структурах позволяет что-ли? Интересно почему..


Это надо мануалы и стандарты к C без плюсов читать... Интересно глянуть, как отреагирует на register char * pсh = NULL;[QUOTE=Artem_3A]Укажите пожалуйста узкие места и прочие огрехи, дабы я мог их исправить и впредь не совершать! Всегда рад принять конструктивную критику!=)[/QUOTE]Не знаю, многие (хоть и не скажу, что большинство) из приведённых выше доводов лично мне показались не столь существенными, но... Лично я в твой код абсолютно не вникал и даже не смотрел его. Более того, пока и не собераюсь. Причина проста: визуально (возможно, лишь визуально) это каша: ну на абзацы-то надо уж как-нибудь поделить! По смыслу. Как-то отделять логические блоки друг от друга... Сплошной кусок кода в две страницы читается не проще, чем "Война и мир" Толстого (то вообще жесть). Комментарии типа

 
Код:
// проверяем
батарея нескольких подряд идущих if ()
кажется, код нагляднее не делают. Это лично моё мнение, не обижайся: по-видимому, не вникал в него пока только я :).
12K
28 августа 2008 года
lifs
163 / / 06.09.2007
Цитата: Der Meister
Интересно глянуть, как отреагирует на register char * pсh = NULL;



Нормально, только не позволит сделать &f.

255
28 августа 2008 года
Dart Bobr
1.4K / / 09.04.2004
Цитата: Der Meister
operator! - операция раз. if () - операция два. Для компилятора: jz (либо иная вариация на тему jump) - операция "только раз" в обоих случаях.


Вот как раз для компилятора без оптимизации в одном случае происходит:
логическая операция отрицания и условный переход.
Во втором случае:
как раз 2 условных перехода

Цитата: Der Meister

Эту проблему и её решение в компиляторах раскрывает товарищ Scott Meyers в своей книге More Effective C++. Каюсь: не проверял, но и не слышал о том, чтобы кто-то жаловался на MSVC++ в отношении возврата из функии экземпляров объектов...


Решил, и не рекомендует возвращать объекты напрямую, а через указатели, а где возможно - через ссылки. То, что вы не слышали, чтоб кто-то жаловался - не показатель.

341
28 августа 2008 года
Der Meister
874 / / 21.12.2007
Цитата:
Решил, и не рекомендует возвращать объекты напрямую, а через указатели, а где возможно - через ссылки. То, что вы не слышали, чтоб кто-то жаловался - не показатель.

Короче, там суть в том, чтобы возвращать не объект, а параметры конструктора

Код:
// здесь можно положиться на компилятор
string SomeMethod()
{
    return string("some text");
}

// а вот здесь, по идее, уже не факт
string AnotherMethod()
{
    string result("some text");
    return result;
}
Несомненно, что, в общем случае, полагаться на компилятор не следует, но с STL всё вроде работает отлично.[QUOTE=Dart Bobr]Вот как раз для компилятора без оптимизации в одном случае происходит:
логическая операция отрицания и условный переход.
Во втором случае:
как раз 2 условных перехода[/QUOTE]Почему? В первом - дополнение и переход, во втором - сравнение и переход. Это высокоуровненвые операции если рассматривать. Для компилятора в обоих случаях jnz должно быть (без оптимизации).
255
28 августа 2008 года
Dart Bobr
1.4K / / 09.04.2004
Распишем :)
случай 1:
(!result) == true
случай 2:
(result == false) == true
341
28 августа 2008 года
Der Meister
874 / / 21.12.2007
Ну да.
 
Код:
случай 1:
(operator!(result)) == true
случай 2:
(operator==(result, false)) == true
И там, и там - две логических (лучше даже сказать простейших в рамках языка) операции. Никаких переходов. То на то.
При этом, я не собираюсь отрицать нежелательность явной проверки с логическими константами: очень похожий код " == true" - источник многих ошибок. Но, по количеству производимых действий, оба варианта эквивалентны.
255
28 августа 2008 года
Dart Bobr
1.4K / / 09.04.2004
Цитата: Der Meister
Ну да.
 
Код:
случай 1:
(operator!(result)) == true
случай 2:
(operator==(result, false)) == true
И там, и там - две логических (лучше даже сказать простейших в рамках языка) операции. Никаких переходов. То на то.
При этом, я не собираюсь отрицать нежелательность явной проверки с логическими константами: очень похожий код " == true" - источник многих ошибок. Но, по количеству производимых действий, оба варианта эквивалентны.


Но сравнений то в одном случае два, а в другом - одно.
operator! - не сравнение, а логическая операция. Следовательно случай 2 - медленнее, + нелогичнее. Так как зачем проверять дополнительно или условие не выполняется.

341
28 августа 2008 года
Der Meister
874 / / 21.12.2007
[QUOTE=Dart Bobr]operator! - не сравнение, а логическая операция. Следовательно случай 2 - медленнее[/QUOTE]Не понимаю... Ты сейчас пытаешься дифференцировать по производительности две элементарных (атомарных, неделимых), с точки зрения языка, операции, определённых для аргументов одного и того же типа и приводящих к одинаковому результату... Каким принципом ты в этом руководствуешься?[QUOTE=Dart Bobr] + нелогичнее[/QUOTE]Почему? Не вижу нарушений логики ни там, ни там. Мне, например, так было бы удобнее:
 
Код:
if (FileTimeToSystemTime(&temp, &UTC) == false)
чем
 
Код:
if (!(FileTimeToSystemTime(&temp, &UTC)))
если FileTimeToSystemTime() действительно возвращала бы bool (а не BOOL, что есть unsigned char. Автор, привет!) Я к тому, что лично мне одинаково логичен как первый, так и второй вариант, вариант с operator!(), в общем случае, лично для меня выглядит привычней, но, конкретно в приведённом мной примере, удобнее и нагляднее (и исключительно "сугубо" логичней) использовать явное сравнение. Иными словами, я считаю это просто проявлением стиля программирования, и логика здесь, похоже, обуславливается личными предпочтениями программиста.
255
28 августа 2008 года
Dart Bobr
1.4K / / 09.04.2004
Цитата: Der Meister
Не понимаю... Ты сейчас пытаешься дифференцировать по производительности две элементарных (атомарных, неделимых), с точки зрения языка, операции, определённых для аргументов одного и того же типа и приводящих к одинаковому результату... Каким принципом ты в этом руководствуешься?Почему?


Простой логикой. Унарные операции должны выполнятся быстрее бинарных.

Цитата: Der Meister

Не вижу нарушений логики ни там, ни там. Мне, например, так было бы удобнее:
 
Код:
if (FileTimeToSystemTime(&temp, &UTC) == false)
чем
 
Код:
if (!(FileTimeToSystemTime(&temp, &UTC)))
если FileTimeToSystemTime() действительно возвращала бы bool (а не BOOL, что есть unsigned char. Автор, привет!) Я к тому, что лично мне одинаково логичен как первый, так и второй вариант, вариант с operator!(), в общем случае, лично для меня выглядит привычней, но, конкретно в приведённом мной примере, удобнее и нагляднее (и исключительно "сугубо" логичней) использовать явное сравнение. Иными словами, я считаю это просто проявлением стиля программирования, и логика здесь, похоже, обуславливается личными предпочтениями программиста.


Ну, как по мне то логичней проверять именно условие, а не факт равенства(или не равенства) этого же условия какой-то константе. С твоей точке зрения в таком коде:

 
Код:
if (((result == false) == true) == true)

нет ничего лишнего? А, я все же думаю, что дописывание лишних == false или == true - только запутывают логику.
341
29 августа 2008 года
Der Meister
874 / / 21.12.2007
[QUOTE=Dart Bobr]Простой логикой. Унарные операции должны выполнятся быстрее бинарных.[/QUOTE]Логика, в общем-то, правильная, но неверная в этом случае. Как должен[QUOTE=Der Meister]дифференцировать по производительности две элементарных (атомарных, неделимых), с точки зрения языка, операции, определённых для аргументов одного и того же типа и приводящих к одинаковому результату[/QUOTE]компилятор? Должен ли? Будет ли? К оптимизации это не имеет никакого отношения: я подчёркивал, что оба оператора - элементарные, в рамках языка, действия. И интерпретируются оба варианта одинаково как программистом, так и компилятором (более того, компилятором здесь две операции будут свернуты в одну, хоть это и не столь важно).[QUOTE=Dart Bobr]С твоей точке зрения в таком коде:
 
Код:
if (((result == false) == true) == true)

нет ничего лишнего? А я все же думаю, что дописывание лишних == false или == true только запутывают логику.[/QUOTE]Безусловно, код выглядит не лучше, чем
 
Код:
if (!!!result)
и факт запутывания логики программы лишними операциями лежит вне всяких сомнений. Но я не вижу в единичном явном сравнении с false каких-то отклонений от принципов логики.
255
29 августа 2008 года
Dart Bobr
1.4K / / 09.04.2004
Цитата: Der Meister
Логика, в общем-то, правильная, но неверная в этом случае. Как долженкомпилятор? Должен ли? Будет ли? К оптимизации это не имеет никакого отношения: я подчёркивал, что оба оператора - элементарные, в рамках языка, действия. И интерпретируются оба варианта одинаково как программистом, так и компилятором (более того, компилятором здесь две операции будут свернуты в одну, хоть это и не столь важно).


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

 
Код:
cmp result,0
jz nextcode

сравнили и перешли.. все просто

 
Код:
cmp result, 0
jz lb1
mov eax,0
lb1:
mov eax,1
cmp eax,0
jz nextcode

сравнили result с false, запомнили результат, потом проверили условие и перешли.

Мне кажется, что первый способ предпочтительней.

Цитата: Der Meister

Безусловно, код выглядит не лучше, чем
 
Код:
if (!!!result)
и факт запутывания логики программы лишними операциями лежит вне всяких сомнений. Но я не вижу в единичном явном сравнении с false каких-то отклонений от принципов логики.


в единичном, но ЛИШНЕМ сравнении.

307
29 августа 2008 года
Artem_3A
863 / / 11.04.2008
Всем спасибо за советы и замечания! Уже читаю литературу, на поднятые вами темы, и исправляю код.
Еще раз всем спасибо за уместную критику!;)
240
01 сентября 2008 года
aks
2.5K / / 14.07.2006
Цитата: Der Meister
Короче, там суть в том, чтобы возвращать не объект, а параметры конструктора
Код:
// здесь можно положиться на компилятор
string SomeMethod()
{
    return string("some text");
}

// а вот здесь, по идее, уже не факт
string AnotherMethod()
{
    string result("some text");
    return result;
}



В С++ просто есть такая вещь как RVO (Return Value Optimization). Вот первый случай относится как раз к RVO, а второй к NRVO (Named RVO). Посмотреть как это работает, например, можно когда результат возвращаемый функцией инициализирует объект такого типа.
Но есстественно это не обязательное требование к компилятору, а скорее рекомендация, которую он может не выполнять. )

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