#c #winapi
#c #winapi
Вопрос:
Я пытаюсь немного изучить c , и у меня возник глупый вопрос. Рассмотрим этот код:
TCHAR tempPath[255];
GetTempPath(255, tempPath);
Зачем Windows нужен размер переменной tempPath? Я вижу, что GetTempPath
объявлено что-то вроде:
GetTempPath(dword size, buf LPTSTR);
Как Windows может изменить значение buf без amp;
оператора? Разве функция не должна быть такой?
GetTempPath(buf amp;LPTSTR);
Может ли кто-нибудь предоставить простой GetTempPath
пример реализации, чтобы я мог увидеть, как size
используется?
Редактировать:
Спасибо за все ваши ответы, все они правильные, и я поставил вам всем 1. Но что я имел в виду под «Может ли кто-нибудь предоставить простую GetTempPath
реализацию), так это то, что я попытался закодировать функцию, аналогичную той, которую использует Windows, следующим образом:
void MyGetTempPath(int size, char* buf)
{
buf = "C:\test\";
}
int main(int argc, char *argv[])
{
char* tempPath = new TCHAR[255];
GetTempPathA(255, tempPath);
MessageBoxA(0, tempPath, "test", MB_OK);
return EXIT_SUCCESS;
}
Но это не работает. MessageBox отображает строку «##$». Как должен быть закодирован MyGetTempPath для правильной работы?
Комментарии:
1. C не имеет встроенного строкового типа. Вместо этого вы используете символьные массивы и указатели. Инструкция
buf = "C:\test\"
копирует адрес «C:\test \» для буферизации. Однако, поскольку сам указатель передается по значению, buf вызывающего не обновляется и ничего не делает. Попробуйте вместо этого использоватьstrcpy_s
.
Ответ №1:
Windows нужен размер в качестве меры предосторожности. Это может привести к сбою приложения, если оно скопирует символы после конца буфера. Когда вы указываете длину, это может предотвратить это.
Переменные массива работают как указатели. Они указывают на данные в массиве. Таким образом, нет необходимости в amp;
операторе.
Не уверен, какой пример вы ищете. Как я уже сказал, ему просто нужно убедиться, что он не записывает больше символов, чем для этого есть места.
Комментарии:
1. В этом нет необходимости,
amp;
потому что происходит неявное преобразование массива в указатель на первый элемент массива, а не потому, что это является указателем на первый элемент. Действительно, использованиеamp;
приводит к типу указателя на массив, такому какint(*)[255]
.2. @GMan: Можете ли вы пояснить разницу между «работает как указатель» и «неявное преобразование из массива в указатель на первый элемент»? Могу заверить вас, что я точно знаю, как это работает. Вы спорите о семантике?
3. @Johathan: Во-первых, поскольку массив на самом деле ни на что не указывает, он просто содержит данные. Во-вторых, существуют обстоятельства, при которых преобразование из «имени массива» в «указатель на начало массива» не выполняется, например
sizeof(array)
. Также важно, чтобы вы не путали эти два параметра вextern
объявлении:extern int a[];
иextern int *a;
не являются одним и тем же.4. @Jonathan: Хм, возможно, мы спорим о семантике, но я скажу, что моя семантика верна. 🙂 Массивы не являются указателями и не работают как указатели. Однако в некоторых случаях они могут быть преобразованы в них. (Если вы думаете, что это означает, что массивы работают как указатели, тогда потоки работают как логические значения, а целые числа — как числа с плавающей точкой.)
5. При передаче массива в качестве аргумента (путем указания имени массива) передается адрес первого элемента в массиве. Если у меня есть указатель на массив и я передаю указатель в качестве аргумента, это также передает адрес первого элемента в массиве. Итак, в обсуждении передачи аргументов я говорю, что массив может работать как указатель.
Ответ №2:
Массив не может быть передан в функции по значению. Вместо этого он преобразуется в указатель на первый элемент, который передается функции. Наличие (неконстантного) указателя на данные позволяет изменять:
void foo(int* i)
{
if (i) (don't dereference null)
*i = 5; // dereference pointer, modify int
}
Аналогично, функция теперь имеет указатель на TCHAR
, в который она может записывать. Затем он принимает размер, чтобы точно знать, сколько TCHAR
существует после этого начального. В противном случае он не знал бы, насколько велик массив.
Ответ №3:
GetTempPath() выводит данные в ваш символьный массив «tempPath». Если вы не сообщите ему, сколько места выделено в массиве (255), у него не будет способа узнать, хватит ли у него места для записи строки path в tempPath.
Массивы символов в C / C — это в значительной степени просто указатели на местоположения в памяти. Они не содержат другой информации о себе, как могли бы экземпляры классов C или Java. Я думаю, что Windows API был разработан до того, как C действительно приобрел большую инерцию, поэтому вам часто придется использовать более старые методы в стиле C и встроенные типы данных для работы с ним.
Ответ №4:
Можно попробовать следующую оболочку, если вы хотите избежать размера:
template<typename CHAR_TYPE, unsigned int SIZE>
void MyGetTempPath (CHAR_TYPE (amp;array)[SIZE]) // 'return' value can be your choice
{
GetTempPath(SIZE, array);
}
Теперь вы можете использовать, как показано ниже:
TCHAR tempPath[255];
MyGetTempPath(tempPath); // No need to pass size, it will count automatically
В вашем другом вопросе, почему мы НЕ используем следующее:
GetTempPath(buf amp;LPTSTR);
это потому, что amp;
используется, когда вы хотите передать тип данных по ссылке (не по адресу). Я не знаю, для чего buf
приводится тип, но это должен быть какой-то тип указателя.
Ответ №5:
Может ли кто-нибудь предоставить простой пример реализации GetTempPath, чтобы я мог видеть, как используется size?
Первый способ (на основе константы MAX_PATH):
TCHAR szPath[MAX_PATH];
GetTempPath(MAX_PATH, szPath);
Второй способ (основанный на описании GetTempPath):
DWORD size;
LPTSTR lpszPath;
size = GetTempPath(0, NULL);
lpszPath = new TCHAR[size];
GetTempPath(size, lpszPath);
/* some code here */
delete[] lpszPath;
Как Windows может изменить значение buf без оператора amp;?
Оператор amp; не нужен, потому что имя массива является указателем на первый элемент массива (или на весь массив). Попробуйте следующий код, чтобы продемонстрировать это:
TCHAR sz[1];
if ((void*)sz == (void*)amp;sz) _tprintf(TEXT("sz equals to amp;sz n"));
if ((void*)sz == (void*)amp;(sz[0])) _tprintf(TEXT("sz equals to amp;(sz[0]) n"));
Ответ №6:
Как и было запрошено, очень простая реализация.
bool MyGetTempPath(size_t size, char* buf)
{
const char* path = "C:\test\";
size_t len = strlen(path);
if(buf == NULL)
return false;
if(size < len 1)
return false;
strncpy(buf, path, size);
return true;
}
Пример вызова новой функции:
char buffer[256];
bool success = MyGetTempPath(256, buffer);
Ответ №7:
из http://msdn.microsoft.com/en-us/library/aa364992 (v = против 85).aspx
DWORD WINAPI GetTempPath(
__in DWORD nBufferLength,
__out LPTSTR lpBuffer
);
итак, GetTempPath определяется примерно так
GetTempPath(DWORD nBufferLength, LPTSTRamp; lpBuffer);
Что означает, что компилятор передает значение lpBuffer по ссылке.
Комментарии:
1. Windows API — это чистый C, и в C. ссылочных типов нет.
__out
Это просто подсказка для компилятора (Microsoft) и она вообще не изменяет типы аргументов.