SlideShare a Scribd company logo
1 of 6
Download to read offline
Большой брат помогает тебе
Автор: Андрей Карпов

Дата: 13.08.2010

В очередной раз убедился, что программисты пишут программы совершенно безалаберно. И
работают они не благодаря их заслугам, а благодаря удачному стечению обстоятельств и заботе
разработчиков компиляторов в Microsoft или Intel. Да, да, именно они заботятся и в нужный
момент подставляют костылики нашим кривобоким программкам.

Читайте далее байтораздирающую историю про класс CString и дочь его, функцию Format.

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

Я знал, что в Microsoft существует отдел, который занимается вопросами обеспечения
максимальной совместимости новых версий операционных систем со старыми приложениями. В
их базе более 10000 наиболее известных старых программ, которые должны обязательно
работать в новых версиях Windows. Именно благодаря таким усилиям я недавно смог без
проблем поиграть в Heroes of Might and Magic II (игра 1996 года) под управлением 64-битной
Windows Vista. Думаю, игра успешно запустится и в Windows 7. Вот интересные заметки Алексея
Пахунова на тему совместимости [1, 2, 3], очень рекомендую почитать.

Но видимо существуют еще и отделы, которые занимаются тем, чтобы помочь нашему ужасному
коду на Си/Си++ работать, работать и работать. Начну эту историю с самого начала.

Я участвую в разработке инструмента PVS-Studio для анализа исходного кода приложений. Тихо,
товарищи, тихо - это не реклама. В этот раз это точно богоугодное дело, ибо мы начали создавать
бесплатный статический анализатор общего назначения. Пока даже до альфа-версии далеко, но
работы потихоньку идут и когда-нибудь я сделаю про этот анализатор пост. Заговорил я об этом
потому, что мы начали собирать наиболее интересные типовые ошибки и учиться их
диагностировать.

Множество ошибок связано с использованием в программах эллипсисов. Теоретическая справка:

Существуют функции, в описании которых невозможно указать число и типы всех
допустимых параметров. Тогда список формальных параметров завершается эллипсисом (...),
что означает: "и, возможно, еще несколько аргументов". Например:
int printf(const char* ...);

Одной такой неприятной, но легко диагностируемой ошибкой является передача в функцию с
переменным количеством аргументов объекта типа класс, вместо указателя на строку. Вот как
выглядит пример этой ошибки:

wchar_t buf[100];

std::wstring ws(L"12345");
swprintf(buf, L"%s", ws);

Такой код приведет к формированию в буфере белиберды или к аварийному завершению
программы. В реальной программе конечно код будет более запутанный, поэтому просьба - не
надо писать комментарии о том, что в отличие от Visual C++, компилятор GCC проверит аргументы
и предупредит. Строки могут поступать из ресурсов или других функций и проверить ничего не
удастся. Здесь же диагностика проста - в функцию формирования строки передается объект
класса, что и приводит к ошибке.

Корректный вариант кода должен выглядеть так:

wchar_t buf[100];

std::wstring ws(L"12345");

swprintf(buf, L"%s", ws.c_str());


Именно из-за того, что в функции с переменным количеством аргументов можно передать все что
угодно их и не рекомендуют использовать практически во всех книгах по программированию на
языке Си++. Вместо этого предлагается использовать безопасные механизмы, например,
boost::format. Однако рекомендации рекомендациями, а кода с разными printf, sprintf,
CString::Format огромное количество и мы с ним будем жить еще очень долго. Именно поэтому
мы и реализовали диагностическое правило, выявляющее подобные опасные конструкции.

Давайте разберемся теоретически, в чем неверен приведенный выше код. Оказывается он
некорректен дважды.

   1. Несоответствие аргумента заданному формату. Раз мы указываем "%s", то и передать
      должны указатель на строку. Однако теоретически мы можем написать свою функцию
      sprintf, которая будет знать, что ей передан объект класса std::wstring и корректно
      распечатает его. Однако и это невозможно в силу причины номер 2.
   2. Аргументом для эллипсиса "..." может быть только POD-тип. А std::string POD типом не
      является.

Теоретическая справка про POD типы:

POD это аббревиатура от "Plain Old Data", что можно перевести как "Простые данные в стиле
Си". К POD-типам относятся:

   1.   все встроенные арифметические типы (включая wchar_t и bool);
   2.   типы, объявленные с помощью ключевого слова enum;
   3.   указатели;
   4.   POD-структуры (struct или class) и POD-объединения (union), которые удовлетворяют
        нижеприведенным требованиям:
            a. не содержат пользовательских конструкторов, деструктора или копирующего
               оператора присваивания;
            b. не имеют базовых классов;
            c. не содержат виртуальных функций;
            d. не содержат защищенных (protected) или закрытых (private) нестатических
               членов данных;
e. не содержат нестатических членов данных не-POD-типов (или массивов из
              таких типов), а также ссылок.

Соответственно, класс std::wstring к POD-типам не относится, так как у него есть конструкторы,
базовый класс и так далее.

При этом если вы передаете в эллипсис объект, не являющимся POD типом, то это приводит к
неопределенному поведению. Таким образом, по крайней мере, теоретически, мы никак не
можем корректно передать объект типа std::wstring в качестве эллипсис аргумента.

Та же самая картина у нас должна наблюдаться и с функций Format из класса CString.
Некорректный вариант код:

CString s;

CString arg(L"OK");

s.Format(L"Test CString: %sn", arg);

Корректный вариант кода:

s.Format(L"Test CString: %sn", arg.GetString());

Или как предлагается в MSDN [4] для получения указателя на строку можно использовать явный
оператор приведения LPCTSTR, реализованный в классе CString. Пример корректного кода из
MSDN:

CString kindOfFruit = "bananas";

int howmany = 25;

printf("You have %d %sn", howmany, (LPCTSTR)kindOfFruit);

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

Это и было и сделано. И вот здесь я был шокирован результатом. Оказывается большинство
разработчиков вообще никогда не задумываются над этими проблемами и спокойно пишут код
вида:

class CRuleDesc

{

    CString GetProtocol();

    CString GetSrcIp();

    CString GetDestIp();

    CString GetSrcPort();

