C/printf

< C
(Przekierowano z C/vsnprintf)

Deklaracja

edytuj
#include <stdio.h>

int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char *format, ...)

#include <stdarg.h>

int vprintf(const char *format, va_list ap);
int vfprintf(FILE *stream, const char *format, va_list ap);
int vsprintf(char *str, const char *format, va_list ap);
int vsnprintf(char *str, size_t size, const char *format, va_list ap);

Kod  źródłowy

edytuj

Funkcje formatują tekst zgodnie z podanym formatem opisanym poniżej. Funkcje printf i vprintf wypisują tekst na standardowe wyjście (tj. do stdout); fprintf i vfprintf do strumienia podanego jako argument; a sprintf, vsprintf, snprintf i vsnprintf zapisują go w podanej jako argument tablicy znaków.

Funkcje vprintf, vfprintf, vsprintf i vsnprintf różnią się od odpowiadających im funkcjom printf, fprintf, sprintf i snprintf tym, że zamiast zmiennej liczby argumentów przyjmują argument typu va_list.

Funkcje snprintf i vsnprintf różnią się od sprintf i vsprintf tym, że nie zapisuje do tablicy nie więcej niż size znaków (wliczając kończący znak '\0'). Oznacza to, że można je używać bez obawy o wystąpienie przepełnienia bufora.

Argumenty

edytuj
format
format, w jakim zostaną wypisane następne argumenty
stream
strumień wyjściowy ( lub plik), do którego mają być zapisane dane
str
tablica znaków, do której ma być zapisany sformatowany tekst
size
rozmiar tablicy znaków
ap
wskaźnik na pierwszy argument z listy zmiennej liczby argumentów


Konwersja:

"%d", 100 

spowoduje zapisanie:

[ 1 | 0 | 0 | \0 ]

4 znaków do tablicy:

  • 3 znaków liczby całkowitej
  • znaku końca łańcucha (snprintf automatycznie dopisuje ten znak)

Jeśli: [1]

  • rozmiar tablicy str jest <4 ale argument size jest >=4 to powstaje niezdefiniowanie zachowanie (ang. undefined behavior)
  • rozmiar tablicy str >=4 ale argument size jest <4, to nie powstaje niezdefiniowanie zachowanie, ale łańcuch zostanie obcięty (ang. truncated)

Format

edytuj

Format składa się ze zwykłych znaków (innych niż znak '%'), które są kopiowane bez zmian na wyjście oraz sekwencji sterujących, zaczynających się od symbolu procenta, po którym następuje:

  • dowolna liczba flag,
  • opcjonalne określenie minimalnej szerokości pola,
  • opcjonalne określenie precyzji,
  • opcjonalne określenie rozmiaru argumentu,
  • określenie formatu.

Jeżeli po znaku procenta występuje od razu drugi procent to cała sekwencja traktowana jest jak zwykły znak procenta (tzn. jest on wypisywany na wyjście).

Format kończy sekwencja wyjścia ( ang. escape sequence [2]), np.

  \n

W sekwencji możliwe są następujące flagi:

  • - (minus) oznacza, że pole ma być wyrównane do lewej, a nie do prawej.
  • + (plus) oznacza, że dane liczbowe zawsze poprzedzone są znakiem (plusem dla liczb nieujemnych lub minusem dla ujemnych).
  • spacja oznacza, że liczby nieujemne poprzedzone są dodatkową spacją; jeżeli flaga plus i spacja są użyte jednocześnie to spacja jest ignorowana.
  • # (hash) powoduje, że wynik jest przedstawiony w alternatywnej postaci:
    • dla formatu o powoduje to zwiększenie precyzji, jeżeli jest to konieczne, aby na początku wyniku było zero;
    • dla formatów x i X niezerowa liczba poprzedzona jest ciągiem 0x lub 0X;
    • dla formatów a, A, e, E, f, F, g i G wynik zawsze zawiera kropkę nawet jeżeli nie ma za nią żadnych cyfr;
    • dla formatów g i G końcowe zera nie są usuwane.
  • 0 (zero) dla formatów d, i, o, u, x, X, a, A, e, E, f, F, g i G do wyrównania pola wykorzystywane są zera zamiast spacji za wyjątkiem wypisywania wartości nieskończoność i NaN. Jeżeli obie flagi 0 i - są obecne to flaga zero jest ignorowana. Dla formatów d, i, o, u, x i X jeżeli określona jest precyzja flaga ta jest ignorowana.

