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いるウィンドウ構造です。lfFaceNameTCHAR lfFaceName[LF_FACESIZE]

私が疑問に思っているのは、次のようなことをすることができるかどうかです(そうでない場合はなぜですか):

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

または、まったく別の選択肢がある場合は、私に知らせてください。

4

5 に答える 5

8

セキュリティ警告が表示される理由は、引数が文字よりも長い文字列を指している可能性があり、構造faceName内の後に続くものを盲目的に上書きする可能性があるためです。バグがあります。LF_FACESIZEstrcpylfFaceNameLOGFONT

次の理由から、に変更してやみくもにバグを修正しないでください。strcpystrcpy_s

  1. これらの関数は移植性のないマイクロソフトの発明であり、そのほとんどすべて*_s、移植性のある他のCライブラリ関数の機能を複製しています。移植性を意図していないプログラムであっても、決して使用しないでください(これはそう思われます)。
  2. ブラインド変更は、このクラスのバグを実際には修正しない傾向があります。たとえば、(、、)の「安全な」バリアントは、strcpy文字strncpy列が長すぎる場合は単に文字列を切り捨てます。この場合、間違ったフォントを読み込もうとします。さらに悪いことに、NULターミネーターを使用する場合は省略します。そのため、NULターミネーターを使用した場合 は、クラッシュを内部に移動するだけです。正しい修正方法は、前もって長さを確認し、長すぎる場合は操作全体を失敗させることです。その時点で安全になります(長すぎないことがわかっているため)が、これについて考えたことをコードの将来の読者に明らかにするため、私は好みます。strlcpystrcpy_sstrncpyCreateFontIndirectstrcpymemcpy
  3. TCHARchar同じものではありません。適切なエンコーディング変換を行わずにCスタイルのconst char *文字列またはC++のいずれかstd::stringをの配列にコピーすると、まったく意味がなくなる場合があります。TCHARTCHAR私の経験では、使用は常に間違いであり、最大の問題は、このようなコードがASCIIビルドで正しく機能し、 UNICODEモードでコンパイルされても、実行時に壊滅的に失敗することです。)

あなたは確かにこの問題std::string助けるために使うことができます、しかしそれはあなたが長さをチェックしてそして手動で文字列をコピーする必要からあなたを取り除くことはありません。私はおそらくこのようにするでしょう。LOGFONTWでUTF-8からのandCreateFontIndirectWと明示的な変換を使用していることに注意してくださいstd::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内に保持されているデータにpoitnerのローカルコピーを作成しているため、これを行うべきではありません。c ++文字列が変更されるか削除されると、ポインタは無効になり、lFaceNameがデータを変更することを決定した場合、これはほぼ確実にstd::stringを破壊します。

ac文字列をコピーする必要があるため、「c」関数が必要です。次に、strcpy_s(または同等のもの)が安全な代替手段です。

于 2011-10-21T16:09:44.343 に答える
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 に答える
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 に答える
-1

lf.lfFaceName = faceName.c_str();2つの理由で機能しません(faceNameをstd:stringに変更すると仮定します)

  1. c_str()によって返されるポインタの存続期間は一時的なものです。fileNameオブジェクトが変更されず、有効である場合にのみ有効です。
  2. 行はコンパイルされません。.c_str()はcharへのポインタを返し、lfFaceNameは文字配列であり、割り当てることはできません。文字列配列を埋めるため、lfFaceNameのバイトを埋めるために何かをする必要がありますが、ポインタの割り当てはそれを行いません。

lfFaceNameはCの「文字列」であるため、ここで役立つC++はありません。strcpyやstrcpy_sなどのC文字列関数を使用する必要があります。コードを次のように変更できます。

strcpy_s(lf.lfFaceName, LF_FACESIZE, faceName);
于 2011-10-21T16:16:27.760 に答える