    CString GetIpDesc(CString strIp);

...
CString CRuleDesc::GetRuleDesc()

{

    CString strDesc;

    strDesc.Format(

      _T("%s all network traffic from <br>%s "

        "on %s<br>to %s on %s <br>for the %s"),

      GetAction(), GetSrcIp(), GetSrcPort(),

      GetDestIp(), GetDestPort(), GetProtocol());

    return strDesc;

}

//---------------



CString strText;

CString _strProcName(L"");

...

strText.Format(_T("%s"), _strProcName);



//---------------



CString m_strDriverDosName;

CString m_strDriverName;

...

m_strDriverDosName.Format(

    _T(".%s"), m_strDriverName);



//---------------



CString __stdcall GetResString(UINT dwStringID);

...
_stprintf(acBuf, _T("%s"),

  GetResString(IDS_SV_SERVERINFO));



//---------------



// Думаю понятно,

// что примеры можно приводить и приводить.


А некоторые и задумываются, но забываются. И поэтому так трогательно смотрится код
следующего вида:

CString sAddr;

CString m_sName;

CString sTo = GetNick( hContact );



sAddr.Format(_T("%smailslot%s"),

  sTo, (LPCTSTR)m_sName);

И таких примеров в проектах, на которых мы тестируем PVS-Studio, оказалась столько, что стало не
понятно, как это вообще может быть. А, тем не менее, это все замечательно работает, в чем я смог
убедиться, написав тестовую программу и попробовав различные варианты использования
CString.

В чем же дело? Видимо разработчики компиляторов не выдержали бесконечных вопросов
почему программы индусов, использующие CString не работают и обвинений в "глючности
компилятора, который неверно работает со строками". И они тихо совершили священный ритуал
экзорцизма, изгнав зло из CString. Они сделали невозможное возможным. А именно класс CString
реализован специальным хитрым образом, так, чтобы его можно было передавать в функции
вида printf, Format.

Сделано это достаточно хитро и кто интересуется, то может почитать исходный код класса
CStringT, а также познакомиться с вот эти развернутым обсуждением "Pass CString to printf?" [5].
Я вдаваться в подробности не буду. Отмечу только важный момент. Специальная реализация
CString не достаточна, теоретически передача не POD-типа приводит к непредсказуемому
поведению. Так вот разработчики Visual C++, а вместе с ними и Intel C++ сделали так, что
непредсказуемое поведение представляет из себя всегда корректный результат. :) Ведь
правильная работа программы вполне себе подмножество непредсказуемого поведения. :)

А еще я теперь начинаю задумываться над некоторыми странными особенностями поведения
компилятора при построении 64-битных программ. Есть подозрение, что разработчики
компилятора сознательно делают поведение программы не теоретическим, а практическим
(работоспособным), в тех простых случаях, когда они распознают некоторый паттерн. Наиболее
понятным примером может быть паттерн цикла. Пример некорректного кода:

size_t n = BigValue;

for (unsigned i = 0; i < n; i++) { ... }

Теоретически, если значение n > UINT_MAX больше, то должен возникнуть бесконечный цикл.
Однако в Release версии он не возникает, так как для переменной "i" используется 64-битный
регистр. Конечно, если код будет посложнее, то бесконечный цикл возникнет, но хотя бы в ряде
случаев программе повезет. Подробнее я писал про это в статье "64-битный конь, который
умеет считать" [6].

Раньше я думал, что такое неожиданно удачное поведение программы связано исключительно с
особенностями оптимизации Release версий. Однако теперь я в этом не уверен. Возможно, это
сознательная попытка хотя бы иногда сделать неработоспособную программу работоспособной.
Конечно, я не знаю, причина в оптимизации или в заботе большого брата, но это волне повод
пофилософствовать. :) Ну а кто знает, тот вряд ли скажет. :)

Уверен, что есть и другие моменты, когда компилятор подставляет руку программам калекам.
Если попадется что-то еще интересно, обязательно расскажу.

Желаю вам безглючного кода!


Библиографический список
   1. Блог Алексея Пахунова. Обратная совместимость это серьезно.
      http://www.viva64.com/go.php?url=390
   2. Блог Алексея Пахунова. AppCompat. http://www.viva64.com/go.php?url=391
   3. Блог Алексея Пахунова. Windows 3.x жив? http://www.viva64.com/go.php?url=392
   4. MSDN. CString Operations Relating to C-Style Strings. Topic: Using CString Objects with Variable
      Argument Functions . http://www.viva64.com/go.php?url=393
   5. Обсуждение на сайте eggheadcafe.com. Pass CString to printf?
      http://www.viva64.com/go.php?url=394
   6. Андрей Карпов. 64-битный конь, который умеет считать. http://www.viva64.com/art-1-1-
      1064884779.html

More Related Content

What's hot

Что такое size_t и ptrdiff_t
Что такое size_t и ptrdiff_tЧто такое size_t и ptrdiff_t
Что такое size_t и ptrdiff_tTatyanazaxarova
 
А существуют ли в реальности 64-битные ошибки?
А  существуют ли в реальности 64-битные ошибки?А  существуют ли в реальности 64-битные ошибки?
А существуют ли в реальности 64-битные ошибки?Tatyanazaxarova
 
20 ловушек переноса Си++ - кода на 64-битную платформу
20 ловушек переноса Си++ - кода на 64-битную платформу20 ловушек переноса Си++ - кода на 64-битную платформу
20 ловушек переноса Си++ - кода на 64-битную платформуTatyanazaxarova
 
Интервью с Дмитрием Вьюковым – автором верификатора Relacy Race Detector (RRD)
Интервью с Дмитрием Вьюковым – автором верификатора Relacy Race Detector (RRD)Интервью с Дмитрием Вьюковым – автором верификатора Relacy Race Detector (RRD)
Интервью с Дмитрием Вьюковым – автором верификатора Relacy Race Detector (RRD)Tatyanazaxarova
 
C++ осень 2012 лекция 6
C++ осень 2012 лекция 6C++ осень 2012 лекция 6
C++ осень 2012 лекция 6Technopark
 
Урок 13. Паттерн 5. Адресная арифметика
Урок 13. Паттерн 5. Адресная арифметикаУрок 13. Паттерн 5. Адресная арифметика
Урок 13. Паттерн 5. Адресная арифметикаTatyanazaxarova
 
