почему передача строки в функцию, которая принимает LPSTR, не работает? - PullRequest
0 голосов
/ 06 августа 2020

Следующий код дает пустую строку и длину = 0, но во время отладки я вижу, что childDisplayName имеет правильное имя.

    CHAR fileSystemName[MAX_PATH + 1] = { 0 }; 
    DWORD serialNumber = 0; DWORD maxComponentLen = 0; 
    string childDisplayName = "";
    DWORD fileSystemFlags = 0; 
    if (GetVolumeInformationA("C:\\", // L"\\MyServer\MyShare\"
        (LPSTR)&childDisplayName, MAX_PATH+1,
        &serialNumber, &maxComponentLen,
        &fileSystemFlags, fileSystemName, sizeof(fileSystemName)) == true)
    {
        
        cout << childDisplayName << "length: "<<childDisplayName.length()<<endl;
    }


следующий код работает нормально. Я не понимаю, почему LPSTR работает, когда я передаю массив символов, и не работает, когда я передаю строку.

    CHAR fileSystemName[MAX_PATH + 1] = { 0 }; 
    DWORD serialNumber = 0; DWORD maxComponentLen = 0; 
    CHAR childDisplayName[MAX_PATH + 1] = { 0 };
    DWORD fileSystemFlags = 0; 
    if (GetVolumeInformationA("C:\\", // L"\\MyServer\MyShare\"
        childDisplayName, MAX_PATH+1,
        &serialNumber, &maxComponentLen,
        &fileSystemFlags, fileSystemName, sizeof(fileSystemName)) == true)
    {
        
        cout << childDisplayName << "length: "<<strlen(childDisplayName)<<endl;
    }

Ответы [ 2 ]

1 голос
/ 06 августа 2020

string childDisplayName = ""; создает пустую пустую строку (нулевой размер и неопределенная емкость). Использование этого в качестве буфера данных для записи вряд ли будет go хорошо.

Вы можете сделать это: string childDisplayName(MAX_PATH + 1, ' ');, чтобы создать строку с правильным выделенным пространством.

Во-вторых , как писал @churill, адрес строки не является адресом символов в ней. Вместо этого используйте childDisplayName.data(), чтобы получить char* во внутреннем хранилище строки, которую вы можете записать, но убедитесь, что не записываете за пределы диапазона [data(); data() + size()).

EDIT: немного о том, как std::string и .data() работает.

Я сделал небольшой пример программы:

#include<iostream>
#include<string>

void print(const std::string& s)
{
    std::cout << "String size: " << s.size() << '\n';
    std::cout << "String contents: >>" << s << "<<\n";
    std::cout << "String as c-string: >>" << s.c_str() << "<<\n";
    std::cout << '\n';
}

int main() {
    std::string bla = "";
    auto bladata = bla.data();
    for (int i = 0;i < 5;++i) {
        bladata[i] = '!';
    }
    print(bla);

    std::string bla2(10, '\0');
    auto bla2data = bla2.data();
    for (int i = 0;i < 5;++i) {
        bla2data[i] = '!';
    }
    print(bla2);
}

При запуске этого вывода:

String size: 0
String contents: >><<
String as c-string: >>!!!!!╠╠╠╠╠╠╠╠╠╠╠<<

String size: 10
String contents: >>!!!!!     <<
String as c-string: >>!!!!!<<

Что здесь происходит? Первое, на что следует обратить внимание, это то, что создается пустой std::string с нулевым размером и неопределенной емкостью - глядя в свой отладчик, я знаю, что в моей системе эта неопределенная емкость равна 15, так что пока я не t go кроме этого ничего не должно cra sh. Но это, очевидно, не то, что вам следует делать в реальном коде (запись здесь - это строго неопределенное поведение).

Это означает, что строка bla имеет размер 0 и содержит 15-символьный буфер символов, где я установил первые 5 символов до '!'. Поэтому, когда я пытаюсь напечатать его size() или напечатать его как std::string, он идентичен любой обычной пустой строке. Однако, если я использую .c_str() для непосредственной печати внутреннего буфера, он печатает как любой старый char* и просто печатает все, что находится в памяти, пока не встретит нулевой символ.

С другой стороны, bla2 инициализируется и содержит 10 нулевых символов. Это означает, что его размер равен 10, а его вместимость составляет минимум 10 (в моем случае это тоже 15). Это означает, что после l oop он по-прежнему отображается как размер 10, независимо от того, сколько '!' Я помещаю в буфер, и когда я печатаю его как std::string, он печатает все 10 символов, которые он содержит; и 5 '! и 5' \ 0. Однако, когда я печатаю его как char*, он печатает 5 '! И останавливается, как только встречает нулевой символ.

0 голосов
/ 07 августа 2020

В первом фрагменте кода я не могу получить правильное имя childDisplayName.

enter image description here

Although it seems to have several characters, but an exception is triggered when I want to output it.

enter image description here

So you passed a wrong address to the GetVolumeInformationA.Because the address of a string is not the address of the characters

You can test like the following code:

string s("test");
CHAR cs[] = "test";
cout 

Output: enter image description here

As @churill says, std::string only manages a dynamic array of characters.So you can not pass the address of string to the function.

And according to MSDN :

Если вы изменяете содержимое строки, возвращаемой перегрузкой данных const, поведение не определено. Вы также получаете неопределенное поведение, если нулевой символ терминала изменяется на любое другое значение. Возвращенный указатель может стать недействительным, если неконстантная ссылка на строку передана стандартной библиотечной функции. Его также можно сделать недействительным, вызвав неконстантную функцию-член.

Так что даже если вам действительно удалось передать childDisplayName.data() в функцию. Это также неопределенное поведение. Я рекомендую вам правильно передавать типы параметров, необходимые для функции, вместо того, чтобы пытаться передать другое неопределенное или нетипизированное поведение, которое вызовет у вас много путаницы.

...