3

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

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是一个窗口结构,其中lfFaceName定义为TCHAR lfFaceName[LF_FACESIZE]

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

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

或者,如果有完全不同的选择,请告诉我。

4

5 回答 5

8

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

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

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

你当然可以用它std::string帮助解决这个问题,但它不会让你不需要检查长度并手动复制字符串。我可能会这样做。LOGFONTW请注意,我CreateFontIndirectWstd::string. 另请注意,其中的大部分内容是从 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

    // ...
}
于 2011-10-21T16:21:18.643 回答
3

lf.lfFaceName = faceName.c_str();

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

由于您需要复制 ac 字符串,因此您需要一个 'c' 函数,然后 strcpy_s (或等效的)是安全的选择

于 2011-10-21T16:09:44.343 回答
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;
}
于 2011-10-21T16:22:51.870 回答
0

你有没有尝试过?鉴于您帖子中的信息,分配应该会生成编译器错误,因为您正在尝试将指针分配给数组,这在 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 替代方案,因为无论如何您都知道目标空间的大小。

于 2011-10-21T16:15:30.003 回答
-1

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);
于 2011-10-21T16:16:27.760 回答