Based on the C language sprintf function in depth understanding

  • 2020-04-01 23:45:00
  • OfStack

Printf is probably the second function that many programmers have been exposed to since they started learning C (I guess the first is main). Do you know much about its twin, sprintf? The power of sprintf rarely lets you down when it comes to structuring data from various classes into strings.
Since sprintf is almost identical in usage to printf, except that it is printed to a different destination, the former to a string and the latter to the command line. This also makes sprintf much more useful than printf. So this article focuses on sprintf, sometimes interspersed with the use of pritnf.
Sprintf is a variable parameter function, defined as follows:
Int sprintf(char *buffer, const char *format [, argument]... );
In addition to the first two parameter types fixed, can be followed by any number of parameters. The essence, of course, is in the second parameter: the formatted string.
Printf and sprintf USES formatted string string format to refer to, in the format string used within some starting with "%" format specifier (the format specifications) to occupy a position in the back and provide the corresponding variables in the list, the final function with the corresponding position of the variable to replace the specifiers, produce a caller to a string.
Format a string of Numbers
One of the most common USES of sprintf is to print integers into strings, so spritnf can replace itoa in most cases. Such as:
// print the integer 123 as a string and save it in s.
Sprintf (s, "% d", 123);     / / create a "123"
You can specify the width, not enough space on the left:
Sprintf (s, 8 8 "% d % d", 123, 4567); // produce:"       123       4567"
It can also be left aligned:
Sprintf (s, "% - 8 d % d", 123, 4567); // produced: "123                 4567"
It can also be printed in hexadecimal:
Sprintf (s, "% x 8", 4567); // lower case hexadecimal, 8 widths, right aligned
Sprintf (s, "% - 8 x", 4568); // capital hexadecimal, width takes 8 positions, left aligned
This makes it easy to get a hexadecimal string for an integer, but when we print hexadecimal content, we usually want an equal width format with zeros on the left, so what do we do? Simply put a 0 in front of the width number.
Sprintf (s, x % 08, 4567); // generate: "000011D7"
The hexadecimal printing above in "%d" can also be done in this way.
Here's a sign extension: for example, if we want to print the memory hexadecimal representation of the short integer (short) -1, on the Win32 platform, a short takes 2 bytes, so we naturally want to print it with 4 hexadecimal digits:
Short si = 1;
Sprintf (s, "% 4 x", si);
"FFFFFFFF". What's going on? Because spritnf is a variable and function, in addition to the front two parameters, the parameters are not type safe behind, function more there is no way through a "% X" can only learn that when the parameters of pressure before the function call stack is pressed in a 4-byte integer or a short integer 2 bytes, so take the unified handling of 4 bytes, lead to arguments stack when did sign extension, expand a 32-bit integer 1, printing was not enough in the 4 positions, the 32-bit integer 1 8 hexadecimal are printed out. If you want to see si for what it is, you should ask the compiler to do a 0 extension instead of a sign extension:
Sprintf (s, "% 4 x", (unsigned short) si);
That's it. Or:
Unsigned short si = -1;
Sprintf (s, "% 4 x", si);
Sprintf and printf can also print integer strings in base 8, using "%o". Notice that both hexadecimal and hexadecimal don't print negative Numbers, they're both unsigned, which is actually a direct hexadecimal or hexadecimal representation of the internal encoding of the variable.
2. Control the format of floating point number printing
The printing and format control of floating point Numbers is another common function of sprintf. Floating point Numbers are controlled by the format character "%f", and 6 decimal places are reserved by default, such as:
Sprintf (s, "% f", 3.1415926);       / / create a "3.141593"
But sometimes we want to control the width of the print and the number of decimal places, so we should use the format "%m.nf", where m is the width of the print and n is the number of decimal places. Such as:
Sprintf (s, "% f" 10.3, 3.1415626);     // produce:"         3.142"
Sprintf (s, "% 10.3 f", 3.1415626); // produced: "3.142         "
Sprintf (s, "%, 3 f, 3.1415626); // does not specify the total width, resulting in: "3.142"
Notice a problem, you guess
Int I = 100;
Sprintf (s, "%. 2 f", I);
What's going to come out? "100.00"? Isn't it? Just try it yourself, but also try this:
Sprintf (s, "%. 2 f", (double) I);
The first one is definitely not the correct result because, as mentioned earlier, the caller does not know that the format control corresponding to I is "%f". The function itself did not know that an integer was being pushed on the stack, so the poor 4 bytes that held the integer I were automatically forced to be interpreted as a floating point.
However, if someone is interested in coding a floating point number by hand, you can use this method to verify that your hand-choreographed results are correct. J
Character /Ascii contrast
As we know, in C/C++, char is also a normal scalable type, which is not fundamentally different from short, int, and long except for the word length, but is used to represent characters and strings. (perhaps it would have been more appropriate to call this type "byte" and now use byte or short to typedef a char, depending on the situation.)
Then, print a character with "%d" or "%x" to get its ASCII code in decimal or hexadecimal; Conversely, print an integer with "%c" to see the ASCII character for which it corresponds. The following program segment prints the ASCII code comparison table of all visible characters to the screen (printf is used here, note that the prefix "0X" is automatically added to hexadecimal Numbers when "#" and "%X" are used together) :
For (int I = 32; i. < 127; I++) {
Printf (" [%c]: %3d 0x%#04X/n ", I, I, I);
}
3. Concatenate strings
Sprintf's format control string, where you can insert various things and eventually "string them together," is a natural alternative to strcat in many cases by concatenating strings, but sprintf is flexible enough to concatenate multiple strings at once (and insert something else in between, naturally). Such as:
Char * who = "I";
Char * disappearance = "CSDN";
Sprintf (s, "%s love %s.", who, whom); // generated: "I love CSDN."
Strcat can only connect strings (an array of characters ending in '/0' or called a character buffer, null-terminated string), but sometimes we have two character buffers that don't end in '/0'. For example, many arrays of characters returned from third-party library functions, or streams of characters read in from hardware or network transfers, do not necessarily end each sequence of characters with a corresponding '/0' at the end. What if a direct connection, whether sprintf or strcat, definitely results in illegal memory operations, and strncat requires at least the first parameter to be null-terminated string? We naturally recall that you can specify widths when printing integers and floating point Numbers, as well as strings. Such as:
Char a1[] = {'A', 'B',' C', 'D',' E', 'F',' G'};
Char a2[] = {'H', 'I',' J', 'K',' L', 'M',' N'};
If:
Sprintf (s, "% s % s", a1, a2); / / Don 't do that!
Nine times out of ten things will go wrong. Can it be changed to:
Sprintf (s, seven "% s % 7 s, a1, a2);
Not much better. The correct answer is:
Sprintf (s, "%. 7 s %. 7 s", a1, a2); // produced: "ABCDEFGHIJKLMN"
This is analogous to "%m.nf" for floating point Numbers, in which "%m.ns", m represents the occupancy width (fill in Spaces if the string length is insufficient, and print the actual width if it is larger), and n represents the maximum number of characters to be taken from the corresponding string. Usually m is not very useful when printing strings, but n after the dot sign is used more. Naturally, you can also take only partial characters before and after:
Sprintf (s, "%. 6 s %. 5 s", a1, a2); // generated: "ABCDEFHIJKL"
In many cases, we may also want these format control character used to specify the length information of digital is dynamic, not static, because a lot of time, the program will run to exactly need to take a few characters in a character array, the dynamic precision of width/set function has also been considered in the realization of the sprintf, sprintf "*" is used to take up a originally need a specified width or constant digital position accuracy, also, the actual width or accuracy can is provided and other variables are printed out, so the above examples can be turned into:
Sprintf (s, "%.*s%.*s", 7, a1, 7, a2);
Or:
Sprintf (s, "%. * s %. * s", sizeof (a1), a1, sizeof (a2), a2);
In fact, the previously described print characters, integers, floating point Numbers, and so on can dynamically specify those constant values, such as:
Sprintf (s, "% - * d", 4, 'A'); // produces "65"
Sprintf (s, "% # 0 * X", 8, 128);       // produces "0X000080" and "#" produces 0X
Sprintf (s, "%*.*f", 10, 2, 3.1415926); / /"           3.14"
Print the address
Sometimes when debugging a program, we might want to look at the address of some variable or member. Since the address or pointer is only a 32-bit number, you can print them out with the "%u" of an unsigned integer:
Sprintf (s, "% u", & I);
But usually people prefer to use hexadecimal instead of hexadecimal to display an address:
Sprintf (s, x % 08, & I);
However, these are indirect methods. For address printing, sprintf provides a special "%p" :
Sprintf (s, "% p", & I);
I think it's actually equivalent to:
Sprintf (s, '%0*x', 2 * sizeof(void *), & I);
5. Use the return value of sprintf
Less noticed, but sometimes useful, is the return value of the printf/sprintf function, which returns the number of characters that the function call ends up printing into the character buffer. That is, every time a sprinf call ends, you don't have to call strlen again to know the length of the resulting string. Such as:
Int len = sprintf(s, "%d", I);
For a positive integer, len is equal to the decimal number of the integer I.
The following is a complete example of generating random Numbers between 10 [0, 100] and printing them into a character array, s, separated by commas.

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
int main() {
srand(time(0));
char s[64];
int offset = 0;
for(int i = 0; i < 10; i++) {
offset += sprintf(s + offset,  " %d, " , rand() % 100);
}
s[offset - 1] =  ' /n';//Replace the last comma with a newline character.
printf(s);
return 0;
}

Imagine when you remove a record from the database, and then hope to connect their different fields according to certain rules into a string, you can use this method, in theory, he should be higher than constant strcat efficiency, because strcat each call to all need to find the last '/ 0' position, and in the example given above, we use sprintf every time the location directly down the return value.
6. Frequently asked questions about using sprintf
Sprintf is a variable parameter function, when the use of the problem is often, and as long as the problem is usually caused by the program crash memory access error, but fortunately, although the misuse of sprintf caused by the problem is serious, but it is easy to find, there are just a few cases, usually with the eyes to see the wrong code more than a few eyes to see.
?? Buffer overflow
The length of the first parameter is too short, so let's make it bigger. Of course, it may also be the problem of the following parameters, it is recommended that the variable parameter correspondence must be careful, and when printing a string, try to use the form of "%. Ns" to specify the maximum number of characters.
?? I forgot the first parameter
I can't think of a more low-level problem than printf is used to. // I often do. :. (
?? Variable parameters correspond to problems
It is usually forgotten to provide the parameter corresponding to a certain format, resulting in the subsequent parameters are all misplaced, check it. Are the parameters corresponding to "*" in particular provided? Don't match an integer to a "%s". The compiler will think you're cheating her too much.
7. Strftime
Sprintf also has a nice cousin: strftime, which is used to format time strings, much like her cousin, and a bunch of formatting controls, except that the little girl is careful that she also requires the caller to specify the maximum length of the buffer, presumably to pass the blame when something goes wrong. Here's an example:

time_t t = time(0);
//Generates a string in the format "yyyy-mm-dd hh: MM :ss".
char s[32];
strftime(s, sizeof(s),  " %Y-%m-%d %H:%M:%S " , localtime(&t));

Sprintf can also find his friends in MFC: CString: : Format, strftime in MFC also has her peers: CTime: : Format, this pair is more elegant to write code due to the sponsorship from object orientation.
8. Afterword.
This article introduces all these functions, in MSDN can be easily found, the author just according to their own use experience, combined with some examples, some commonly used, useful, and may be a lot of beginners do not know the use of the introduction of a point, I hope you do not joke, also hope you criticism.
Some people think that this kind of function with variable parameters can cause all kinds of problems, so the use of it is not recommended. But I often still can not resist the temptation of their powerful function, in the actual work has been in use. In fact, C#.NET has supported variable parameters since its inception, as has the recently released Java5.0.
Void GetSystemTime(LPSYSTEMTIME LPSYSTEMTIME); Here are some examples:

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
void main() {
SYSTEMTIME st; //Defines a structure for storing time
char strTime[256];
int n=0;
GetSystemTime(&st);
n = sprintf(strTime, " Year:/t%d/n " ,st.wYear);
n += sprintf(strTime+n, " Month:/t%d/n " ,st.wMonth);
n += sprintf(strTime+n, " Day:/t%d/n " ,st.wDay);
n += sprintf(strTime+n, " Date:/t%d/n " ,st.wDayOfWeek);
n += sprintf(strTime+n, " Hour:/t%d/n " ,st.wHour);
n += sprintf(strTime+n, " Minute:/t%d/n " ,st.wMinute);
n += sprintf(strTime+n, " Second:/t%d/n " ,st.wSecond);
n += sprintf(strTime+n, " MilliSecond:/t%d/n " ,st.wMilliseconds);
printf( " %s " ,strTime);
system( " pause " );
}


Related articles: