C++ std::string 可以替代 strcpy 吗?

发布于 2024-12-11 06:16:32 字数 1070 浏览 1 评论 0原文

我知道已经有一个类似标题的问题,但我想知道针对这个特定案例的选择。

MSVC 编译器给出有关 strcpy 的警告:

1>c:\something\mycontrol.cpp(65): warning C4996: 'strcpy': This function or
variable may be unsafe. Consider using strcpy_s instead. To disable
deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.

这是我的代码:

void MyControl::SetFontFace(const char *faceName)
{
    LOGFONT lf;

    CFont *currentFont = GetFont();
    currentFont->GetLogFont(&lf);
    strcpy(lf.lfFaceName, faceName); <--- offending line
    font_.DeleteObject();
    // Create the font.
    font_.CreateFontIndirect(&lf);

    // Use the font to paint a control.
    SetFont(&font_);
}

注意 font_ 是一个实例变量。 LOGFONT 是一个 Windows 结构,其中 lfFaceName 定义为 TCHAR lfFaceName[LF_FACESIZE]

我想知道的是我可以做类似以下的事情(如果不是为什么不):

void MyControl::SetFontFace(const std::string& faceName)
...
  lf.lfFaceName = faceName.c_str();
...

或者如果有完全不同的替代方案,请告诉我。

I know there is a similarly titled question already on SO but I want to know my options for this specific case.

MSVC compiler gives a warning about strcpy:

1>c:\something\mycontrol.cpp(65): warning C4996: 'strcpy': This function or
variable may be unsafe. Consider using strcpy_s instead. To disable
deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.

Here's my code:

void MyControl::SetFontFace(const char *faceName)
{
    LOGFONT lf;

    CFont *currentFont = GetFont();
    currentFont->GetLogFont(&lf);
    strcpy(lf.lfFaceName, faceName); <--- offending line
    font_.DeleteObject();
    // Create the font.
    font_.CreateFontIndirect(&lf);

    // Use the font to paint a control.
    SetFont(&font_);
}

Note font_ is an instance variable. LOGFONT is a windows structure where lfFaceName is defined as TCHAR lfFaceName[LF_FACESIZE].

What I'm wondering is can I do something like the following (and if not why not):

void MyControl::SetFontFace(const std::string& faceName)
...
  lf.lfFaceName = faceName.c_str();
...

Or if there is a different alternative altogether then let me know.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

梦里梦着梦中梦 2024-12-18 06:16:32

您收到安全警告的原因是,您的 faceName 参数可能指向一个比 LF_FACESIZE 字符的字符串,然后 < code>strcpy 会盲目地覆盖 LOGFONT 结构中 lfFaceName 之后的任何内容。你确实有一个错误。

您不应该通过将 strcpy 更改为 strcpy_s 来盲目地修复该错误,因为:

  1. *_s 函数是不可移植的 Microsoft几乎所有的发明都复制了其他可移植的 C 库函数的功能。永远不应该使用它们,即使是在不打算移植的程序中(正如看起来的那样)。
  2. 盲目的改变往往并不能真正修复此类错误。例如,strcpy 的“安全”变体(strncpystrlcpystrcpy_s)仅在以下情况下截断字符串:它太长了,在这种情况下会让您尝试加载错误的字体。更糟糕的是,strncpy 在执行此操作时会忽略 NUL 终止符,因此如果您使用了 CreateFontIndirect,您可能会将崩溃移至 CreateFontIndirect 内。 正确的修复方法是预先检查长度,如果太长则整个操作失败。在这一点上,strcpy变得安全(因为你知道它不会太长),尽管我更喜欢memcpy,因为它让未来的代码读者可以清楚地看到我所认为的关于这个。
  3. TCHARchar 不是同一件事;将 C 样式 const char * 字符串或 C++ std::string 复制到 TCHAR 数组而不进行适当的编码转换可能会产生完全是胡说八道。 (根据我的经验,使用 TCHAR 总是一个错误,最大的问题是这样的代码在 ASCII 构建中似乎可以正常工作,并且仍然可以编译 在 UNICODE 模式下,但在运行时会发生灾难性的失败。)

