Зачем Windows нужен размер при вызове функции?

#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) и она вообще не изменяет типы аргументов.