Sprintf в char* var[1] завершается с ошибкой сегментации

рассмотрим код:

using std::cout; using std::cerr;
using std::endl; using std::string;
using std::vector;

// . . .

    char* envp[10];

    vector<string> lines;
    char* c_line = nullptr;
    size_t len = 0;

    while ((getline(&c_line, &len, input_file)) != -1) {
        string line;
        lines.push_back(line.assign(c_line));
    }

    fclose(input_file);
    free(c_line);

    sprintf(envp[0], "SERVER=%s", lines[0].data());

    // printing envp[0] here OK

    sprintf(envp[1], "DOMAIN=%s", lines[1].data());

    // printing envp[1] never happened - Segmentation fault prints in output instead

Я разработчик C#, я не использовал C пару десятков лет. Чего-то очевидного не хватает. Распределение памяти?

P.S. Я смешиваю «старый» char * для строк со стандартными строками, поскольку приложение использует стороннюю dll с символами*

Объявление РЕДАКТИРОВАТЬchar envp[10][512]; завершается ошибкой, когда я пытаюсь передать стороннее свойство somedllObj.envps = envp; с помощью cannot convert char[10][512] to char**

вы не инициализировали элементы envp

Alan Birtles 21.03.2022 13:55

Вам нужно выделить память для ваших указателей с помощью new. Пример: char* c_line = nullptr; -> char* c_line = new char[100]; Или просто char c_line[100];

Johnny Mopp 21.03.2022 13:55

Я не вижу, где вы выделяете память для envp строк, прежде чем printf перейти к...

Aconcagua 21.03.2022 13:56

@JohnnyMopp - я скопировал это из Google. этот бит работает. возможно, вы правы, и это небрежно, но сейчас это не проблема. спасибо за ваше время в любом случае

Boppity Bop 21.03.2022 13:57

@JohnnyMopp Нет, если нулевой указатель, это сделает getline.

Aconcagua 21.03.2022 13:57

@Aconcagua - как правильно выделить envp?

Boppity Bop 21.03.2022 13:59

В порядке. Неправильно getline. Но мой комментарий верен для указателей в envp.

Johnny Mopp 21.03.2022 14:00

Какое тебе дело до envp? Можете ли вы изменить тип на std::vector<std::string>>?

mch 21.03.2022 14:00

Я не могу, потому что я должен передать его как char* [] сторонней dll

Boppity Bop 21.03.2022 14:00

Я нет рекомендую эту дикую смесь std::string и до-струн. Считывайте все напрямую в std::string — все, что у вас есть, — это std::vector<std::string>, содержащий все ваши данные. Затем вызовите стороннюю библиотеку, создайте char* array[], но назначьте ей содержимое строк в векторе — std::string::c_str! Затем передайте этот массив в библиотеку — я предполагаю, что библиотека не будет хранить эти указатели. Если это так, то сохраните вектор в живых, пока библиотека все еще нуждается в этих строках.

Aconcagua 21.03.2022 14:01

Вы можете объявить char envp[10][256] ;, например: 256 символов должно быть достаточно для переменных среды... Но использование настоящего C++ вместо старого кода C должно быть лучше в любом случае.

Wisblade 21.03.2022 14:01

@Wisblade: 260 - это MAX_PATH, а не 256. Диск, двоеточие и обратная косая черта, а также trainling \0 должны соответствовать

Thomas Weller 21.03.2022 14:02

Вам не обязательно хранить данные в массивах символов, чтобы передавать их (только для чтения) функциям в стиле C. std::string имеет член c_str(), который возвращает const char*, если это работает.

BoP 21.03.2022 14:02

Не связанный с рассматриваемым вопросом - вы можете сделать эту задачу намного сложнее, чем нужно, смешивая код C и C++. Этот код почти полностью написан на языке C с использованием концепций, которые не рекомендуются для любого кода на C++, за исключением символа vector, который, как я полагаю, на самом деле является std::vector. Вы также описываете его как код C, помечая его как C++. Когда этот код не работает, вам нужно будет поговорить с людьми, имеющими опыт работы как с C, так и с C++ (см. предыдущий комментарий, в котором предполагалось, что C++ std::getline, а вы использовали C getline)

Drew Dormann 21.03.2022 14:04

Что делает dll со строками, на которые указывают указатели в envp? Он просто читает их или модифицирует?

Jabberwocky 21.03.2022 14:07

@ThomasWeller в некоторых версиях Windows, но вопрос помечен как linux, даже если это был новый код Windows, следует предполагать, что путь может вести к совсем немного дальше, а linux имеет максимальную длину дорожка 4096 и длину имени файла 255

Mgetz 21.03.2022 14:09

@ThomasWeller Это в Linux, так что это 16384, если я хорошо помню - так что это большой буфер - и два примера, которые дал OP, - это просто некоторые базовые назначения переменных (его первый sprintf даже работал ...). Правильным решением должно быть использование std::string и правильное форматирование через std::stringstream, ИЛИ, по крайней мере, использование snprintf и malloc, но для такой простой задачи это может быть излишним.

Wisblade 21.03.2022 14:09

хорошо, я думал, что для этого есть однострочное решение. Я не ожидал такой путаницы. Вместо этого я попытаюсь использовать совет @Wisblade и отформатировать std::string.

Boppity Bop 21.03.2022 14:12

@Mgetz: да, NTFS также поддерживает длинные имена файлов, начиная с Windows 10 1607 (по крайней мере, 5 лет назад) и настраивается через реестр MSDN

Thomas Weller 21.03.2022 15:09
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
19
91
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Не рекомендую так дико смешивать std::string и старые до-струны. Вместо этого я бы как можно дольше полагался на классы С++:

std::ifstream inputFile("path to file");
if (!inputFile)
{
    // error handling
}

std::vector<std::string> lines;
std::string tmp;
while(std::getline(inputFile, tmp))
{
    lines.emplace_back(std::move(tmp)); // since C++11, not copying the strings...
}

if (!inputFile.eof())
{
    // not the entire file read
    // -> error handling!
}

// TODO: size check; what, if not sufficient lines in file?

lines[0] = std::string("SERVER = ") + lines[0];
lines[1] = std::string("DOMAIN = ") + lines[1];

std::vector<char*> envp;
envp.reserve(lines.size());
for(auto& l : lines) // C++11: range based for loop...
{
    // note that this ABSOLUTELY requires the library not
    // modifying the strings, otherwise undefined behaviour!
    envp.emplace_back(const_cast<char*>(l.c_str()));

    // UPDATE: this works as well:
    envp.emplace_back(l.data());
    // the string is allowed to be modified, too – apart from
    // the terminating null character (undefined behaviour!)
}

// so far, pure C++...
// now we just get the pointers out of the vector:

libraryFunction(envp.size(), envp.data()); 

Бонус: вообще никакого ручного управления памятью...

(Примечание: предполагается, что строки нет изменены в библиотеке!)

Мне нужно передать переменную в библиотеку следующим образом: somedllObj.envps = envp; где envps есть char **

Boppity Bop 21.03.2022 14:29

это работает envp.emplace_back(l.data()); предложенный l.c_str не работает - ошибка времени компиляции

Boppity Bop 21.03.2022 14:46

@BoppityBop Моя вина, const_cast пропала...

Aconcagua 21.03.2022 14:47

что это за чай! :)

Boppity Bop 21.03.2022 14:51

@Aconcagua lines[0] = std::string("SERVER=%s") + lines[0]; должно быть lines[0] = std::string("SERVER = ") + lines[0]; или lines[0].insert(0, "SERVER = ");, и то же самое для lines[1].

Remy Lebeau 21.03.2022 19:15

@BoppityBop "Мне нужно передать переменную в библиотеку следующим образом: somedllObj.envps = envp;" - для этого вы можете использовать somedllObj.envps = envp.data();. Указатель data() в std::vector<char*> является указателем char**.

Remy Lebeau 21.03.2022 19:16

@RemyLebeau Ошибка копирования-вставки ... Спасибо за подсказку.

Aconcagua 21.03.2022 20:25

Предполагая, что серверы и домены находятся во входном файле, в них нет пробелов, поэтому getline не нужен.

vector<string> lines;
std::ifstream input_file("path to file");
std::copy(std::istream_iterator<std::string>(input_file),
          std::istream_iterator<std::string>(),
          std::back_inserter(lines));
lines[0] = "SERVER = "s + lines[0];
lines[1] = "DOMAIN = "s + lines[1];

std::vector<char*> envp;
for(auto& line : lines)
    envp.emplace_back(line.data());
envp.emplace_back(nullptr);  // Usually env arrays end with nullptr, thus +1 to array size
somedllObj.envps = envp.data();

Другие вопросы по теме