snprintf und Visual Studio 2010

Lesezeit: 6 Minuten

Benutzeravatar von Andrew
Andreas

Ich bin unglücklich genug, mit VS 2010 für ein Projekt stecken zu bleiben, und habe festgestellt, dass der folgende Code immer noch nicht mit dem nicht standardkonformen Compiler erstellt wird:

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

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

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

    return 0;
}

(Kompilierung schlägt fehl mit dem Fehler: C3861: ‘snprintf’: Identifier not found)

Ich erinnere mich, dass dies vor langer Zeit bei VS 2005 der Fall war, und ich bin schockiert, dass es immer noch nicht behoben wurde.

Weiß jemand, ob Microsoft irgendwelche Pläne hat, seine Standard-C-Bibliotheken in das Jahr 2010 zu verschieben?

  • … oder Sie können einfach “#define snprintf _snprintf” tun

    – Fernando González Sánchez

    25. Februar 2015 um 1:16 Uhr

  • …könnten Sie, aber leider ist _snprintf() nicht dasselbe wie snprintf(), da es keine Nullterminierung garantiert.

    – Andy Krouwel

    16. Oktober 2015 um 8:46 Uhr

  • Ok, also müssen Sie es auf Null setzen, bevor Sie _snprintf() verwenden. Auch da stimme ich dir zu. Unter MSVC zu entwickeln ist schrecklich. Die Fehler sind auch verdammt verwirrend.

    – Eule

    17. August 2016 um 13:37 Uhr

Benutzeravatar von Valentin Milea
Valentin Milea

Kurzgeschichte: Microsoft hat snprintf endlich in Visual Studio 2015 implementiert. In früheren Versionen können Sie es wie folgt simulieren.


Lange Version:

Hier ist das erwartete Verhalten für snprintf:

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

Schreibt höchstens buf_size - 1 Zeichen in einen Puffer. Die resultierende Zeichenfolge wird mit einem Nullzeichen abgeschlossen, es sei denn
buf_size ist Null. Wenn buf_size Null ist, wird nichts geschrieben und
buffer kann ein Nullzeiger sein. Der Rückgabewert ist die Anzahl der Zeichen, die unter der Annahme von unbegrenzt geschrieben worden wären buf_sizewobei das abschließende Nullzeichen nicht mitgezählt wird.

Versionen vor Visual Studio 2015 hatten keine konforme Implementierung. Es gibt stattdessen nicht standardmäßige Erweiterungen wie z _snprintf() (der bei Überlauf keinen Nullterminator schreibt) und _snprintf_s() (was eine Null-Terminierung erzwingen kann, aber bei Überlauf -1 anstelle der Anzahl der Zeichen zurückgibt, die geschrieben worden wären).

Vorgeschlagener Fallback für VS 2005 und höher:

#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

  • Dadurch wird die Zeichenfolge nicht immer mit einer 0 abgeschlossen, die bei einem Überlauf erforderlich ist. Das zweite if in c99_vsnprintf muss sein: if (count == -1) { if (size > 0) str[size-1] = 0; count = _vscprintf (Format, ap); }

    – Lothar

    11. Februar 2014 um 2:09 Uhr


  • @Lothar: Der Puffer ist immer nullterminiert. Laut MSDN: “Wenn das Abschneiden von Zeichenfolgen durch Übergeben von _TRUNCATE aktiviert ist, kopieren diese Funktionen nur so viel von der Zeichenfolge, wie hineinpasst, wobei der Zielpuffer nullterminiert bleibt und erfolgreich zurückgegeben wird”.

    – Valentin Milea

    12. Februar 2014 um 9:18 Uhr

  • Stand Juni 2014 gibt es noch immer keine “vollständige” C99-Unterstützung in Visual Studio, selbst mit Update 2. Dieser Blog gibt den C99-Supportbrief für MSVC 2013. Da die Funktionen der snprintf()-Familie jetzt Teil des C++11-Standards sind, bleibt MSVC in der C++11-Implementierung hinter clang und gcc zurück!

    – fnisi

    3. Juni 2014 um 23:24 Uhr


  • Mit VS2014 werden C99-Standards mit snprintf und vsnprintf hinzugefügt. Sehen blogs.msdn.com/b/vcblog/archive/2014/06/18/….

    – Vulkanrabe

    29. Oktober 2014 um 23:12 Uhr

  • Mikael Lepistö: Wirklich? Für mich funktioniert _snprintf nur, wenn ich _CRT_SECURE_NO_WARNINGS aktiviere. Diese Problemumgehung funktioniert gut ohne diesen Schritt.

    – FvD

    10. September 2015 um 13:46 Uhr

Benutzeravatar von kennytm
kennytm

snprintf ist kein Teil von C89. Es ist nur in C99 Standard. Microsoft hat kein Plan, der C99 unterstützt.

(Aber es ist auch Standard in C++0x…!)

Siehe andere Antworten unten für eine Problemumgehung.

  • Es ist jedoch keine gute Problemumgehung … da es Unterschiede im Verhalten von snprintf und _snprintf gibt. _snprintf verarbeitet das Null-Terminator verzögert, wenn es um unzureichenden Pufferspeicher geht.

    – Andreas

    26. Mai 2010 um 18:37 Uhr

  • @DeadMG – falsch. cl.exe unterstützt die Option /Tc, die den Compiler anweist, eine Datei als C-Code zu kompilieren. Darüber hinaus wird MSVC mit einer Version von Standard-C-Bibliotheken ausgeliefert.

    – Andreas

    26. Mai 2010 um 20:04 Uhr

  • @DeadMG – es unterstützt jedoch den C90-Standard sowie einige Teile von C99, was es zu einem C-Compiler macht.

    – Andreas

    27. Mai 2010 um 14:26 Uhr

  • Nur wenn Sie zwischen 1990 und 1999 leben.

    – Welpe

    27. Mai 2010 um 17:41 Uhr

  • -1, Microsofts _snprintf ist eine unsichere Funktion, die sich anders verhält snprintf (es fügt nicht unbedingt ein Null-Terminator hinzu), daher ist der in dieser Antwort gegebene Rat irreführend und gefährlich.

    – zwischenjay

    4. Juni 2013 um 9:31 Uhr


Wenn Sie den Rückgabewert nicht benötigen, können Sie snprintf auch einfach als _snprintf_s definieren

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

Ich glaube, das Windows-Äquivalent ist sprintf_s

Ein weiterer sicherer Ersatz von snprintf() und vsnprintf() wird von ffmpeg bereitgestellt. Sie können die auschecken Quelle hier (empfohlen).

Ich habe den Code von @Valentin Milea ausprobiert, aber ich habe Zugriffsverletzungsfehler. Das einzige, was für mich funktioniert hat, war die Implementierung von Insane Coding: http://asprintf.insanecoding.org/

Insbesondere habe ich mit VC++2008-Legacy-Code gearbeitet. Aus der Implementierung von Insane Coding (kann über den obigen Link heruntergeladen werden) habe ich drei Dateien verwendet: asprintf.c, asprintf.h und vasprintf-msvc.c. Andere Dateien waren für andere Versionen von MSVC.

[EDIT] Der Vollständigkeit halber sind ihre Inhalte wie folgt:

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);
}

Verwendung (Teil von test.c bereitgestellt von Insane 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);
  }
}

1421750cookie-checksnprintf und Visual Studio 2010

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy