What is the use of "snprintf" with more than 4 parameters? - c

I have something in my code as below:
This is code for creating a DEVICE in linux under /dev
#define PRINTER_STR "printer_"
char str[32];
snprintf(str, sizeof(str), PRINTER_STR "%s%s", dev->type, "%u");
device_create(mycan_drv.class, parent,
MKDEV(dev->nMajor, dev->nMinor),
dev, str, dev->nMinor);
4th parameter to snprintf which is dev->type is assigned with strings like epson,hp,canon.
Output achieved is something like this :
printer_epson32,printer_hp33,printer_canon34
In the above output strings, I couldnt understand how the numbers like 32,33,34 are built.
I can understand this is because of the 5th parameter "%u" passed to snprintf. But how ?
All the references i got are with max 3 or 4 parameters of snprintf.
Kindly help.

char str[32];
dev->type = "epson";
snprintf(str, sizeof(str), "printer_" "%s%s", dev->type, "%u");
results in:
str = "printer_epson%u".
Then code does:
device_create(..., str, dev->nMinor);
which is really:
device_create(..., "printer_epson%u", dev->nMinor);
and then inside device_create a *printf like function is once again called and it writes dev->nMinor in place of %u. So, like, it's not snprintf that writes the number, the number is written inside device_create. snprintf is used to create the formatting string for device_create and device_create writes that number.
Side note: The "%s%s", dev->type, "%u") looks strange, it could have been just "%s%%u", dev->type);. And anyway for all that it could have just been device_create(...., "%s%u", dev->type, dev->nMinor).

Simpler would be:
snprintf(str, sizeof(str), PRINTER_STR "%s%%u", dev->type);
Since %u is a fixed string, it could be just included in the format. The % needs to be escaped to avoid being interpreted by snprintf.

Related

sprintf vs. wsprintf in VS2017 with "%.*s"

While trying to output some counted strings, I encountered the following asymmetry between the Microsoft's sprintf() and wsprintf() functions:
#define _UNICODE
sprintf(buff, "%.3s", "abcdef"); //Outputs: "abc"
sprintf(buff, "%.*s", 3, "abcdef"); //Outputs: "abc"
wsprintf(buff, L"%.3s", L"abcdef"); //Outputs: L"abc"
wsprintf(buff, L"%.*s", 3, L"abcdef"); //Outputs: L"*s"
Note that the last wsprintf() does not output L"abc" like its narrow sister function sprintf() with the same (but wide) arguments.
Q: Is this a bug or feature?
Note: This is similar to the issue described here:
Formatting differences between sprintf() and wsprintf() in VS2015
wsprintf is old, very old. It does not document * in precision, so don't pass that format string to wsprintf. Your test is technically unspecified.
Please note that wsprintf will not write more than 1023 characters to buff followed by the null character, and in UCS-2 rather than UTF-16. The design of this function is you pass it a fixed size stack buffer of 1024 and don't worry about buffer overflow since it truncates for you.
As far as I can tell for its intention it's more intended for making debug messages to pass to MessageBox rather than for actual application use. It's a much-reduced form of snprintf with a fixed n that's implemented independently of the other standard libraries.
Ok so you want a swprintf that always null terminates. Try this:
int swprintf2(wchar_t *ws, size_t len, const wchar_t* format, ...)
{
va_arg arg;
va_start(arg, format);
ws[len - 1] = 0;
return vswprintf(ws, len - 1, format, arg);
}

Changing char* string in a C loop

I am trying to change a string within a loop to be able to save my images with a changing variable. Code snippet is as follows:
for (frames=1; frames<=10; frames++)
{
char* Filename = "NEWIMAGE";
int Save_Img = is_SaveImageMemEx (hCam, Filename, pMem, memID,
IS_IMG_PNG, 100);
printf("Status Save %d\n",Save_Img);
}
What I want to do is put a variable that changes with the loop counter inside Filename so my saved file changes name with every iteration.
Any help would be great.
Create a file name string with sprintf and use the %d format conversion specifier for an int:
char filename[32];
sprintf(filename, "NEWIMAGE-%d", frames);
sprintf works just like printf, but "prints" to a string instead of stdout.
If you declared frames as an unsigned int, use %u. If it is a size_t use %zu. For details see your friendly printf manual page, which will tell you how you can for example zero pad the number.
Be sure that the character array you write to is large enough to hold the longest output plus an extra '\0' character. In your particular case NEWIMAGE-10 + 1 means 11 + 1 = 12 characters is enough, but 32 is future-proof for some time.
If you want to program like a pro, look at the snprintf and asnprintf functions, which can limit or allocate the memory written to, respectively.
You can use sprintf to create a formatting string:
char Filename[50];
sprintf(Filename, "NEWIMAGE%d", frames);

**argv contain a lot of characters than expected