Как уменьшить вероятность ошибки на этапе написания кода. Заметка N1.
Как уменьшить вероятность ошибки на этапе написания кода. Заметка N1.Как уменьшить вероятность ошибки на этапе написания кода. Заметка N1.
Как уменьшить вероятность ошибки на этапе написания кода. Заметка N1.Tatyanazaxarova
 
Разработка статического анализатора кода для обнаружения ошибок переноса прог...
Разработка статического анализатора кода для обнаружения ошибок переноса прог...Разработка статического анализатора кода для обнаружения ошибок переноса прог...
Разработка статического анализатора кода для обнаружения ошибок переноса прог...Tatyanazaxarova
 
Урок 10. Паттерн 2. Функции с переменным количеством аргументов
Урок 10. Паттерн 2. Функции с переменным количеством аргументовУрок 10. Паттерн 2. Функции с переменным количеством аргументов
Урок 10. Паттерн 2. Функции с переменным количеством аргументовTatyanazaxarova
 
Антон Потапов — С++ контейнеры и многопоточность: вместе или врозь?
Антон Потапов — С++ контейнеры и многопоточность: вместе или врозь?Антон Потапов — С++ контейнеры и многопоточность: вместе или врозь?
Антон Потапов — С++ контейнеры и многопоточность: вместе или врозь?Yandex
 
PVS-Studio, решение для разработки современных ресурсоемких приложений
PVS-Studio, решение для разработки современных ресурсоемких приложенийPVS-Studio, решение для разработки современных ресурсоемких приложений
PVS-Studio, решение для разработки современных ресурсоемких приложенийTatyanazaxarova
 
Статический анализ кода для верификации 64-битных приложений
Статический анализ кода для верификации 64-битных приложенийСтатический анализ кода для верификации 64-битных приложений
Статический анализ кода для верификации 64-битных приложенийTatyanazaxarova
 
Мульти-блиц выступление на Стачка-2012
Мульти-блиц выступление на Стачка-2012Мульти-блиц выступление на Стачка-2012
Мульти-блиц выступление на Стачка-2012Alexey Mahotkin
 
Андрей Карпов, Приватные байки от разработчиков анализатора кода
Андрей Карпов, Приватные байки от разработчиков анализатора кодаАндрей Карпов, Приватные байки от разработчиков анализатора кода
Андрей Карпов, Приватные байки от разработчиков анализатора кодаSergey Platonov
 
Дмитрий Прокопцев "Memory-mapped storage: ещё один подход к сериализации данных"
Дмитрий Прокопцев "Memory-mapped storage: ещё один подход к сериализации данных"Дмитрий Прокопцев "Memory-mapped storage: ещё один подход к сериализации данных"
Дмитрий Прокопцев "Memory-mapped storage: ещё один подход к сериализации данных"Yandex
 
Александр Гладыш — Visual editor for business logic in Lua and JS
Александр Гладыш — Visual editor for business logic in Lua and JSАлександр Гладыш — Visual editor for business logic in Lua and JS
Александр Гладыш — Visual editor for business logic in Lua and JSYury Yurevich
 
Deep c slides_oct2011_rus
Deep c slides_oct2011_rusDeep c slides_oct2011_rus
Deep c slides_oct2011_rusGarrikus
 
C++ весна 2014 лекция 2
C++ весна 2014 лекция 2C++ весна 2014 лекция 2
C++ весна 2014 лекция 2Technopark
 
C++ осень 2013 лекция 7
C++ осень 2013 лекция 7C++ осень 2013 лекция 7
C++ осень 2013 лекция 7Technopark
 
My talk at YouCon Saratov 2016
My talk at YouCon Saratov 2016My talk at YouCon Saratov 2016
My talk at YouCon Saratov 2016Alex Chistyakov
 

What's hot (20)

Что такое size_t и ptrdiff_t
Что такое size_t и ptrdiff_tЧто такое size_t и ptrdiff_t
Что такое size_t и ptrdiff_t
 
А существуют ли в реальности 64-битные ошибки?
А  существуют ли в реальности 64-битные ошибки?А  существуют ли в реальности 64-битные ошибки?
А существуют ли в реальности 64-битные ошибки?
 
20 ловушек переноса Си++ - кода на 64-битную платформу
20 ловушек переноса Си++ - кода на 64-битную платформу20 ловушек переноса Си++ - кода на 64-битную платформу
20 ловушек переноса Си++ - кода на 64-битную платформу
 
Интервью с Дмитрием Вьюковым – автором верификатора Relacy Race Detector (RRD)
Интервью с Дмитрием Вьюковым – автором верификатора Relacy Race Detector (RRD)Интервью с Дмитрием Вьюковым – автором верификатора Relacy Race Detector (RRD)
Интервью с Дмитрием Вьюковым – автором верификатора Relacy Race Detector (RRD)
 
C++ осень 2012 лекция 6
C++ осень 2012 лекция 6C++ осень 2012 лекция 6
C++ осень 2012 лекция 6
 
Урок 13. Паттерн 5. Адресная арифметика
Урок 13. Паттерн 5. Адресная арифметикаУрок 13. Паттерн 5. Адресная арифметика
Урок 13. Паттерн 5. Адресная арифметика
 
Как уменьшить вероятность ошибки на этапе написания кода. Заметка N1.
Как уменьшить вероятность ошибки на этапе написания кода. Заметка N1.Как уменьшить вероятность ошибки на этапе написания кода. Заметка N1.
Как уменьшить вероятность ошибки на этапе написания кода. Заметка N1.
 
Разработка статического анализатора кода для обнаружения ошибок переноса прог...
Разработка статического анализатора кода для обнаружения ошибок переноса прог...Разработка статического анализатора кода для обнаружения ошибок переноса прог...
Разработка статического анализатора кода для обнаружения ошибок переноса прог...
 
Урок 10. Паттерн 2. Функции с переменным количеством аргументов
Урок 10. Паттерн 2. Функции с переменным количеством аргументовУрок 10. Паттерн 2. Функции с переменным количеством аргументов
Урок 10. Паттерн 2. Функции с переменным количеством аргументов
 
Антон Потапов — С++ контейнеры и многопоточность: вместе или врозь?
Антон Потапов — С++ контейнеры и многопоточность: вместе или врозь?Антон Потапов — С++ контейнеры и многопоточность: вместе или врозь?
Антон Потапов — С++ контейнеры и многопоточность: вместе или врозь?
 
