web-dev-qa-db-ja.com

snprintfおよびVisual Studio 2010

プロジェクトでVS 2010を使用して立ち往生するのは残念ですが、次のコードはまだ標準に準拠していないコンパイラを使用してビルドされません。

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

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

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

    return 0;
}

(次のエラーでコンパイルに失敗します:C3861: 'snprintf':identifier not found)

これはVS 2005での事例であり、まだ修正されていないことにショックを受けています。

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

99
Andrew

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


長いバージョン:

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

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

最大で_buf_size - 1_文字をバッファーに書き込みます。 _buf_size_がゼロでない限り、結果の文字ストリングはヌル文字で終了します。 _buf_size_がゼロの場合、何も書き込まれず、bufferはNULLポインターになる場合があります。戻り値は、無制限の_buf_size_を想定して書き込まれた文字数であり、終端のヌル文字をカウントしません。

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
_
85
Valentin Milea

snprintfはC89の一部ではありません。 C99でのみ標準です。 Microsoftには C99をサポートする計画はありません があります。

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

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

32
kennytm

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

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

Windowsの同等のものはsprintf_s

3
Il-Bhima

snprintf()vsnprintf()の別の安全な置き換えは、ffmpegによって提供されます。 source here (推奨)をチェックアウトできます。

2
Marco Pracucci

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

具体的には、VC++ 2008のレガシーコードを使用していました。 Insane Codingの実装(上記のリンクからダウンロード可能)から、asprintf.casprintf.h、およびvasprintf-msvc.cの3つのファイルを使用しました。他のファイルは、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);
}

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

#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);
  }
}
1
andertavares