您当然可以使用 std::string帮助解决这个问题,但它不会帮助您无需检查长度并手动复制字符串。我可能会这样做。请注意,我在 std::string 中使用 LOGFONTWCreateFontIndirectW 以及从 UTF-8 的显式转换。另请注意,其中大部分内容是从 MSDN 中收集而来的,并且没有经过测试。对不起。

void MyControl::SetFontFace(const std::string& faceName)
{
    LOGFONTW lf;
    this->font_.GetLogFontW(&lf);

    int count = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
                                    faceName.data(), faceName.length(),
                                    lf.lfFaceName, LF_FACESIZE - 1)
    if (count <= 0)
        throw GetLastError(); // FIXME: use a real exception

    lf.lfFaceName[count] = L'\0'; // MultiByteToWideChar does not NUL-terminate.

    this->font_.DeleteObject();
    if (!this->font_.CreateFontIndirectW(&lf))
        throw GetLastError(); // FIXME: use a real exception

    // ...
}

The reason you're getting the security warning is, your faceName argument could point to a string that is longer than LF_FACESIZE characters, and then strcpy would blindly overwrite whatever comes after lfFaceName in the LOGFONT structure. You do have a bug.

You should not blindly fix the bug by changing strcpy to strcpy_s, because:

  1. The *_s functions are unportable Microsoft inventions almost all of which duplicate the functionality of other C library functions that are portable. They should never be used, even in a program not intended to be portable (as this appears to be).
  2. Blind changes tend to not actually fix this class of bug. For instance, the "safe" variants of strcpy (strncpy, strlcpy, strcpy_s) simply truncate the string if it's too long, which in this case would make you try to load the wrong font. Worse, strncpy omits the NUL terminator when it does that, so you'd probably just move the crash inside CreateFontIndirect if you used that one. The correct fix is to check the length up front and fail the entire operation if it's too long. At which point strcpy becomes safe (because you know it's not too long), although I prefer memcpy because it makes it obvious to future readers of the code that I've thought about this.
  3. TCHAR and char are not the same thing; copying either a C-style const char * string or a C++ std::string into an array of TCHAR without a proper encoding conversion may produce complete nonsense. (Using TCHAR is, in my experience, always a mistake, and the biggest problem with it is that code like this will appear to work correctly in an ASCII build, and will still compile in UNICODE mode, but will then fail catastrophically at runtime.)

You certainly can use std::string to help with this problem, but it won't get you out of needing to check the length and manually copy the string. I'd probably do it like this. Note that I am using LOGFONTW and CreateFontIndirectW and an explicit conversion from UTF-8 in the std::string. Note also that chunks of this were cargo-culted out of MSDN and none of it has been tested. Sorry.

void MyControl::SetFontFace(const std::string& faceName)
{
    LOGFONTW lf;
    this->font_.GetLogFontW(&lf);

    int count = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
                                    faceName.data(), faceName.length(),
                                    lf.lfFaceName, LF_FACESIZE - 1)
    if (count <= 0)
        throw GetLastError(); // FIXME: use a real exception

    lf.lfFaceName[count] = L'\0'; // MultiByteToWideChar does not NUL-terminate.

    this->font_.DeleteObject();
    if (!this->font_.CreateFontIndirectW(&lf))
        throw GetLastError(); // FIXME: use a real exception

    // ...
}
述情 2024-12-18 06:16:32

lf.lfFaceName = faceName.c_str();

不,您不应该这样做,因为您正在将指向器的本地副本复制到 std::string 中保存的数据。如果 c++ 字符串更改或被删除,则指针不再有效,并且如果 lFaceName 决定更改数据,这几乎肯定会破坏 std::string。

由于您需要复制 ac 字符串,因此需要一个 'c' 函数,那么 strcpy_s (或等效的)是安全的替代方案

lf.lfFaceName = faceName.c_str();

No you shouldn't do that because you are making a local copy of the poitner to the data held inside the std::string. If the c++ string changes, or is deleted, the pointer is no longer valid, and if lFaceName decides to change the data this will almost certainly break the std::string.