PVS-Studio, решение для разработки современных ресурсоемких приложений
PVS-Studio, решение для разработки современных ресурсоемких приложенийPVS-Studio, решение для разработки современных ресурсоемких приложений
PVS-Studio, решение для разработки современных ресурсоемких приложений
 
Статический анализ кода для верификации 64-битных приложений
Статический анализ кода для верификации 64-битных приложенийСтатический анализ кода для верификации 64-битных приложений
Статический анализ кода для верификации 64-битных приложений
 
Мульти-блиц выступление на Стачка-2012
Мульти-блиц выступление на Стачка-2012Мульти-блиц выступление на Стачка-2012
Мульти-блиц выступление на Стачка-2012
 
Андрей Карпов, Приватные байки от разработчиков анализатора кода
Андрей Карпов, Приватные байки от разработчиков анализатора кодаАндрей Карпов, Приватные байки от разработчиков анализатора кода
Андрей Карпов, Приватные байки от разработчиков анализатора кода
 
Дмитрий Прокопцев "Memory-mapped storage: ещё один подход к сериализации данных"
Дмитрий Прокопцев "Memory-mapped storage: ещё один подход к сериализации данных"Дмитрий Прокопцев "Memory-mapped storage: ещё один подход к сериализации данных"
Дмитрий Прокопцев "Memory-mapped storage: ещё один подход к сериализации данных"
 
Александр Гладыш — Visual editor for business logic in Lua and JS
Александр Гладыш — Visual editor for business logic in Lua and JSАлександр Гладыш — Visual editor for business logic in Lua and JS
Александр Гладыш — Visual editor for business logic in Lua and JS
 
Deep c slides_oct2011_rus
Deep c slides_oct2011_rusDeep c slides_oct2011_rus
Deep c slides_oct2011_rus
 
C++ весна 2014 лекция 2
C++ весна 2014 лекция 2C++ весна 2014 лекция 2
C++ весна 2014 лекция 2
 
C++ осень 2013 лекция 7
C++ осень 2013 лекция 7C++ осень 2013 лекция 7
C++ осень 2013 лекция 7
 
My talk at YouCon Saratov 2016
My talk at YouCon Saratov 2016My talk at YouCon Saratov 2016
My talk at YouCon Saratov 2016
 

Viewers also liked (20)

Plantilla y contenido ponencia formando para pensar pensar para transformar
Plantilla y contenido ponencia formando para pensar pensar para transformarPlantilla y contenido ponencia formando para pensar pensar para transformar
Plantilla y contenido ponencia formando para pensar pensar para transformar
 
The next big thing .
The next big thing .The next big thing .
The next big thing .
 
10conversations
10conversations10conversations
10conversations
 
Dogs
DogsDogs
Dogs
 
Analysis one night only2
Analysis one night only2Analysis one night only2
Analysis one night only2
 
Virtual Makeover 2.0
Virtual Makeover 2.0Virtual Makeover 2.0
Virtual Makeover 2.0
 
My dog error
My dog errorMy dog error
My dog error
 
Olympic games 1º y 2º
Olympic games 1º y 2ºOlympic games 1º y 2º
Olympic games 1º y 2º
 
Memorias
MemoriasMemorias
Memorias
 
Masabe ejercicios unid 3
Masabe ejercicios unid 3Masabe ejercicios unid 3
Masabe ejercicios unid 3
 
Cv
CvCv
Cv
 
Talento humano
Talento humanoTalento humano
Talento humano
 
Identifying Your "Middle Child Donors"
Identifying Your "Middle Child Donors"Identifying Your "Middle Child Donors"
Identifying Your "Middle Child Donors"
 
Avance dic 2012[1]
Avance dic 2012[1]Avance dic 2012[1]
Avance dic 2012[1]
 
Tsim sudalgaa
Tsim sudalgaaTsim sudalgaa
Tsim sudalgaa
 
Varicela zoster
Varicela zosterVaricela zoster
Varicela zoster
 
Encuesta de opinión
Encuesta de opiniónEncuesta de opinión
Encuesta de opinión
 
Cartoon classic
Cartoon classicCartoon classic
Cartoon classic
 
Location Change
Location ChangeLocation Change
Location Change
 
Test
TestTest
Test
 

Similar to Большой брат помогает тебе

Статический анализ Си++ кода и новый стандарт языка C++0x
Статический анализ Си++ кода и новый стандарт языка C++0xСтатический анализ Си++ кода и новый стандарт языка C++0x
Статический анализ Си++ кода и новый стандарт языка C++0xTatyanazaxarova
 
Разница в подходах анализа кода компилятором и выделенным инструментом
Разница в подходах анализа кода компилятором и выделенным инструментомРазница в подходах анализа кода компилятором и выделенным инструментом
Разница в подходах анализа кода компилятором и выделенным инструментомTatyanazaxarova
 
Принципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-StudioПринципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-StudioAndrey Karpov
 
static - defcon russia 20
static  - defcon russia 20static  - defcon russia 20
static - defcon russia 20DefconRussia
 
Статический анализ и регулярные выражения
Статический анализ и регулярные выраженияСтатический анализ и регулярные выражения
Статический анализ и регулярные выраженияTatyanazaxarova
 
64-битный конь, который умеет считать
64-битный конь, который умеет считать64-битный конь, который умеет считать
64-битный конь, который умеет считатьTatyanazaxarova
 
Как стандарт C++0x поможет в борьбе с 64-битными ошибками
Как стандарт C++0x поможет в борьбе с 64-битными ошибкамиКак стандарт C++0x поможет в борьбе с 64-битными ошибками
Как стандарт C++0x поможет в борьбе с 64-битными ошибкамиTatyanazaxarova
 
Урок 5. Сборка 64-битного приложения
Урок 5. Сборка 64-битного приложенияУрок 5. Сборка 64-битного приложения
Урок 5. Сборка 64-битного приложенияTatyanazaxarova
 
Цена ошибки
Цена ошибкиЦена ошибки
Цена ошибкиAndrey Karpov
 
Android: Как написать приложение, которое не тормозит
Android: Как  написать приложение, которое не тормозитAndroid: Как  написать приложение, которое не тормозит
Android: Как написать приложение, которое не тормозитElena Kotina
 
CodeFest 2014. Пугачев С. — Язык TypeScript или JavaScript на стероидах
CodeFest 2014. Пугачев С. — Язык TypeScript или JavaScript на стероидахCodeFest 2014. Пугачев С. — Язык TypeScript или JavaScript на стероидах
CodeFest 2014. Пугачев С. — Язык TypeScript или JavaScript на стероидахCodeFest
 
Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?Andrey Karpov
 
СИ++ УМЕР. ДА ЗДРАВСТВУЕТ СИ++
СИ++ УМЕР. ДА ЗДРАВСТВУЕТ СИ++СИ++ УМЕР. ДА ЗДРАВСТВУЕТ СИ++
СИ++ УМЕР. ДА ЗДРАВСТВУЕТ СИ++Pavel Tsukanov
 
PVS-Studio. Статический анализатор кода. Windows/Linux, C/C++/C#
PVS-Studio. Статический анализатор кода. Windows/Linux, C/C++/C#PVS-Studio. Статический анализатор кода. Windows/Linux, C/C++/C#
PVS-Studio. Статический анализатор кода. Windows/Linux, C/C++/C#Andrey Karpov
 
WebCamp 2016: Python.Максим Климишин.Типизированный Python
WebCamp 2016: Python.Максим Климишин.Типизированный PythonWebCamp 2016: Python.Максим Климишин.Типизированный Python
WebCamp 2016: Python.Максим Климишин.Типизированный PythonWebCamp
 
Урок 17. Паттерн 9. Смешанная арифметика
Урок 17. Паттерн 9. Смешанная арифметикаУрок 17. Паттерн 9. Смешанная арифметика
Урок 17. Паттерн 9. Смешанная арифметикаTatyanazaxarova
 

Similar to Большой брат помогает тебе (20)

Статический анализ Си++ кода и новый стандарт языка C++0x
Статический анализ Си++ кода и новый стандарт языка C++0xСтатический анализ Си++ кода и новый стандарт языка C++0x
Статический анализ Си++ кода и новый стандарт языка C++0x
 
Разница в подходах анализа кода компилятором и выделенным инструментом
Разница в подходах анализа кода компилятором и выделенным инструментомРазница в подходах анализа кода компилятором и выделенным инструментом
Разница в подходах анализа кода компилятором и выделенным инструментом
 
Принципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-StudioПринципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-Studio
 
static - defcon russia 20
static  - defcon russia 20static  - defcon russia 20
static - defcon russia 20
 
Статический анализ и регулярные выражения
Статический анализ и регулярные выраженияСтатический анализ и регулярные выражения
Статический анализ и регулярные выражения
 
64-битный конь, который умеет считать
64-битный конь, который умеет считать64-битный конь, который умеет считать
64-битный конь, который умеет считать
 
Как стандарт C++0x поможет в борьбе с 64-битными ошибками
Как стандарт C++0x поможет в борьбе с 64-битными ошибкамиКак стандарт C++0x поможет в борьбе с 64-битными ошибками
Как стандарт C++0x поможет в борьбе с 64-битными ошибками
 
Урок 5. Сборка 64-битного приложения
Урок 5. Сборка 64-битного приложенияУрок 5. Сборка 64-битного приложения
Урок 5. Сборка 64-битного приложения
 
Цена ошибки
Цена ошибкиЦена ошибки
Цена ошибки
 
Цена ошибки
Цена ошибкиЦена ошибки
Цена ошибки
 
Android: Как написать приложение, которое не тормозит
Android: Как  написать приложение, которое не тормозитAndroid: Как  написать приложение, которое не тормозит
Android: Как написать приложение, которое не тормозит
 
CodeFest 2014. Пугачев С. — Язык TypeScript или JavaScript на стероидах
CodeFest 2014. Пугачев С. — Язык TypeScript или JavaScript на стероидахCodeFest 2014. Пугачев С. — Язык TypeScript или JavaScript на стероидах
CodeFest 2014. Пугачев С. — Язык TypeScript или JavaScript на стероидах
 
Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?
 
СИ++ УМЕР. ДА ЗДРАВСТВУЕТ СИ++
СИ++ УМЕР. ДА ЗДРАВСТВУЕТ СИ++СИ++ УМЕР. ДА ЗДРАВСТВУЕТ СИ++
СИ++ УМЕР. ДА ЗДРАВСТВУЕТ СИ++
 
PVS-Studio. Статический анализатор кода. Windows/Linux, C/C++/C#
PVS-Studio. Статический анализатор кода. Windows/Linux, C/C++/C#PVS-Studio. Статический анализатор кода. Windows/Linux, C/C++/C#
PVS-Studio. Статический анализатор кода. Windows/Linux, C/C++/C#
 
Rgsu04
Rgsu04Rgsu04
Rgsu04
 
Rgsu04
Rgsu04Rgsu04
Rgsu04
 
WebCamp 2016: Python.Максим Климишин.Типизированный Python
WebCamp 2016: Python.Максим Климишин.Типизированный PythonWebCamp 2016: Python.Максим Климишин.Типизированный Python
WebCamp 2016: Python.Максим Климишин.Типизированный Python
 
Js fuckworks
Js fuckworksJs fuckworks
Js fuckworks
 
Урок 17. Паттерн 9. Смешанная арифметика
Урок 17. Паттерн 9. Смешанная арифметикаУрок 17. Паттерн 9. Смешанная арифметика
Урок 17. Паттерн 9. Смешанная арифметика
 

More from Tatyanazaxarova

Урок 27. Особенности создания инсталляторов для 64-битного окружения
Урок 27. Особенности создания инсталляторов для 64-битного окруженияУрок 27. Особенности создания инсталляторов для 64-битного окружения
Урок 27. Особенности создания инсталляторов для 64-битного окруженияTatyanazaxarova
 
Урок 26. Оптимизация 64-битных программ
Урок 26. Оптимизация 64-битных программУрок 26. Оптимизация 64-битных программ
Урок 26. Оптимизация 64-битных программTatyanazaxarova
 
Урок 25. Практическое знакомство с паттернами 64-битных ошибок
Урок 25. Практическое знакомство с паттернами 64-битных ошибокУрок 25. Практическое знакомство с паттернами 64-битных ошибок
Урок 25. Практическое знакомство с паттернами 64-битных ошибокTatyanazaxarova
 
Урок 24. Фантомные ошибки
Урок 24. Фантомные ошибкиУрок 24. Фантомные ошибки
Урок 24. Фантомные ошибкиTatyanazaxarova
 
