【问题标题】:Error of indefinite parameter function in C++ 17C++ 17 中不定参数函数的错误
【发布时间】:2020-01-03 10:38:13
【问题描述】:

我写了一个字符串类,我想在MFC的CString类中写一个类似Format()函数的函数。但是当我使用它时,它总是会导致一些“随机”错误,根本不起作用。

我使用sprintf() 来制作这个函数,但是当我输入从va_arg() 得到的参数时,它告诉我这个参数是无效的。通过使用std::string().c_str(),这个错误已经消失了,但是我得到了这个错误:

“在 0x00B326A0(UString.exe 内部)Csusing 错误:0xC0000005:访问 冲突发生在读取位置 0x77000073" 否则它将转向我的 throw() 行。

这里是所有“格式化”函数及其支持函数代码:

    std::string UString::SUBSTR(std::string src, int start, int stop)
{
    if (start > stop)
    {
        return "null";
    }
    if ((stop - start) > src.length())
    {
        return "overload";
    }
    char* tmp = new char[src.length()];
    for (int i = start; i <= stop; i++)
    {
        tmp[i - start] = src[i];
    }
    std::string ret = std::string(tmp);
    delete[] tmp;
    return ret;
}
std::wstring UString::STW(const std::string & str)
{
    std::wstring ret;
    try {
        std::wstring_convert< std::codecvt_utf8<wchar_t> > wcv;
        ret = wcv.from_bytes(str);
    }
    catch (const std::exception & e) {
        std::cerr << e.what() << std::endl;
    }
    return ret;
}
bool UString::Matchopt(std::string s)
{
    const std::string opt[10] = { "d","o","x","X","c","s","f","ld","lld","lf" };
    for (int i = 0; i < 10; i++)
    {
        if (s == opt[i])
            return true;
    }
    return false;
}
bool UString::IsLegalFormatStringEnd(char c)
{
    std::string sg = "doxXsfc";
    for (int i = 0; i < sg.length(); i++)
    {
        if (c == sg[i])
            return true;
    }
    return false;
}
bool UString::IsLegalFormatString(std::string fms)
{
    UString ustr = fms;
    return ustr.legalstring(std::string("%-0123456789.cdoflsxX"));
}
std::string UString::GetSubFormatString(std::string full,int pos)
{
    for (int i = pos; i < full.length(); i++)
    {
        if (IsLegalFormatStringEnd(full[i]))
        {
            printf("format string:%s,result:%d\n", SUBSTR(full, pos, i).c_str(), IsLegalFormatString(SUBSTR(full, pos, i)));
            if (IsLegalFormatString(SUBSTR(full, pos, i)))
            {
                return SUBSTR(full, pos, i);
            }
            else
            {
                throw("UString: Unlegal Format String");
                return "";
            }
        }
    }
    throw("UString: Unlegal Format String");
    return "";
}
std::string UString::GetCoreString(std::string sfs)
{
    UString utmp = sfs;
    if (utmp.length() == 0)
    {
        throw("UString: Cannot get sub string from a empty source string");
    }
    else if (utmp.length() == 1)
    {
        if (Matchopt(utmp.std_str()))
        {
            return utmp.std_str();
        }
        else
        {
            throw("UString: Unlegal Format String");
        }
    }
    else if (utmp.length() == 2)
    {
        if (Matchopt(utmp.substr(1, 1)))
        {
            return utmp.substr(1, 1);
        }
        else if (Matchopt(utmp.substr(0, 1)))
        {
            return utmp.substr(0, 1);
        }
        else
        {
            throw("UString: Unlegal Format String");
        }
    }
    else if (Matchopt(utmp.substr(utmp.length() - 1, utmp.length() - 1)))
    {
        return utmp.substr(utmp.length() - 1, utmp.length() - 1);
    }
    else if (Matchopt(utmp.substr(utmp.length() - 2, utmp.length() - 1)))
    {
        return utmp.substr(utmp.length() - 2, utmp.length() - 1);
    }
    else if (Matchopt(utmp.substr(utmp.length() - 3, utmp.length() - 1)))
    {
        return utmp.substr(utmp.length() - 3, utmp.length() - 1);
    }
    else
    {
        throw("UString: Unlegal Format String");
        return "";
    }
}
void UString::format(std::string cmd, ...)
{
    va_list vl;
    int cnt = 0;
    for (int i = 0; i < cmd.length(); i++)
    {
        if (i != (cmd.length() - 1))
        {
            if ((cmd[i] == '%') && (cmd[i + 1] != '%'))
            {
                cnt++;
            }
        }
    }
    if (cnt == 0)
    {
        str = std::string(cmd);
        return;
    }
    else
    {
        va_start(vl, cnt);
        std::string* cmdgroup = new std::string[cnt + 1];
        int* pos = new int[cnt];
        char* tmp = nullptr;
        int posi = 0, posold = 0;
        for (int i = 0; i < cmd.length(); i++)
        {
            if (i != (cmd.length() - 1))
            {
                if ((cmd[i] == '%') && (cmd[i + 1] != '%'))
                {
                    pos[posi] = i;
                    posi++;
                }
            }
        }
        for (int i = 0; i < cnt; i++)
        {
            std::string full = GetSubFormatString(cmd, pos[i]);
            std::string opt = GetCoreString(full);
            char* buffer = NULL;
            if (opt == "d" || opt == "o" || opt == "x" || opt == "X")
            {
                int tmpi = va_arg(vl, int);
                sprintf(buffer, full.c_str(), tmpi);
            }
            else if (opt == "c")
            {
                char tmpc = va_arg(vl, char);
                sprintf(buffer, full.c_str(), tmpc);
            }
            else if (opt == "s")
            {
                const char* tmpcp = va_arg(vl, char*);
                std::string tmps = std::string(tmpcp);
                sprintf(buffer, full.c_str(), tmps.c_str());
            }
            else if (opt == "f")
            {
                float tmpf = va_arg(vl, float);
                sprintf(buffer, full.c_str(), tmpf);
            }
            else if (opt == "ld")
            {
                long tmpl = va_arg(vl, long);
                sprintf(buffer, full.c_str(), tmpl);
            }
            else if (opt == "lld")
            {
                long long tmpll = va_arg(vl, long long);
                sprintf(buffer, full.c_str(), tmpll);
            }
            else if (opt == "lf")
            {
                double tmpd = va_arg(vl, double);
                sprintf(buffer, full.c_str(), tmpd);
            }
            delete[] buffer;
            cmdgroup[i + 1] = std::string(buffer);
        }
        if (cmd[0] != '%')
        {
            cmdgroup[0] = SUBSTR(cmd, 0, pos[0] - 1);
        }
        else 
            cmdgroup[0] = "";
        va_end(vl);
        str = "";
        for (int i = 0; i < cnt + 1; i++)
        {
            str = str + cmdgroup[i];
        }
        delete[] pos;
        delete[] tmp;
        delete[] cmdgroup;
    }
    return;
}
bool UString::legalstring(std::string sstr)
{
    return legalstring(STW(sstr));
}
bool UString::legalstring(std::wstring wstr)
{
    std::wstring ws = STW(str);
    for (int i = 0; i < length(); i++)
    {
        for (int j = 0; j < wstr.length(); j++)
        {
            if (ws[i] == wstr[j])
                break;
            else if (j == (wstr.length() - 1))
                return false;
        }
    }
    return true;
}