Szerokość pola i precyzja

edytuj

Minimalna szerokość pola oznacza ile najmniej znaków ma zająć dane pole. Jeżeli wartość po formatowaniu zajmuje mniej miejsca jest ona wyrównywana spacjami z lewej strony (chyba, że podano flagi, które modyfikują to zachowanie). Domyślna wartość tego pola to 0.

Precyzja dla formatów:

  • d, i, o, u, x i X określa minimalną liczbę cyfr, które mają być wyświetlone i ma domyślną wartość 1;
  • a, A, e, E, f i F - liczbę cyfr, które mają być wyświetlone po kropce i ma domyślną wartość 6;
  • g i G określa liczbę cyfr znaczących i ma domyślną wartość 1;
  • dla formatu s - maksymalną liczbę znaków, które mają być wypisane.

Szerokość pola może być albo dodatnią liczbą zaczynającą się od cyfry różnej od zera albo gwiazdką. Podobnie precyzja z tą różnicą, że jest jeszcze poprzedzona kropką. Gwiazdka oznacza, że brany jest kolejny z argumentów, który musi być typu int. Wartość ujemna przy określeniu szerokości jest traktowana tak jakby podano flagę - (minus).

Rozmiar argumentu

edytuj

Dla formatów d i i można użyć jednego ze modyfikator rozmiaru:

  • hh - oznacza, że format odnosi się do argumentu typu signed char,
  • h - oznacza, że format odnosi się do argumentu typu short,
  • l (el) - oznacza, że format odnosi się do argumentu typu long,
  • ll (el el) - oznacza, że format odnosi się do argumentu typu long long,
  • j - oznacza, że format odnosi się do argumentu typu intmax_t,
  • z - oznacza, że że format odnosi się do argumentu typu będącego odpowiednikiem typu size_t ze znakiem,
  • t - oznacza, że że format odnosi się do argumentu typu ptrdiff_t.

Dla formatów o, u, x i X można użyć takich samych modyfikatorów rozmiaru jak dla formatu d i oznaczają one, że format odnosi się do argumentu odpowiedniego typu bez znaku.

Dla formatu n można użyć takich samych modyfikatorów rozmiaru jak dla formatu d i oznaczają one, że format odnosi się do argumentu będącego wskaźnikiem na dany typ.

Dla formatów a, A, e, E, f, F, g i G można użyć modyfikatorów rozmiaru L, który oznacza, że format odnosi się do argumentu typu long double.

Dodatkowo, modyfikator l (el) dla formatu c oznacza, że odnosi się on do argumentu typu wint_t, a dla formatu s, że odnosi się on do argumenty typu wskaźnik na wchar_t.

Format

edytuj

Funkcje z rodziny printf obsługują następujące formaty:

  • d, i - argument typu int jest przedstawiany jako liczba całkowita ze znakiem w postaci [-]ddd.
  • o, u, x, X - argument typu unsigned int jest przedstawiany jako nieujemna liczba całkowita zapisana w systemie oktalnym (o), dziesiętnym (u) lub heksadecymalnym (x i X).
  • f, F - argument typu double jest przedstawiany w postaci [-]ddd.ddd.
  • e, E - argument typu double jest reprezentowany w postaci [i]d.ddde+dd, gdzie liczba przed kropką dziesiętną jest różna od zera, jeżeli liczba jest różna od zera, a + oznacza znak wykładnika. Format E używa wielkiej litery E zamiast małej.
  • g, G - argument typu double jest reprezentowany w formacie takim jak f lub e (odpowiednio F lub E) zależnie od liczby znaczących cyfr w liczbie oraz określonej precyzji.
  • a, A - argument typu double przedstawiany jest w formacie [-]0xh.hhhp+d czyli analogicznie jak dla e i E, tyle że liczba zapisana jest w systemie heksadecymalnym.
  • c - argument typu int jest konwertowany do unsigned char i wynikowy znak jest wypisywany. Jeżeli podano modyfikator rozmiaru l argument typu wint_t konwertowany jest do wielobajtowej sekwencji i wypisywany.
  • s - argument powinien być typu wskaźnik na char (lub wchar_t). Wszystkie znaki z podanej tablicy, kończące się na null, są wypisywane.
  • p - argument powinien być typu wskaźnik na void. Jest on konwertowany na serię drukowalnych znaków w sposób zależny od implementacji.
  • n - argument powinien być wskaźnikiem na liczbę całkowitą ze znakiem, do którego zwracana jest liczba zapisanych znaków.