Урок 23. Паттерн 15. Рост размеров структур
Урок 23. Паттерн 15. Рост размеров структурУрок 23. Паттерн 15. Рост размеров структур
Урок 23. Паттерн 15. Рост размеров структурTatyanazaxarova
 
Урок 21. Паттерн 13. Выравнивание данных
Урок 21. Паттерн 13. Выравнивание данныхУрок 21. Паттерн 13. Выравнивание данных
Урок 21. Паттерн 13. Выравнивание данныхTatyanazaxarova
 
Урок 20. Паттерн 12. Исключения
Урок 20. Паттерн 12. ИсключенияУрок 20. Паттерн 12. Исключения
Урок 20. Паттерн 12. ИсключенияTatyanazaxarova
 
Урок 19. Паттерн 11. Сериализация и обмен данными
Урок 19. Паттерн 11. Сериализация и обмен даннымиУрок 19. Паттерн 11. Сериализация и обмен данными
Урок 19. Паттерн 11. Сериализация и обмен даннымиTatyanazaxarova
 
Урок 16. Паттерн 8. Memsize-типы в объединениях
Урок 16. Паттерн 8. Memsize-типы в объединенияхУрок 16. Паттерн 8. Memsize-типы в объединениях
Урок 16. Паттерн 8. Memsize-типы в объединенияхTatyanazaxarova
 
Урок 15. Паттерн 7. Упаковка указателей
Урок 15. Паттерн 7. Упаковка указателейУрок 15. Паттерн 7. Упаковка указателей
Урок 15. Паттерн 7. Упаковка указателейTatyanazaxarova
 
Урок 11. Паттерн 3. Операции сдвига
Урок 11. Паттерн 3. Операции сдвигаУрок 11. Паттерн 3. Операции сдвига
Урок 11. Паттерн 3. Операции сдвигаTatyanazaxarova
 
Урок 9. Паттерн 1. Магические числа
Урок 9. Паттерн 1. Магические числаУрок 9. Паттерн 1. Магические числа
Урок 9. Паттерн 1. Магические числаTatyanazaxarova
 
Урок 8. Статический анализ для выявления 64-битных ошибок
Урок 8. Статический анализ для выявления 64-битных ошибокУрок 8. Статический анализ для выявления 64-битных ошибок
Урок 8. Статический анализ для выявления 64-битных ошибокTatyanazaxarova
 
Урок 7. Проблемы выявления 64-битных ошибок
Урок 7. Проблемы выявления 64-битных ошибокУрок 7. Проблемы выявления 64-битных ошибок
Урок 7. Проблемы выявления 64-битных ошибокTatyanazaxarova
 
Урок 6. Ошибки в 64-битном коде
Урок 6. Ошибки в 64-битном кодеУрок 6. Ошибки в 64-битном коде
Урок 6. Ошибки в 64-битном кодеTatyanazaxarova
 
Урок 4. Создание 64-битной конфигурации
Урок 4. Создание 64-битной конфигурацииУрок 4. Создание 64-битной конфигурации
Урок 4. Создание 64-битной конфигурацииTatyanazaxarova
 
Статический анализ Си++ кода
Статический анализ Си++ кодаСтатический анализ Си++ кода
Статический анализ Си++ кодаTatyanazaxarova
 
PVS-Studio научился следить за тем, как вы программируете
PVS-Studio научился следить за тем, как вы программируетеPVS-Studio научился следить за тем, как вы программируете
PVS-Studio научился следить за тем, как вы программируетеTatyanazaxarova
 
Пояснения к статье про Copy-Paste
Пояснения к статье про Copy-PasteПояснения к статье про Copy-Paste
Пояснения к статье про Copy-PasteTatyanazaxarova
 

More from Tatyanazaxarova (20)

Урок 27. Особенности создания инсталляторов для 64-битного окружения
Урок 27. Особенности создания инсталляторов для 64-битного окруженияУрок 27. Особенности создания инсталляторов для 64-битного окружения
Урок 27. Особенности создания инсталляторов для 64-битного окружения
 
Урок 26. Оптимизация 64-битных программ
Урок 26. Оптимизация 64-битных программУрок 26. Оптимизация 64-битных программ
Урок 26. Оптимизация 64-битных программ
 
Урок 25. Практическое знакомство с паттернами 64-битных ошибок
Урок 25. Практическое знакомство с паттернами 64-битных ошибокУрок 25. Практическое знакомство с паттернами 64-битных ошибок
Урок 25. Практическое знакомство с паттернами 64-битных ошибок
 
Урок 24. Фантомные ошибки
Урок 24. Фантомные ошибкиУрок 24. Фантомные ошибки
Урок 24. Фантомные ошибки
 
Урок 23. Паттерн 15. Рост размеров структур
Урок 23. Паттерн 15. Рост размеров структурУрок 23. Паттерн 15. Рост размеров структур
Урок 23. Паттерн 15. Рост размеров структур
 
Урок 21. Паттерн 13. Выравнивание данных
Урок 21. Паттерн 13. Выравнивание данныхУрок 21. Паттерн 13. Выравнивание данных
Урок 21. Паттерн 13. Выравнивание данных
 
Урок 20. Паттерн 12. Исключения
Урок 20. Паттерн 12. ИсключенияУрок 20. Паттерн 12. Исключения
Урок 20. Паттерн 12. Исключения
 
Урок 19. Паттерн 11. Сериализация и обмен данными
Урок 19. Паттерн 11. Сериализация и обмен даннымиУрок 19. Паттерн 11. Сериализация и обмен данными
Урок 19. Паттерн 11. Сериализация и обмен данными
 
Урок 16. Паттерн 8. Memsize-типы в объединениях
Урок 16. Паттерн 8. Memsize-типы в объединенияхУрок 16. Паттерн 8. Memsize-типы в объединениях
Урок 16. Паттерн 8. Memsize-типы в объединениях
 
Урок 15. Паттерн 7. Упаковка указателей
Урок 15. Паттерн 7. Упаковка указателейУрок 15. Паттерн 7. Упаковка указателей
Урок 15. Паттерн 7. Упаковка указателей
 
Урок 11. Паттерн 3. Операции сдвига
Урок 11. Паттерн 3. Операции сдвигаУрок 11. Паттерн 3. Операции сдвига
Урок 11. Паттерн 3. Операции сдвига
 
Урок 9. Паттерн 1. Магические числа
Урок 9. Паттерн 1. Магические числаУрок 9. Паттерн 1. Магические числа
Урок 9. Паттерн 1. Магические числа
 
Урок 8. Статический анализ для выявления 64-битных ошибок
Урок 8. Статический анализ для выявления 64-битных ошибокУрок 8. Статический анализ для выявления 64-битных ошибок
Урок 8. Статический анализ для выявления 64-битных ошибок
 
Урок 7. Проблемы выявления 64-битных ошибок
Урок 7. Проблемы выявления 64-битных ошибокУрок 7. Проблемы выявления 64-битных ошибок
Урок 7. Проблемы выявления 64-битных ошибок
 
Урок 6. Ошибки в 64-битном коде
Урок 6. Ошибки в 64-битном кодеУрок 6. Ошибки в 64-битном коде
Урок 6. Ошибки в 64-битном коде
 
Урок 4. Создание 64-битной конфигурации
Урок 4. Создание 64-битной конфигурацииУрок 4. Создание 64-битной конфигурации
Урок 4. Создание 64-битной конфигурации
 
Статический анализ Си++ кода
Статический анализ Си++ кодаСтатический анализ Си++ кода
Статический анализ Си++ кода
 
PVS-Studio
PVS-Studio PVS-Studio
PVS-Studio
 
PVS-Studio научился следить за тем, как вы программируете
PVS-Studio научился следить за тем, как вы программируетеPVS-Studio научился следить за тем, как вы программируете
PVS-Studio научился следить за тем, как вы программируете
 
Пояснения к статье про Copy-Paste
Пояснения к статье про Copy-PasteПояснения к статье про Copy-Paste
Пояснения к статье про Copy-Paste
 

