snprintfおよびVisual Studio 2010


102

残念なことに、プロジェクトでVS 2010を使用できず、次のコードは非標準準拠のコンパイラーを使用してビルドできないことに気づきました。

#include <stdio.h>
#include <stdlib.h>

int main (void)
{
    char buffer[512];

    snprintf(buffer, sizeof(buffer), "SomeString");

    return 0;
}

(エラーでコンパイルが失敗:C3861: 'snprintf':識別子が見つかりません)

私はこれがVS 2005の昔のケースであることを覚えており、まだ修正されていないことにショックを受けています。

Microsoftが標準のCライブラリを2010年に移行する計画があるかどうか、誰か知っていますか?


1
...または、「#define snprintf _snprintf」を実行するだけ
フェルナンドゴンザレスサンチェス

4
...可能ですが、残念ながら_snprintf()は、null終了を保証しないため、snprintf()と同じではありません。
Andy Krouwel

では、_snprintf()を使用する前にゼロにmemsetする必要があります。また、あなたにも同意します。MSVCでの開発はひどいです。エラーも地獄のように混乱しています。
フクロウ

回答:


88

短編: MicrosoftはついにVisual Studio 2015にsnprintfを実装しました。以前のバージョンでは、以下のようにシミュレーションできます。


ロングバージョン:

snprintfの予想される動作は次のとおりです。

int snprintf( char* buffer, std::size_t buf_size, const char* format, ... );

最大でbuf_size - 1文字をバッファに書き込みます。結果の文字列buf_sizeは、ゼロでない限り、ヌル文字で終了し ます。Ifはbuf_sizeゼロであり、何も書かれていないと bufferNULLポインタかもしれません。戻り値はbuf_size、終端のnull文字を数えずに、unlimitedを想定して書き込まれる文字数です。

Visual Studio 2015より前のリリースには、準拠した実装がありませんでした。_snprintf()(オーバーフロー時にnullターミネーターを書き込まない)や_snprintf_s()(null終了を強制できますが、書き込まれるはずの文字数ではなく、オーバーフロー時に-1を返す)などの非標準の拡張機能があります。

VS 2005以降の推奨フォールバック:

#if defined(_MSC_VER) && _MSC_VER < 1900

#define snprintf c99_snprintf
#define vsnprintf c99_vsnprintf

__inline int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap)
{
    int count = -1;

    if (size != 0)
        count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
    if (count == -1)
        count = _vscprintf(format, ap);

    return count;
}

__inline int c99_snprintf(char *outBuf, size_t size, const char *format, ...)
{
    int count;
    va_list ap;

    va_start(ap, format);
    count = c99_vsnprintf(outBuf, size, format, ap);
    va_end(ap);

    return count;
}

#endif

これは常に、オーバーフローで必要な0で文字列を終了するわけではありません。c99_vsnprintfの2番目のifは、次のようにする必要があります。if(count == -1){if(size> 0)str [size-1] = 0; count = _vscprintf(format、ap); }
Lothar

1
@Lothar:バッファは常にnullで終了します。MSDNによれば、「_ TRUNCATEを渡すことによって文字列の切り捨てが有効になっている場合、これらの関数は文字列をコピーするだけで、宛先バッファをnullで終了し、正常に戻ります」。
Valentin Milea 14

2
2014年6月の時点で、Update 2を使用しても、Visual Studioでの「完全な」C99サポートはまだありません。このブログでは、MSVC 2013のC99サポートの概要を説明しています。、C ++ 11実装では、MSVCはclangおよびgccよりも遅れています!
fnisi 14年

2
VS2014では、snprintfおよびvsnprintfを使用したC99標準が追加されています。blogs.msdn.com/b/vcblog/archive/2014/06/18/…を参照してください。
バルカンレイヴン

1
ミカエル・レピスト:本当に?私にとって、_snprintfは、_CRT_SECURE_NO_WARNINGSを有効にした場合にのみ機能します。この回避策は、その手順なしで問題なく機能します。
FvD

33

snprintfC89の一部ではありません。C99のみの標準です。マイクロソフトはC99をサポートする予定はありません

(しかし、C ++ 0xでも標準です...!)

回避策については、以下の他の回答を参照してください。