W przypadku formatów f, F, e, E, g, G, a i A wartość nieskończoność jest przedstawiana w formacie [-]inf lub [-]infinity zależnie od implementacji. Wartość NaN jest przedstawiana w postaci [-]nan lub [i]nan(sekwencja), gdzie sekwencja jest zależna od implementacji. W przypadku formatów określonych wielką literą również wynikowy ciąg znaków jest wypisywany wielką literą.


liczb całkowitych o stałej szerokości

edytuj

W pliku inttypes.h są ( od C99) zdefiniowane makra dla liczb całkowitych o stałej szerokości ( ang. Fixed width integer types ) [3]


  printf("%" PRId64 "\n", t);

Wartość zwracana

edytuj

Jeżeli funkcje zakończą się sukcesem zwracają liczbę znaków w tekście (wypisanym na standardowe wyjście, do podanego strumienia lub tablicy znaków) nie wliczając kończącego '\0'. W przeciwnym wypadku zwracana jest liczba ujemna.

Wyjątkami są funkcje snprintf i vsnprintf, które zwracają liczbę znaków, które zostałyby zapisane do tablicy znaków, gdyby była wystarczająco duża.

Przykłady

edytuj

Podstawowe użycie

edytuj
#include <stdio.h>

int main()
{
    int i = 4;
    float f = 3.1415;
    const char *s = "Monty Python";
    printf("i = %d\nf = %.1f\nWskaznik s wskazuje na napis: %s\n", i, f, s);
    return 0;
}
i = 4
f = 3.1
Wskaźnik s wskazuje na napis: Monty Python

Konwersja

edytuj

Konwersja 1 liczby typu double na łańcuch:

/*
gcc s.c -Wall

http://stackoverflow.com/questions/7228438/convert-double-float-to-string
*/
#include <stdio.h>


int main() {


  size_t size = sizeof(double);
  double  d = 234.567;
  char s[size];   
  
  snprintf(s, size, "%f", d);   // convert double to string 
  printf(" d = %f = %s \n", d,s); // check

  return 0;
}

Konwersja 2 liczb typu double na łańcuch:

/*
http://stackoverflow.com/questions/7228438/convert-double-float-to-string

http://linux.die.net/man/3/snprintf    
The conversion specifier : f, F
The double argument is rounded and converted to decimal notation in the style [-]ddd.ddd, 
where the number of digits after the decimal-point character is equal to the precision specification. 
If the precision is missing, it is taken as 6; 

The precision
the number of digits to appear after the radix character for a, A, e, E, f, and F conversions,
*/
#include <stdio.h>

int main()
{

  /* And remember to allow space for :
      - the trailing null '\0' character!
      - comma
  */

  int   n= 2; // number of doubles 
  size_t z_size = n*sizeof(double) +4; 
  // doubles with 15 decimal digits after comma 
  double  d1 = -1.123456789012345;
  double  d2 = -2.123456789012345;
  char sz[z_size];  // 
  // The return value is the number of characters which would be generated for the given input, excluding the trailing null, as per ISO C99. If the return is greater than or equal to size, the resulting string is truncated.
  int rz; // return value , Upon successful return, these functions return the number of characters printed (excluding the null byte used to end output to strings).

 
  // double has 15–17 significant decimal digits precision.
  rz = snprintf(sz, z_size, "%f,%f", d1, d2);   // convert 2 double to string with rounding on 6 decimal place

 
  printf("d = %s\n", sz); // check
  printf ("good size of string is r = %d but now true size is = %zd  \n", rz, z_size);

  return 0;
}

Wynik działania:

d = -1.123457,-2.123457
good size of string is = 19 and now true size is = 20  

Liczby całkowite