First, I need to execute two commands with system(), for example, I receive an string and open this string with an text editor, like this:
$ ./myprogram string1
And the output should be a command like this:
$ vim string1
But, I cannot find a way to do this like this pseudo code:
system("vim %s",argv[1]); //Error:
test.c:23:3: error: too many arguments to function 'system'
system("vim %s",argv[1]);
Therefore, my solution is store the argv[1] on a char array that already initialized with four characters, like this:
char command[strlen(argv[1])+4];
command[0] = 'v'; command [1] = 'i'; command[2] = 'm'; command[3] = ' ';
And assign the argv[1] to my new char array:
for(int i = 0; i < strlen(argv[1]) ; i++)
command[i+4] = argv[1][i];
And finally:
system(command);
But, if the arguments given to my program has less than 3 characters, its works fine, but if not, some weird characters that I do not expect appear in the output, like this:
./myprogramg 1234
And the output is:
$ vim 12348�M�
How can I solve this bug and why does this happen?
The full code is:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc,char **argv) {
char command[strlen(argv[1])+4];
command[0] = 'v'; command [1] = 'i'; command[2] = 'm'; command[3] = ' ';
for(int i = 0; i < strlen(argv[1]) ; i++)
command[i+4] = argv[1][i];
system(command);
return 0;
}
You need to NUL terminate your C-style strings, and that includes allocating enough memory to hold the NUL.
Your array is a byte short (must be char command[strlen(argv[1])+4+1]; to leave space for NUL), and you should probably just use something like sprintf to fill it in, e.g.:
sprintf(command, "vim %s", argv[1]);`
That's simpler than manual loops, and it also fills in the NUL for you.
The garbage you see is caused by the search for the NUL byte (which terminates the string) wandering off into unrelated (and undefined for that matter) memory that happens to occur after the buffer.
The reason you're running into problems is that you aren't terminating your command string with NULL. But you really want to use sprintf (or even better to use snprintf) for something like this. It works similarly to printf but outputs to memory instead of stdout and handles the terminating NULL for you. E.g:
char cmd[80];
snprintf(cmd, 80, "vim %s", argv[1])
system(cmd);
As #VTT points out, this simplified code assumes that the value in argv[1] will be less than 75 characters (80 minus 4 for "vim " minus 1 for the NULL character). One safer option would be to verify this assumption first and throw an error if it isn't the case. To be more flexible you could dynamically allocate the cmd buffer:
char *cmd = "vim ";
char *buf = malloc(strlen(argv[1]) + strlen(cmd) + 1);
sprintf(buf, "%s%s", cmd, argv[1]);
system(buf);
free(buf);
Of course you should also check to be sure argc > 1.
I know that there are already good answers here, but I'd like to expand them a little bit.
I often see this kind of code
system("vim %s",argv[1]); //Error:
and beginners often wonder, why that is not working.
The reason for that is that "%s", some_string is not a feature of the C
language, the sequence of characters %s has no special meaning, in fact it is
as meaningful as the sequence mickey mouse.
The reason why that works with printf (and the other members of the
printf-family) is because printf was designed to replace sequences like
%s with a value passed as an argument. It's printf which make %s special,
not the C language.
As you may have noticed, doing "hallo" + " world" doesn't do string
concatenation. C doesn't have a native string type that behaves like C++'s
std::string or Python's String. In C a string is just a sequence of
characters that happen to have a byte with value of 0 at the end (also called
the '\0'-terminating byte).
That's why you pass to printf a format as the first argument. It tells
printf that it should print character by character unless it finds a %,
which tells printf that the next character(s)1 is/are special and
must substitute them with the value passed as subsequent arguments to printf.
The %x are called conversion specifiers and the documentation of printf
will list all of them and how to use them.
Other functions like the scanf family use a similar strategy, but that doesn't
mean that all functions in C that expect strings, will work the same way. In
fact the vast majority of C functions that expect strings, do not work in that
way.
man system
#include <stdlib.h>
int system(const char *command);
Here you see that system is a function that expects one argument only.
That's why your compiler complains with a line like this: system("vim %s",argv[1]);.
That's where functions like sprintf or snprintf come in handy.
1If you take a look at the printf documentation you will see that
the conversion specifier together with length modifiers can be longer than 1
character.

how can I printf in c

How can I make this print properly without using two printf calls?
char* second = "Second%d";
printf("First%d"second,1,2);
The code you showed us is syntactically invalid, but I presume you want to do something that has the same effect as:
printf("First%dSecond%d", 1, 2);
As you know, the first argument to printf is the format string. It doesn't have to be a literal; you can build it any way you like.
Here's an example:
#include <stdio.h>
#include <string.h>
int main(void)
{
char *second = "Second%d";
char format[100];
strcpy(format, "First%d");
strcat(format, second);
printf(format, 1, 2);
putchar('\n');
return 0;
}
Some notes:
I've added a newline after the output. Output text should (almost) always be terminated by a newline.
I've set an arbitrary size of 100 bytes for the format string. More generally, you could declare
char *format;
and initialize it with a call to malloc(), allocating the size you actually need (and checking that malloc() didn't signal failure by returning a null pointer); you'd then want to call free(format); after you're done with it.
As templatetypedef says in a comment, this kind of thing can be potentially dangerous if the format string comes from an uncontrolled source.
(Or you could just call printf twice; it's not that much more expensive than calling it once.)
Use the preprocessor to concatenate the two strings.
#define second "Second%d"
printf("First%d"second,1,2);
Do not do this in a real program.
char *second = "Second %d";
char *first = "First %d";
char largebuffer[256];
strcpy (largebuffer, first);
strcat (largebuffer, second);
printf (largebuffer, 1, 2);
The problem with using generated formats such as the method above is that the printf() function, since it is a variable length argument list, has no way of knowing the number of arguments provided. What it does is to use the format string provided and using the types as described in the format string it will then pick that number and types of arguments from the argument list.
If you provide the correct number of arguments like in the example above in which there are two %d formats and there are two integers provided to be printed in those places, everything is fine. However what if you do something like the following:
char *second = "Second %s";
char *first = "First %d";
char largebuffer[256];
strcpy (largebuffer, first);
strcat (largebuffer, second);
printf (largebuffer, 1);
In this example the printf() function is expecting the format string as well as a variable number of arguments. The format string says that there will be two additional arguments, an integer and a zero terminated character string. However only one additional argument is provided so the printf() function will just use what ever is next on the stack as being a pointer to a zero terminated character string.
If you are lucky, the data that the printf() function interprets as a pointer will a valid memory address for your application and the memory area pointed to will be a couple of characters terminated by a zero. If you are less lucky the pointer will be zero or garbage and you will get an access violation right then and it will be easy to find the cause of the application crash. If you have no luck at all, the pointer will be good enough that it will point to a valid address that is about 2K of characters and the result is that printf() will totally mess up your stack and go into the weeds and the resulting crash data will be pretty useless.
char *second = "Second%d";
char tmp[256];
memset(tmp, 0, 256);
sprintf(tmp, second, 2);
printf("First%d%s", 1,tmp);
Or something like that
I'm assuming you want the output:
First 1 Second 2
To do this we need to understand printf's functionality a little better. The real reason that printf is so useful is that it not only prints strings, but also formats variables for you. Depending on how you want your variable formatted you need to use different formatting characters. %d tells printf to format the variable as a signed integer, which you already know. However, there are other formats, such as %f for floats and doubles, %l% for long integers, and %s for strings, or char*.
Using the %s formatting character to print your char* variable, second, our code looks like this:
char* second = "Second";
printf ( " First %d %s %d ", 1, second, 2 );
This tells printf that you want the first variable formatted as an integer, the second as a string, and the third as another integer.

String format works in fprintf but doesn't work in sprintf, gives segmentation fault

fprintf(fp,"IP: %d: %.*s\n",
ip,
strstr(strstr(p->data, "User-Agent:"),"\n") - strstr(p->data, "User-Agent:"),
strstr(p->data, "User-Agent: ") );
fclose(fp);
Hi All, as you can see, in the above statement, I am trying to write off just the User Agent header from a char pointer which contains the entire http packet data. The thing is, After fiddling with the string format, I came up with this %.*s format which lets me, dynamically select the number of characters to be printed to the file, and then prints them. What the code is basically doing is, first, it's printing an int, then the number of chars from the occurrence of "User-Agent:" to the very next occurrence new line character is passed, and that amount of chars are then passes starting at where the "User-Agent:" starts, from the entire packet data string. I know it's all pretty messy, but it's working fine. Except that it's not working in sprintf.
Please save all my hard word! Any help is appreciated!
char *stat;
sprintf(stat,"%.*s\0",
strstr(strstr(p->data, "User-Agent:"),"\n") - strstr(p->data, "User-Agent:"),
strstr(p->data, "User-Agent: ")) ;
You are not allocating memory for stat. Try
char *stat = malloc(MAXLEN);
snprintf(stat, MAXLEN, ...);
^ ^
When you use sprintf, you need an array of characters to write into. You're writing to an uninitialized pointer.
Try this instead:
char stat[200];
sprintf(stat, etc...
Well, you are trying to write the data into uninitialized unallocated random memory location. Now that can't possibly work.
Either do:
char stat[SUFFICIENTLY_LARGE_NUMBER];
snprintf(stat, SUFFICIENTLY_LARGE_NUMBER, ...);
or:
char *stat = malloc(SUFFICIENTLY_LARGE_NUMBER);
snprintf(stat, SUFFICIENTLY_LARGE_NUMBER, ...);
and make sure "SUFFICIENTLY_LARGE_NUMBER" is enough bytes that the string fits in and not unnecessarily huge.
PS: snprintf, because your format does not include length limits. If it does, sprintf is OK, but never ever use sprintf with unlimited %s. Your %.*s, while formally limited, is not enough, because the expression will happily return more than the size of the allocated buffer, so you need another check to avoid overruning it.

Resources