Since you need to copy a c string, you need a 'c' function, then strcpy_s (or it's equivalent) is the safe alternative

爱的那么颓废 2024-12-18 06:16:32

你尝试过吗?鉴于您帖子中的信息,该赋值应该会生成编译器错误,因为您尝试将指针分配给数组,这在 C(++) 中不起作用。

#include <cstdio>
#include <string>
using namespace std;

struct LOGFONT {
 char lfFaceName[3];
};


int main() {
        struct LOGFONT f;
        string foo="bar";
        f.lfFaceName = foo.c_str();
        return 0;
}

导致

x.c:13: error: incompatible types in assignment of `const char*' to `char[3]'

我建议使用安全的 strcpy 替代方案,如警告所述,前提是您无论如何都知道目标空间的大小。

Have you tried? Given the information in your post, the assignment should generate a compiler error because you're trying to assign a pointer to an array, which does not work in C(++).

#include <cstdio>
#include <string>
using namespace std;

struct LOGFONT {
 char lfFaceName[3];
};


int main() {
        struct LOGFONT f;
        string foo="bar";
        f.lfFaceName = foo.c_str();
        return 0;
}

leads to

x.c:13: error: incompatible types in assignment of `const char*' to `char[3]'

I'd recommend using a secure strcpy alternative like the warning says, given that you know the size of the destination space anyway.

赠佳期 2024-12-18 06:16:32
#include <algorithm>
#include <iostream>
#include <string>

enum { LF_FACESIZE = 256 }; // = 3 // test too-long input
struct LOGFONT
{
    char lfFaceName[LF_FACESIZE];
};

int main()
{
    LOGFONT f;
    std::string foo("Sans-Serif");
    std::copy_n(foo.c_str(), foo.size()+1 > LF_FACESIZE ? LF_FACESIZE : foo.size()+1,
                f.lfFaceName);

    std::cout << f.lfFaceName << std::endl;
    return 0;
}
#include <algorithm>
#include <iostream>
#include <string>

enum { LF_FACESIZE = 256 }; // = 3 // test too-long input
struct LOGFONT
{
    char lfFaceName[LF_FACESIZE];
};

int main()
{
    LOGFONT f;
    std::string foo("Sans-Serif");
    std::copy_n(foo.c_str(), foo.size()+1 > LF_FACESIZE ? LF_FACESIZE : foo.size()+1,
                f.lfFaceName);

    std::cout << f.lfFaceName << std::endl;
    return 0;
}
夏末 2024-12-18 06:16:32

lf.lfFaceName = faceName.c_str(); 不起作用有两个原因(假设您将faceName 更改为std:string)

  1. c_str() 返回的指针的生命周期是临时的。仅当 fileName 对象未更改且处于活动状态时它才有效。
  2. 该行将无法编译。 .c_str() 返回一个指向 char 的指针,而 lfFaceName 是一个字符数组,无法赋值。您需要执行一些操作来填充字符串数组,填充 lfFaceName 处的字节,而指针赋值不会执行此操作。

没有任何 C++ 可以帮助这里,因为 lfFaceName 是一个 C“字符串”。您需要使用 C 字符串函数,例如 strcpy 或 strcpy_s。您可以将代码更改为:

strcpy_s(lf.lfFaceName, LF_FACESIZE, faceName);

lf.lfFaceName = faceName.c_str(); won't work for two reasons (assuming you change faceName to a std:string)

  1. The lifetime of the pointer returned by c_str() is temporary. It's only valid as long as the fileName object doesn't change and in alive.
  2. The line won't compile. .c_str() returns a pointer to a char, and lfFaceName is a character array and can't be assigned to. You need to do something to fill in the string array, to fill in the bytes at lfFaceName, and pointer assignment doesn't do that.

There isn't anything C++ that can help here, since lfFaceName is a C "string". You need to use a C string function, like strcpy or strcpy_s. You can change your code to:

strcpy_s(lf.lfFaceName, LF_FACESIZE, faceName);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文