edytuj
/*
gcc l.c -lm -Wall
./a.out

http://stackoverflow.com/questions/29592898/do-long-long-and-long-have-same-range-in-c-in-64-bit-machine
*/
#include <stdio.h>
#include <math.h> // M_PI; needs -lm also 
#include <limits.h> // INT_MAX, http://pubs.opengroup.org/onlinepubs/009695399/basedefs/limits.h.html

int main()
{
 double lMax;

 lMax = log2(INT_MAX);
 printf("INT_MAX \t= %25d ; lMax = log2(INT_MAX) \t= %.0f \n",INT_MAX,  lMax); 

 lMax = log2(UINT_MAX);
 printf("UINT_MAX \t= %25u ; lMax = log2(UINT_MAX) \t= %.0f \n", UINT_MAX,  lMax); 

 lMax = log2(LONG_MAX);
 printf("LONG_MAX \t= %25ld ; lMax = log2(LONG_MAX) \t= %.0f \n",LONG_MAX,  lMax); 


 lMax = log2(ULONG_MAX);
 printf("ULONG_MAX \t= %25lu ; lMax = log2(ULONG_MAX) \t= %.0f \n",ULONG_MAX,  lMax); 

 lMax = log2(LLONG_MAX);
 printf("LLONG_MAX \t= %25lld ; lMax = log2(LLONG_MAX) \t= %.0f \n",LLONG_MAX, lMax); 

 lMax = log2(ULLONG_MAX);
 printf("ULLONG_MAX \t= %25llu ; lMax = log2(ULLONG_MAX) \t= %.0f \n",ULLONG_MAX, lMax); 

 return 0;
}

Wynik:

INT_MAX 	=                2147483647 ; lMax = log2(INT_MAX) 	= 31 
UINT_MAX 	=                4294967295 ; lMax = log2(UINT_MAX) 	= 32 
LONG_MAX 	=       9223372036854775807 ; lMax = log2(LONG_MAX) 	= 63 
ULONG_MAX 	=      18446744073709551615 ; lMax = log2(ULONG_MAX) 	= 64 
LLONG_MAX 	=       9223372036854775807 ; lMax = log2(LLONG_MAX) 	= 63 
ULLONG_MAX 	=      18446744073709551615 ; lMax = log2(ULLONG_MAX) 	= 64

format

edytuj

Format wydruku zdefiniowany osobno:[4]

const char format_str[] = "name: %s\targs: %s\tvalue %d\tarraysize %d\n";
...
printf(format_str, 
       sp->name, 
       sp->args, 
       sp->value, 
       sp->arraysize);


zmienny format

edytuj
// Akshay Hegde 
// https://www.unix.com/programming/244285-passing-printf-formatting-parameters-variables.html
 
#include <stdio.h>

/* Following header files are not needed stdio.h is enough 
 #include <stdlib.h>
 #include <string.h> */

#define red     "\x1b[31m"
#define green   "\x1b[32m"
#define yell    "\x1b[33m"
#define blue    "\x1b[34m"
#define magneta "\x1b[35m"
#define cyan    "\x1b[36m"
#define reset   "\x1b[0m"

int main () {
	
        // Your value to be printed
	float testvalue = 125.25; 
      
        // precision
	int prec = 5;  
       
        // number of digits after decimal            
	int dig  = 3;   
       
        // 'f' for float           
	char c = 'f';   

        // this holds your format string...("%s...%f") etc          
	char format[10];  

        // Here we are creating your fmt string         
	sprintf(format, "%%s%%%d.%d%c%%s\n", prec, dig,c);
  	
        // Finally printing with different colors
	printf(format,red,testvalue,reset);
	printf(format,blue,testvalue,reset);
  	
}

Liczba cyfr

edytuj
/* 
https://www.digitalmars.com/ctg/ctgNumerics.html
gcc f.c -lm -Wall
./a.out
*/


#include <stdio.h>
#include <float.h>
#define pi 3.14159265358979323846264338327950288419716939937510582097494459230781 // https://pl.wikipedia.org/wiki/Pi