Большой брат помогает тебе

  • 1. Большой брат помогает тебе Автор: Андрей Карпов Дата: 13.08.2010 В очередной раз убедился, что программисты пишут программы совершенно безалаберно. И работают они не благодаря их заслугам, а благодаря удачному стечению обстоятельств и заботе разработчиков компиляторов в Microsoft или Intel. Да, да, именно они заботятся и в нужный момент подставляют костылики нашим кривобоким программкам. Читайте далее байтораздирающую историю про класс CString и дочь его, функцию Format. Молитесь, молитесь на компиляторы и их разработчиков. Они столько сил прилагают, чтобы наши программы работали, несмотря на многие недостатки и даже ошибки. Причем эта их работа трудна и не видна. Они - благородные рыцари кодирования и ангелы-покровители для всех нас. Я знал, что в Microsoft существует отдел, который занимается вопросами обеспечения максимальной совместимости новых версий операционных систем со старыми приложениями. В их базе более 10000 наиболее известных старых программ, которые должны обязательно работать в новых версиях Windows. Именно благодаря таким усилиям я недавно смог без проблем поиграть в Heroes of Might and Magic II (игра 1996 года) под управлением 64-битной Windows Vista. Думаю, игра успешно запустится и в Windows 7. Вот интересные заметки Алексея Пахунова на тему совместимости [1, 2, 3], очень рекомендую почитать. Но видимо существуют еще и отделы, которые занимаются тем, чтобы помочь нашему ужасному коду на Си/Си++ работать, работать и работать. Начну эту историю с самого начала. Я участвую в разработке инструмента PVS-Studio для анализа исходного кода приложений. Тихо, товарищи, тихо - это не реклама. В этот раз это точно богоугодное дело, ибо мы начали создавать бесплатный статический анализатор общего назначения. Пока даже до альфа-версии далеко, но работы потихоньку идут и когда-нибудь я сделаю про этот анализатор пост. Заговорил я об этом потому, что мы начали собирать наиболее интересные типовые ошибки и учиться их диагностировать. Множество ошибок связано с использованием в программах эллипсисов. Теоретическая справка: Существуют функции, в описании которых невозможно указать число и типы всех допустимых параметров. Тогда список формальных параметров завершается эллипсисом (...), что означает: "и, возможно, еще несколько аргументов". Например: int printf(const char* ...); Одной такой неприятной, но легко диагностируемой ошибкой является передача в функцию с переменным количеством аргументов объекта типа класс, вместо указателя на строку. Вот как выглядит пример этой ошибки: wchar_t buf[100]; std::wstring ws(L"12345");
  • 2. swprintf(buf, L"%s", ws); Такой код приведет к формированию в буфере белиберды или к аварийному завершению программы. В реальной программе конечно код будет более запутанный, поэтому просьба - не надо писать комментарии о том, что в отличие от Visual C++, компилятор GCC проверит аргументы и предупредит. Строки могут поступать из ресурсов или других функций и проверить ничего не удастся. Здесь же диагностика проста - в функцию формирования строки передается объект класса, что и приводит к ошибке. Корректный вариант кода должен выглядеть так: wchar_t buf[100]; std::wstring ws(L"12345"); swprintf(buf, L"%s", ws.c_str()); Именно из-за того, что в функции с переменным количеством аргументов можно передать все что угодно их и не рекомендуют использовать практически во всех книгах по программированию на языке Си++. Вместо этого предлагается использовать безопасные механизмы, например, boost::format. Однако рекомендации рекомендациями, а кода с разными printf, sprintf, CString::Format огромное количество и мы с ним будем жить еще очень долго. Именно поэтому мы и реализовали диагностическое правило, выявляющее подобные опасные конструкции. Давайте разберемся теоретически, в чем неверен приведенный выше код. Оказывается он некорректен дважды. 1. Несоответствие аргумента заданному формату. Раз мы указываем "%s", то и передать должны указатель на строку. Однако теоретически мы можем написать свою функцию sprintf, которая будет знать, что ей передан объект класса std::wstring и корректно распечатает его. Однако и это невозможно в силу причины номер 2. 2. Аргументом для эллипсиса "..." может быть только POD-тип. А std::string POD типом не является. Теоретическая справка про POD типы: POD это аббревиатура от "Plain Old Data", что можно перевести как "Простые данные в стиле Си". К POD-типам относятся: 1. все встроенные арифметические типы (включая wchar_t и bool); 2. типы, объявленные с помощью ключевого слова enum; 3. указатели; 4. POD-структуры (struct или class) и POD-объединения (union), которые удовлетворяют нижеприведенным требованиям: a. не содержат пользовательских конструкторов, деструктора или копирующего оператора присваивания; b. не имеют базовых классов; c. не содержат виртуальных функций; d. не содержат защищенных (protected) или закрытых (private) нестатических членов данных;
  • 3. e. не содержат нестатических членов данных не-POD-типов (или массивов из таких типов), а также ссылок. Соответственно, класс std::wstring к POD-типам не относится, так как у него есть конструкторы, базовый класс и так далее. При этом если вы передаете в эллипсис объект, не являющимся POD типом, то это приводит к неопределенному поведению. Таким образом, по крайней мере, теоретически, мы никак не можем корректно передать объект типа std::wstring в качестве эллипсис аргумента. Та же самая картина у нас должна наблюдаться и с функций Format из класса CString. Некорректный вариант код: CString s; CString arg(L"OK"); s.Format(L"Test CString: %sn", arg); Корректный вариант кода: s.Format(L"Test CString: %sn", arg.GetString()); Или как предлагается в MSDN [4] для получения указателя на строку можно использовать явный оператор приведения LPCTSTR, реализованный в классе CString. Пример корректного кода из MSDN: CString kindOfFruit = "bananas"; int howmany = 25; printf("You have %d %sn", howmany, (LPCTSTR)kindOfFruit); Итак, вроде бы все прозрачно и понятно. Как сделать правило тоже ясно. Будем обнаруживать опечатки при использовании функций с переменным количеством аргументов. Это и было и сделано. И вот здесь я был шокирован результатом. Оказывается большинство разработчиков вообще никогда не задумываются над этими проблемами и спокойно пишут код вида: class CRuleDesc { CString GetProtocol(); CString GetSrcIp(); CString GetDestIp(); CString GetSrcPort(); CString GetIpDesc(CString strIp); ...
  • 4. CString CRuleDesc::GetRuleDesc() { CString strDesc; strDesc.Format( _T("%s all network traffic from <br>%s " "on %s<br>to %s on %s <br>for the %s"), GetAction(), GetSrcIp(), GetSrcPort(), GetDestIp(), GetDestPort(), GetProtocol()); return strDesc; } //--------------- CString strText; CString _strProcName(L""); ... strText.Format(_T("%s"), _strProcName); //--------------- CString m_strDriverDosName; CString m_strDriverName; ... m_strDriverDosName.Format( _T(".%s"), m_strDriverName); //--------------- CString __stdcall GetResString(UINT dwStringID); ...
  • 5. _stprintf(acBuf, _T("%s"), GetResString(IDS_SV_SERVERINFO)); //--------------- // Думаю понятно, // что примеры можно приводить и приводить. А некоторые и задумываются, но забываются. И поэтому так трогательно смотрится код следующего вида: CString sAddr; CString m_sName; CString sTo = GetNick( hContact ); sAddr.Format(_T("%smailslot%s"), sTo, (LPCTSTR)m_sName); И таких примеров в проектах, на которых мы тестируем PVS-Studio, оказалась столько, что стало не понятно, как это вообще может быть. А, тем не менее, это все замечательно работает, в чем я смог убедиться, написав тестовую программу и попробовав различные варианты использования CString. В чем же дело? Видимо разработчики компиляторов не выдержали бесконечных вопросов почему программы индусов, использующие CString не работают и обвинений в "глючности компилятора, который неверно работает со строками". И они тихо совершили священный ритуал экзорцизма, изгнав зло из CString. Они сделали невозможное возможным. А именно класс CString реализован специальным хитрым образом, так, чтобы его можно было передавать в функции вида printf, Format. Сделано это достаточно хитро и кто интересуется, то может почитать исходный код класса CStringT, а также познакомиться с вот эти развернутым обсуждением "Pass CString to printf?" [5]. Я вдаваться в подробности не буду. Отмечу только важный момент. Специальная реализация CString не достаточна, теоретически передача не POD-типа приводит к непредсказуемому поведению. Так вот разработчики Visual C++, а вместе с ними и Intel C++ сделали так, что непредсказуемое поведение представляет из себя всегда корректный результат. :) Ведь правильная работа программы вполне себе подмножество непредсказуемого поведения. :) А еще я теперь начинаю задумываться над некоторыми странными особенностями поведения компилятора при построении 64-битных программ. Есть подозрение, что разработчики компилятора сознательно делают поведение программы не теоретическим, а практическим
  • 6. (работоспособным), в тех простых случаях, когда они распознают некоторый паттерн. Наиболее понятным примером может быть паттерн цикла. Пример некорректного кода: size_t n = BigValue; for (unsigned i = 0; i < n; i++) { ... } Теоретически, если значение n > UINT_MAX больше, то должен возникнуть бесконечный цикл. Однако в Release версии он не возникает, так как для переменной "i" используется 64-битный регистр. Конечно, если код будет посложнее, то бесконечный цикл возникнет, но хотя бы в ряде случаев программе повезет. Подробнее я писал про это в статье "64-битный конь, который умеет считать" [6]. Раньше я думал, что такое неожиданно удачное поведение программы связано исключительно с особенностями оптимизации Release версий. Однако теперь я в этом не уверен. Возможно, это сознательная попытка хотя бы иногда сделать неработоспособную программу работоспособной. Конечно, я не знаю, причина в оптимизации или в заботе большого брата, но это волне повод пофилософствовать. :) Ну а кто знает, тот вряд ли скажет. :) Уверен, что есть и другие моменты, когда компилятор подставляет руку программам калекам. Если попадется что-то еще интересно, обязательно расскажу. Желаю вам безглючного кода! Библиографический список 1. Блог Алексея Пахунова. Обратная совместимость это серьезно. http://www.viva64.com/go.php?url=390 2. Блог Алексея Пахунова. AppCompat. http://www.viva64.com/go.php?url=391 3. Блог Алексея Пахунова. Windows 3.x жив? http://www.viva64.com/go.php?url=392 4. MSDN. CString Operations Relating to C-Style Strings. Topic: Using CString Objects with Variable Argument Functions . http://www.viva64.com/go.php?url=393 5. Обсуждение на сайте eggheadcafe.com. Pass CString to printf? http://www.viva64.com/go.php?url=394 6. Андрей Карпов. 64-битный конь, который умеет считать. http://www.viva64.com/art-1-1- 1064884779.html