5
ただし、snprintfと_snprintfの動作に違いがあるため、これは適切な回避策ではありません。_snprintfは、不十分なバッファスペースを処理するときに、ヌルターミネータを遅延して処理します。
Andrew、

7
@DeadMG-間違っています。cl.exeは、ファイルをCコードとしてコンパイルするようコンパイラーに指示する/ Tcオプションをサポートしています。さらに、MSVCには標準Cライブラリのバージョンが付属しています。
Andrew

3
@DeadMG-ただし、C90標準とC99のいくつかのビットをサポートしているため、Cコンパイラになります。
アンドリュー

15
あなたは、1990年から1999年の間住んでいる場合にのみ
子犬

6
-1、Microsoft _snprintfは安全ではない機能であり、動作が異なるsnprintf(必ずしもnullターミネーターを追加する必要はない)ため、この回答で提供されるアドバイスは誤解を招きやすく危険です。
インタージェイ2013年

8

戻り値が必要ない場合は、snprintfを_snprintf_sとして定義することもできます。

#define snprintf(buf,len, format,...) _snprintf_s(buf, len,len, format, __VA_ARGS__)

3

Windowsの同等の機能は sprintf_s


7
sprintf_sとは動​​作が異なりsnprintfます。
インタージェイ2013年

具体的には、sprintf_sのドキュメントでは、「バッファーが小さすぎて印刷するテキストに対して小さすぎる場合、バッファーは空の文字列に設定されます」と述べています。対照的に、snprintfは切り捨てられた文字列を出力に書き込みます。
Andrew Bainbridge

2
@AndrewBainbridge-ドキュメントを切り捨てました。完全な文は、「印刷されるテキストに対してバッファが小さすぎる場合、バッファは空の文字列に設定され、無効なパラメータハンドラが呼び出されます。」です。無効なパラメータハンドルのデフォルトの動作は、プログラムを終了することです。_sファミリーで切り捨てが必要な場合は、snprintf_sと_TRUNCATEフラグを使用する必要があります。はい、_s関数が便利な切り捨て方法を提供しないのは残念です。一方、_s関数はテンプレートマジックを使用してバッファーサイズを推測します。これは優れています。
Bruce Dawson


1

@Valentin Mileaのコードを試しましたが、アクセス違反エラーが発生しました。私のために働いた唯一のものは、Insane Codingの実装でした:http : //asprintf.insanecoding.org/

具体的には、VC ++ 2008レガシーコードを使用していました。非常識コーディングの実装から(上記のリンクからダウンロードすることができますが)、私は3つのファイルを使用:asprintf.casprintf.hvasprintf-msvc.c。他のファイルは、MSVCの他のバージョン用でした。

[編集]完全にするために、その内容は次のとおりです。

asprintf.h:

#ifndef INSANE_ASPRINTF_H
#define INSANE_ASPRINTF_H

#ifndef __cplusplus
#include <stdarg.h>
#else
#include <cstdarg>
extern "C"
{
#endif

#define insane_free(ptr) { free(ptr); ptr = 0; }

int vasprintf(char **strp, const char *fmt, va_list ap);
int asprintf(char **strp, const char *fmt, ...);

#ifdef __cplusplus
}
#endif

#endif

asprintf.c:

#include "asprintf.h"

int asprintf(char **strp, const char *fmt, ...)
{
  int r;
  va_list ap;
  va_start(ap, fmt);
  r = vasprintf(strp, fmt, ap);
  va_end(ap);
  return(r);
}

vasprintf-msvc.c:

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include "asprintf.h"

int vasprintf(char **strp, const char *fmt, va_list ap)
{
  int r = -1, size = _vscprintf(fmt, ap);

  if ((size >= 0) && (size < INT_MAX))
  {
    *strp = (char *)malloc(size+1); //+1 for null
    if (*strp)
    {
      r = vsnprintf(*strp, size+1, fmt, ap);  //+1 for null
      if ((r < 0) || (r > size))
      {
        insane_free(*strp);
        r = -1;
      }
    }
  }
  else { *strp = 0; }

  return(r);
}

使用法(test.cInsane Coding が提供する一部):

#include <stdio.h>
#include <stdlib.h>
#include "asprintf.h"

int main()
{
  char *s;
  if (asprintf(&s, "Hello, %d in hex padded to 8 digits is: %08x\n", 15, 15) != -1)
  {
    puts(s);
    insane_free(s);
  }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.