int main()
{
  
  long double l;
  double d;
  float f;


  l = (long double) pi;
  printf("(long double) \tpi = %.*Lg\n", LDBL_DIG, l);

  d = (double)pi;
  printf("(double) \tpi = %.*g\n", DBL_DIG, d);

  f = (float) pi;
  printf("(float) \tpi = %.*g\n", FLT_DIG, f);

  return 0;
}

Wynik:

(long double) 	pi = 3.14159265358979312
(double) 	pi = 3.14159265358979
(float) 	pi = 3.14159

fragment łańcucha ( ang. substring)

edytuj

Jak wydrukować fragment łańcucha [5]

   printf("%.*s\n", str_len, str);

Funkcje snprintf i vsnprintf nie były zdefiniowane w standardzie C89. Zostały one dodane dopiero w standardzie C99.

Biblioteka glibc do wersji 2.0.6 włącznie posiadała implementacje funkcji snprintf oraz vsnprintf, które były niezgodne ze standardem, gdyż zwracały -1 w przypadku, gdy wynikowy tekst nie mieścił się w podanej tablicy znaków.

Rozszerzenia GNU

edytuj

asprintf = allocating string print formatted


/*

Using asprintf instead of sprintf or snprintf by james : 
"To use this function you also need to define _GNU_SOURCE on the gcc command line (of in the program with a define before the includes)"
http://www.stev.org/post/2012/02/10/Using-saprintf-instead-of-sprintf-or-snprintf.aspx

http://ubuntuforums.org/showthread.php?t=279801

gcc a.c -D_GNU_SOURCE -Wall // without #define _GNU_SOURCE

gcc a.c -Wall 

*/


#define _GNU_SOURCE // asprintf
#include <stdio.h>
#include <stdlib.h>
 
int main() {
    char *tmp = 0;
 

    // 
    if (asprintf(&tmp, "%s %s %s", "Hello", "world", "here I am !") < 0) {
        perror("asprintf");
        exit(EXIT_FAILURE);
    }
 
    printf("%s\n", tmp);
 
    free(tmp);
 
    return 0;
}

Kiedy chcemy kilka zapisać do tej samej zmiennej to używamy jej jednocześnie jako wyjścia i wejścia:

asprintf(&tmp, "%s %f,%f ", tmp, zx , -zy );

Problemy

edytuj

nie ma wyniku na ekranie

edytuj
  • przyczyna : przekierowanie stdout
  • brak \n
  • błąd w programie[6]

Rozwiązanie:

  • restart konsoli ( przywraca standardowe ustawienia)
  • dodanie \n lub procedury fflush(stdout);.
  • usunięcie błędu z programu

stack smashing detected ***: terminated

edytuj

Użycie łancucha dłuższego niż bufor powoduje przepełnienie stosu (ang. stack smashing, stack buffer overflow)

    
#include <stdio.h>
#include <stdlib.h>		// malloc
#include <string.h>		// strcat

int main(void){

	char buf[5]; 
	sprintf (buf, "%s",  "123456789"); // 
	return 0;
}

Kompilator przy kompilacji pokazuje ostrzeżenie:

 gcc j.c

wynik:

s.c: In function ‘main’:
s.c:9:33: warning: ‘%s’ directive writing 9 bytes into a region of size 5 [-Wformat-overflow=]
   9 |         sprintf (long_comment, "%s",  "123456789"); // f_description is global var
     |                                 ^~    19:21, 23 lut 2023 (CET)19:21, 23 lut 2023 (CET)~
s.c:9:9: note: ‘sprintf’ output 10 bytes into a destination of size 5
   9 |         sprintf (long_comment, "%s",  "123456789"); // f_description is global var
     |         ^19:21, 23 lut 2023 (CET)19:21, 23 lut 2023 (CET)19:21, 23 lut 2023 (CET)19:21, 23 lut 2023 (CET)19:21, 23 lut 2023 (CET)19:21, 23 lut 2023 (CET)19:21, 23 lut 2023 (CET)19:21, 23 lut 2023 (CET)~

Jeśli pomimo to uruchominy program:

./a.out
*** stack smashing detected ***: terminated
Aborted

Zobacz też

edytuj
scanf
Podstawowe procedury wejścia i wyjścia
Znaki specjalne
Liczba cyfr dziesiętnych ( LDBL_DIG, DBL_DIG ) zdefiniowane w float.h

Przypisy