Значение объекта C ++ внезапно становится NULL без связанной предыдущей инструкции - PullRequest
0 голосов
/ 04 августа 2020

ПРИМЕЧАНИЕ. Вопрос открыт повторно с более подробной информацией.

Итак, здесь у нас есть код

someCode. cpp

(...)
MyStruct sct = performRequest(.... .... ....); //Line 1
std::cout << "Line 2";
std::cout << "Line 3";
(...)

someCode .hpp ( для struct MyStruct)

(...)
struct MyStruct {
    char** memory;
    int* response_code;
};
(...)

функция performRequest ()

static size_t
WriteMemoryCallback(void* contents, size_t size, size_t nmemb, void* userp)
{
    size_t realsize = size * nmemb;
    struct MemoryStruct* mem = (struct MemoryStruct*)userp;

    char* ptr = (char*) realloc(mem->memory, mem->size + realsize + 1);
    if (ptr == NULL) {
        /* out of memory! */
        printf("not enough memory (realloc returned NULL)\n");
        return 0;
    }

    mem->memory = ptr;
    memcpy(&(mem->memory[mem->size]), contents, realsize);
    mem->size += realsize;
    mem->memory[mem->size] = 0;

    return realsize;
}



MyStruct performRequest(std::string requestType, std::string URL, std::string requestParameters, struct curl_slist* headersList, std::string requestFields) {
    CURL* curl_handle = curl_easy_init();
    CURLcode curl_code;
    

    struct MemoryStruct chunk;

    chunk.memory = (char*) malloc(1);  /* will be grown as needed by the realloc above */
    chunk.size = 0;    /* no data at this point */

    curl_easy_setopt(curl_handle, CURLOPT_BUFFERSIZE, 102400L);
    curl_easy_setopt(curl_handle, CURLOPT_URL, (URL + requestParameters).c_str());
    curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, true);
    curl_easy_setopt(curl_handle, CURLOPT_HEADER, false); 
    if (!requestFields.empty()) {
        curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, requestFields.c_str());
    }

    if (headersList != NULL) {
        curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headersList);
    }
    curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, 50L);
    curl_easy_setopt(curl_handle, CURLOPT_CUSTOMREQUEST, requestType.c_str());
    curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, false);
    curl_easy_setopt(curl_handle, CURLOPT_TCP_KEEPALIVE, true);

    /* send all data to this function, we pass our 'chunk' struct to the callback function   */
    curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
    curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void*)& chunk);

    curl_code = curl_easy_perform(curl_handle);

    /* check for errors */
    if (curl_code != CURLE_OK) {
        fprintf(stderr, "curl_easy_perform() failed: %s\n",
            curl_easy_strerror(curl_code));
    }

    int response_code;
    curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &response_code);

    /* cleanup curl stuff */
    curl_easy_cleanup(curl_handle);

    MyStruct res;
    res.memory = &chunk.memory;
    res.response_code = &response_code;

    return res;
}

Если мы отлаживаем someCode.cpp и добавляем две точки останова в строках 2 и 3 ...

    (...)
    MyStruct sct = performRequest(···,···,···); //Line 1
    [BREAKPOINT (1) HERE] std::cout << "Line 2";
    [BREAKPOINT (2) HERE] std::cout << "Line 3";
    (...)

... см. Следующие изображения о результирующих значениях отладчиком для struct sct. Breakpoint 1 (which corresponds to Breakpoint 1) :)

Точка останова 2 (что соответствует точке останова 2) : (

Как видите, значения в sct меняются на NULL без предварительной инструкции. Я не знаю, почему это происходит, хотя подозреваю, что это может быть связано с указателем ...

Итак, чтобы иметь дело с временем жизни объекта (sct), я изменил performRequest() на это:

RequestResponse* performRequest(···) {
    (···)
    return new RequestResponse(&chunk.memory,&response_code);
} 

, но у меня все еще та же проблема. Надеюсь, у вас есть идеи и спасибо!

1 Ответ

3 голосов
/ 04 августа 2020

Ваша проблема здесь:

MyStruct res;
res.memory = &chunk.memory;
res.response_code = &response_code;

return res;

Вы указываете res с адресом двух локальных переменных. Итак, как только эта функция завершится и вы вернете структуру с этими указателями, они оба недействительны. Это приводит к неопределенному поведению.

Вы можете выделить новые указатели в куче и заполнить их значениями chunk.memory и response_code. Поскольку они находятся в куче, они будут сохраняться до тех пор, пока вы не освободите память, так что это сработает.

Однако я настоятельно рекомендую перейти на std::vector<std::string> вместо char** и по возможности перейдите к необработанному int вместо int*, чтобы вам даже не нужно было беспокоиться о UB, как это.

...