它应该像这样工作:

UString ustr; ustr.format("test:%d",5); std::cout<<ustr<<std::endl;

输出应该是:test:5

【问题讨论】:

  • 这可能无法解决您的问题,但我确实看到UString::SUBSTR() 存在一些问题。最大的问题是 tmp 不是空终止的。 tmp[stop - start + 1] 必须是 '\0'。缺少该终端'\0' 意味着ret = std::string(tmp) 将无法工作,因为std::string 的构造函数将尝试扫描不应该在的'\0'。第二个问题是在start == 0stop == src.length() - 1的角落情况下,终端'\0'没有足够的空间; tmp需要初始化为new char[src.length()+1]
  • 谢谢。我以前没有意识到。真正的事情是有一个函数可以做到这一点,叫做“vsprintf”,而不是“sprintf”,但还是谢谢你,你帮我找到了另一个错误.

标签: c++ visual-studio visual-studio-2017 printf


【解决方案1】:

使用神秘的vsnprintf() 函数可以更快地实现Format() 成员函数!试试这个:

#include <stdio.h>
#include <stdarg.h>
#include <iostream>

class UString {
public:
    char buffer[4096]; // Just to keep the code simple!
    UString() : buffer {""} {}
    inline void Format(const char* fmt, ...) {
        va_list argList; va_start(argList, fmt);
        vsnprintf(buffer, 4095, fmt, argList);
        va_end(argList);
    }
    operator char* () { return buffer; }
};

int main()
{
    UString test;
    printf("Initial: >>>%s<<<\n", test.operator char *());
    int i = 12; double d = 3.1415926536; const char* s = "Hello, World!";
    test.Format("%s i = %d, d = %lf", s, i, d);
    printf("Tested: >>>%s<<<\n", test.operator char *());
    // Your test ...
    UString ustr;
    ustr.Format("test:%d", 5);
    std::cout << ustr << std::endl;
    return 1;
}

编辑:更正为更“标准”vsnprintf()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-10-21
    • 1970-01-01
    • 2021-04-24
    • 2019-01-24
    • 2013-06-21
    • 2021-03-08
    • 1970-01-01
    相关资源
    最近更新 更多