5

我写了一个自定义打印功能。我的问题是我需要返回 aconst char*因为它必须在另一个函数中使用。我根本不知道如何管理...

anotherFunction (const char* text /*Here*/, unsigned __int32 value, unsigned __int64 bigVal);

我知道以下 example/s 不能正常工作。这就是我到目前为止所尝试的。

const char* CatchMessage (const char *message, ...)
{
    va_list  args;
    va_start (args, message);
    /*?*/
    va_end   (args);
    return message;
}

我还只是设法在 cmd 中获得正确的输出,但我实际上需要它作为返回值。

void CatchMessage (const char *message, ...)
{
    va_list  args;
    va_start (args, message);
    vfprintf (stdout, message, args);
    va_end   (args);
}

称呼:

CatchMessage ("Some Input %s and %d equals to %d", randString, randNumber, secRandNumber);

应该返回:

"Some Input stuff and 12 equals to 6"

我一直无法找到解决方案。任何帮助,将不胜感激。

问:如何让这个CatchMessage函数返回正确的格式const char*

4

6 回答 6

6

听起来CatchMessage应该将指针指向 char 缓冲区(及其大小),然后vsnprintf()放入该缓冲区。

于 2013-11-13T09:32:43.973 回答
3

既然您使用的是 C++(至少根据问题上的标签),为什么不只返回 a 中的字符串std::string

于 2013-11-13T09:41:57.427 回答
2

返回 ( const)的问题char *是您必须在某处有一个缓冲区。

有几种方法可以做到这一点:

  1. 调用者必须提供该缓冲区
  2. 你必须这样malloc()
  3. 该函数本身作为static缓冲区,但这将使其不可重入 - 这对多线程等不利。

广告 1:

void CatchMessage(char * result, size_t maxlen, const char *message, ...)
{
    va_list ap;
    va_start(ap, message);
    vsnprintf(result, maxlen, message, ap);
    va_end(ap);
}

char buffer[500];
CatchMessage(buffer, sizeof buffer, "Some Input %s and %d equals to %d", randString, randNumber, secRandNumber);
anotherfunction(buffer, ...)

广告 2:

char * CatchMessage(const char *message, ...)
{
    size_t size = 500;
    char * result = malloc(size);
    if (!result) return NULL; // error handling!
    while (1) {
        va_list ap;
        va_start(ap, message);
        size_t used = vsnprintf(result, size, message, ap);
        va_end(ap);
        char * newptr = realloc(result, size);
        if (!newptr) { // error
            free(result);
            return NULL;
        }
        result = newptr;
        if (used <= size) break;
        size = used;
    }
    return result;
}

char * buffer = CatchMessage(buffer, sizeof buffer, "Some Input %s and %d equals to %d", randString, randNumber, secRandNumber);
if (!buffer) { /* error handling: no memory! */ }
anotherfunction(buffer, ...)
free(buffer); // important for avoiding memory leaks

广告 3:

char * CatchMessage(const char *message, ...)
{

    static char result[500]; // static is important here! Otherwise the memory will be freed immediately after returning.
    va_list ap;
    va_start(ap, message);
    vsnprintf(result, sizeof result, message, ap);
    va_end(ap);
    return result;
}

char * buffer = CatchMessage(buffer, sizeof buffer, "Some Input %s and %d equals to %d", randString, randNumber, secRandNumber);
anotherfunction(buffer, ...)

没有其他选择,尤其是没有定义

char result[500];

在函数中然后返回它:这个数组存在于堆栈中,并在返回后立即释放。调用者不能安全地访问它;它的内容只是未定义的。

于 2013-11-13T09:48:20.230 回答
1

如果你不关心重入,你可以返回指向静态缓冲区的指针:

#define MESSAGE_MAX 1024

const char *
CatchMessage (const char *message, ...)
{
    static buffer[MESSAGE_MAX];
    va_list  args;
    va_start (args, message);
    vsnprintf (buffer, MESSAGE_MAX, message, args);
    va_end   (args);
    return buffer;
}

笔记:

  1. 此实现不是线程安全的。如果您关心线程安全,请使用线程本地存储而不是静态缓冲区

  2. 此实现对消息长度具有硬编码上限。如果这是不可取的,并且您的编译器符合 C99,您可以首先调用作为第一个参数vsprintfNULL了解结果字符串长度,然后分配该侧的缓冲区。

于 2013-11-13T09:52:41.453 回答
0

在 C++ 中,使用可变参数是不好的,因为它对类型安全(和内存安全)有一定的强调。因此,您可能想要提出一个实际上是类型安全的版本(是的,这是可能的),但又提出了一个类似的接口:

template <typename H, typename... Args>
void format(std::ostream& out,
            char const* format,
            size_t len,
            H const& head,
            Args const&... args);

// Variations with 'char const (&)[N]' and 'std::string' formats
// as well as variations returning directly a 'std::string'.

现在,实施起来format并不太难;特别是在不支持位置参数的情况下。一个简单的可以在下面找到:

inline void format_string(std::ostream& out, char const* const* c) { out << *c; }
inline void format_string(std::ostream& out, std::string const* s) { out << *s; }
inline void format_string(std::ostream& out, void const*); // will throw

template <typename Integral, typename = enable_integral<Integral>::type>
inline void format_integral(std::ostream& out, Integral const* i) { out << *i; }
inline void format_integral(std::ostream& out, void const*); // will throw

inline size_t format_consume(std::ostream& out,
                             char const* const format,
                             size_t const length)
{
    char const* end = format + length;
    char const* current = format;

    do {
        // 1. Find first "format identifier", output stuff in-between
        char const* perc = std::find(current, end, '%');

        if (perc != current) { out.write(current, perc - current); }

        current = perc;

        // 2. %% is % escaped by %, so output it directly
        while (*current == '%' and *(current + 1) == '%') {
            out.put('%');
            current += 2;
        }
    } while (current != end and *current != '%');

    // 3. Return number of characters of format parameter consumed
    return current - format;
} // format_consume

inline void format(std::ostream& out, char const* format, size_t len) {
     size_t const consumed = format_consume(out, format, len);

     if (consumed != len) { throw std::runtime_exception("Missing arguments"); }
} // format

template <typename H, typename typename... Args>
void format(std::ostream& out,
            char const* format,
            size_t len,
            H const& head,
            Args const&... args)
{
     size_t const consumed = format_consume(out, format, len);

     if (consumed == len) { throw std::runtime_exception("Extraneous arguments"); }

     format += consumed;
     len -= consumed;

     assert(*format == '%');

     switch(*(format+1)) {
     case 's': format_string(out, &head); break;
     case 'd': format_integral(out, &head); break;
     default: throw std::runtime_exception("Invalid specifier");
     }

     format(out, format+2, len-2, args...);
} // format

在一般情况下,它稍微有点麻烦,因为您需要解析修饰符等......但是,对于生产就绪的实现,我建议看看 Boost.Format。

于 2013-11-13T12:40:31.793 回答
-3
char* CatchMessage (size_t size, const char *message, ...)
{
    char result[size];
    va_list  args;
    va_start (args, message);
    vsprintf (result, message, args);
    va_end   (args);
    return result;
}

请参阅此内容以供参考。

而对于glglgl:如果你想使用干净的方式,使用vsnprintf(result, size, message, args)

于 2013-11-13T09:35:16.003 回答