Проблема с fputs и printf

Я получил следующий код:

char* writeSpace(int i)
{
 fputs("                              " + (30-i), stdout);
}

printf("#%i key: %s%svalue: %s%s value2: %s", id, key, writeSpace(10), value, writeSpace(8), value2);

мой вывод должен выглядеть примерно так:

#1 key: foo          value: bar        value2: foobar

но это не так. это выглядит как:

                      #1 key: foo(null)value: bar(null)value2: foobar(null)

Что не так с моим кодом?

5 ответов

Решение

Ну ты fputпривязать все эти пробелы к консоли, чтобы вы сначала получили их.

Затем вы выводите все остальное, так что вы получите это дальше.

Возможно, вы имели в виду writeSpace вернуть строку в стиле C, а не выводить ее на консоль.

Но будьте уверены, что вы выделите для этого место! Поскольку владение буферами памяти становится немного странным, лучше всего выделять пространство вне функции.

void writeSpace(char* buf, int i) {
   fputs("                              " + (30-i), buf);
}

char spaceBuf1[30];
writeSpace(spaceBuf1, 10);

char spaceBuf2[30];
writeSpace(spaceBuf2, 8);

printf("#%i key: %s%svalue: %s%s value2: %s",
   id, key, spaceBuf1, value, spaceBuf2, value2);

И подумайте об использовании реальных функций C++, таких как iostreams и std::string, Это будет намного проще:

std::cout << "#" << id << " "
          << " key: " << std::setw(30) << key
          << " value: " << std::setw(30) << value
          << " value2: " << value2;

Я рекомендую эти ресурсы для изучения идиоматического C++.

Вы объявили writeSpace() вернуть char*, но вы ничего не вернули.

Ваш writeSpace Функция не возвращает новую строку (даже если вы объявили ее таким образом), но пишет напрямую в терминал. Так как вы называете это в качестве аргумента в вашем printf вызов, writeSpace сначала вызывается, печатает свои вещи, а затем printf печатает свои вещи. Вы должны сделать это так:

char* writeSpace(int i)
{
    char *ret = NULL;
    asprintf(ret, "                              " + (30-i));
    return ret;
}

Конечно, это требует от вас free память (в противном случае у вас будет утечка памяти). Вы можете сделать это так:

char *spaces = writeSpace(10);
printf("%s%i", spaces, 42);
free(spaces);

Альтернативой является использование статической переменной, которую сама функция очищает при следующем вызове:

char* writeSpace(int i)
{
    static char *ret = NULL;
    if (ret != NULL) free(ret);
    asprintf(ret, "                              " + (30-i));
    return ret;
}

Но это имеет тот недостаток, что вы можете использовать только один вызов writeSpace в вашем printf аргументы, так как второй очистит память предыдущего вызова. Все еще может быть то, что вы хотите.

Кстати, The + (30-i) часть зла То, что вы, вероятно, хотите, это вместо этого:

asprintf(ret, "%*s", i, ""); // Prints i spaces

Если вы хотите отформатировать переменную ws'и вам абсолютно необходимо использовать простой C, вы можете использовать строки такого формата (%*c, где * - это число ws, а c - символ, который нужно установить (' 'в этом случае, конечно)):

sprintf(buffer, "%*cafterspace", 30, ' '); // Will print 30 ws and then "afterspace".
sprintf(buffer, "%*cafterspace", 15, ' '); // Will print 15 ws and then "afterspace".

Но вы все равно должны подготовить буфер, как объяснил Томалак!

Другие предоставленные ответы имеют правильный ответ, который вы ищете. Тем не менее, я хотел бы попытаться добавить небольшое объяснение симптомов, которые вы видели, чтобы помочь вам избежать их в будущем.

когда ваш оператор printf выполняется:

 printf("#%i key: %s%svalue: %s%s value2: %s", id, key, writeSpace(10), value, writeSpace(8), value2);

Два вызова метода writeSpace() выполняются до того, как сам printf что-либо сделает. Причина этого заключается в том, что возвращаемое значение этих методов используется в качестве аргументов для метода printf. Итак, последовательность выполнения для приведенной выше строки кода:

  • writeSpace (10), который печатает некоторые пробелы в стандартный вывод и возвращает нулевой указатель.
  • writeSpace (8), который печатает еще несколько пробелов для стандартного вывода, а также возвращает нулевой указатель.
  • наконец, printf вызывается со следующими аргументами: printf("#%i ключ: %s%svalue: %s%s значение2: %s", id, ключ, ноль, значение, ноль, значение2);

Вот почему вы видите пробелы, напечатанные в начале, а также почему вы видите, что "(ноль)" появляется в ваших выходных данных.

Таким образом, урок здесь заключается в том, что во время выполнения программы аргументы метода разрешаются до вызова самого метода.

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