Как переменная аргументная функция переносит объект в структуру / класс?
Например :
CString a_csName;
CString a_csAge(_T("20"));
a_csName.Format(_T("My Age is : %s"),a_csAge);
Вот CString::Format
является функцией аргумента переменной в стиле printf, которая принимает это CString
объект. Как это возможно?
После небольшого исследования и отладки с помощью кода MFC, найденного ниже, надеюсь, он поможет любому, кто столкнется с когда-либо известной ошибкой статического анализатора кода «Передача структуры ‘CStringT’ многоточию”, которая на самом деле является источником моих сомнений.
http://www.gimpel.com/html/bugs/bug437.htm
Функция форматирования, являющаяся переменной функцией, зависит от спецификатора формата, присутствующего в первом параметре. Первый параметр — это всегда символ *.
Он анализирует спецификатор формата (% s,% d,% i…) и считывает массив var_arg на основе индекса найденного спецификатора формата и выполняет прямое приведение к char *, если% s или int, если указан% d.
Таким образом, если указана CString, а соответствующий спецификатор формата равен% s, то для объекта CString делается прямая попытка приведения к char *.
CString a_csName;
CString a_csAge(_T("20"));
a_csName.Format(_T("My Age is : %s"),a_csAge);
wcout<<a_csName;
Напечатал бы мой возраст 20
CString a_csName;
CString a_csAge(_T("20"));
a_csName.Format(_T("My Age is : %d"),a_csAge);
wcout<<a_csName;
Напечатал бы мой возраст 052134
Так что за этим нет разума. Просто прямой актерский состав. Следовательно, мы передаем POD или пользовательскую структуру данных, это не имеет значения.
От: http://msdn.microsoft.com/en-us/library/aa300688%28v=vs.60%29.aspx
- Вы можете свободно заменять объекты CString аргументами функций const char * и LPCTSTR.
Наверное CString
класс без vtable и только с атрибутом типа char*
, Это означает, что sizeof(CString) == sizeof(const char*)
и что если вы reinterpret_cast CString
к const char*
ты получишь работу const char*
,
Компилятор не должен принимать передачу структуры переменной части функции. Но если я не ошибаюсь, в GCC, когда вы передаете структуру как переменный аргумент, она копируется в память (т.е. не используется конструктор копирования) и выдается предупреждение. Я думаю, что MSVC делает то же самое там, а затем, Format
Метод просто предполагает, что данные являются const char*
,
Позвольте мне привести альтернативный пример. Предположим, у вас есть класс без vtable, в котором только члены являются int. Если вы передадите его переменному аргументу, компилятор скопирует содержимое этого объекта, которое является только int. Что касается реализации функции, вы (каким-то образом) знаете, что вы получили int
, Затем вы запрашиваете параметр, запрашивая int
, Так как на уровне памяти ваш класс ничем не отличается от int
все будет работать «хорошо». Функция получит доступ к int
атрибут класса.
Мне недавно пришлось очистить эту ошибку Lint и наткнулся на эту тему.
Хотя это интересное исследование происходящего приведения, самый простой способ избежать предупреждения Lint — передать указатель CString, а не структуру CString с помощью метода CString. Get.String()
как в следующем
a_csName.Format(_T("My Age is : %d"), a_csAge.GetString());
Позвольте мне начать отвечать на этот вопрос по-другому.
struct Value
{
int nValue;
float fOtherValue;
};
int main()
{
Value theValue;
theValue.nValue = 220;
theValue.fOtherValue = 3.14f;
printf("Value: %d", theValue);
}
Этот код напечатает 220
без проблем. Но если вы передадите второй аргумент после theValue
не будет:
printf("Values: %d, %f", theValue, theValue.fOtherValue);
Так как первый переменный аргумент theValue
не соответствует размеру, указанному %d
аргумент. Следовательно, 3.14
не будет (не может) отображаться. Давай не будем говорить как printf
аргумент, выдвигаемый на стеке, va_start
и подобные вещи работают.
Точно так же, когда мы разрабатываем String
Класс таким образом:
struct String
{
char* pData;
public:
String()
{
pData = new char[40];
strcpy_s(pData, 40, "Sample");
}
};
И используйте это так:
String str;
printf("%s", str);
Это определенно будет работать.
Но как насчет аргумента и повреждения стека вызовов? Что делать, если размер класса (например, Value
) больше размера данных (4 для Value
) как указано форматом (%d
). Ну, в этом случае, разработанный класс должен гарантировать, что размер данного класса совпадает с размером аргумента, используемого printf
функция.
Я не буду упоминать детали об этом, но CString
использует внутренний класс CStringData
, Последний класс содержит строку, длину, количество ссылок и т. Д. CString
(на самом деле CSimpleStringT
) использует этот класс, но содержит только указатель char
/wchar_t
, управляемый CStringData
,
Во многом, CString
реализуется как String
упомянутый класс (насколько данные и sizeof
идет).