I have two strings that may be changing independently. I thought to make them #defines so I don't have to change it all over my code. I'm having problems putting them together with sprintf().
So, below, if VoltageSet was 2.2, I want to put the string "sour:volt:2.2\r" into the cmd buffer.
(FYI- SOUR:VOLT:2.2 is a SCPI command to an instrument, and \r is a carriage return to terminate the message)
In my header:
#define SETVOLTAGELEVEL "SOUR:VOLT:"
#define TERMINATIONCHAR "\r"
In my c code:
int SetVoltageLevel (double VoltageSet)
{
char cmd[255]={0};
sprintf(cmd, "SETVOLTAGELEVEL%fTERMINATIONCHAR", VoltageSet);
}
Am I over thinking this? Is there an easier way?
sprintf(cmd, SETVOLTAGELEVEL "%f" TERMINATIONCHAR, VoltageSet);
or
sprintf(cmd, "%s%f%s", SETVOLTAGELEVEL, VoltageSet, TERMINATIONCHAR);
I don't quite get what you want to achieve this with this behavior (An XY problem?), but you can concatenate string literals by writing them one after another:
sprintf(cmd, SETVOLTAGELEVEL "%f" TERMINATIONCHAR, VoltageSet);
But...
Why?
Very risky. You not only fail to use snprintf() instead of sprintf(), but you may also run into format string errors in case the two macros define more or less format specifiers than you actually supply arguments to the function.
There is nothing wrong here. Preprocessor i.e. define will be replaced by its value before compilation. So you wont see any difference.
#BLUEPIXY has pointed out one way. Here is something closer to what you had in mind. Note that what you have does not work because the preprocessor does not expand tokens inside a string. But you can use string concatenation. Good to know.
int SetVoltageLevel (double VoltageSet)
{
char cmd[255]={0};
sprintf(cmd, SETVOLTAGELEVEL "%f" TERMINATIONCHAR, VoltageSet);
}
Related
I want to implement a wrapper function for C sscanf without using vsscanf, because in my environment vsscanf() is not there only sscanf is there. I don't want to do a complete implementation of sscanf also because for that I need to consider all possible scenarios. I have seen some samples in google, but it has not considered all scenarios.
So now I want to implement like below:
int my_sscanf(char * buf, char format[], ...)
{
va_list vargs = {0};
va_start(vargs, format);
//some loop to get the variable aguments
//and call again sscanf() here.
va_end (vargs);
}
Ouch! Here's a hammer; it'll be more fun hitting yourself on the head with it. Seriously, that's a non-trivial proposition.
You'll need a loop that scans through the format string, reading characters from the buffer when they're normal characters, remembering that spaces in the format chew up zero or more spaces in the buffer. When you encounter a conversion specification, you'll need to create a singleton format string containing the user-supplied conversion specification plus a %n conversion specification. You'll invoke:
int pos;
int rc = sscanf(current_pos_in_buf, manufactured_format_with_percent_n,
appropriate_pointer_from_varargs, &pos);
If rc is not 1, you'll fail. Otherwise, you update the current position in the buffer using the value stored in pos, and then repeat. Note that scanning a conversion specification is not trivial. Also, if there is an assignment-suppressing * in the specification, you'll have to expect a 0 back from sscanf() (and not provide the appropriate pointer from the variable args).
Try telling your compiler to compile your code as C99. If that still doesn't work, your libc does not comply with the C99 standard – in that case, get a proper libc.
E.g. if you're using gcc, try adding -std=c99 to the compiler command line.
There's a slightly simpler way to do this using the preprocessor, but it's a little hacky. Take this as an example:
#define my_sscanf(buf, fmt, ...) { \
do_something(); \
sscanf((buf), (fmt), __VA_ARGS__); \
do_something_else(); }
i am learning now c and i come up with this example, where i can print a text using pointers.
#include <stdio.h>
main ()
{
char *quotes = "One good thing about music, when it hits you, you feel no pain. \"Bob Marley\"\n";
printf(quotes);
}
I get a warning from the compiler "format not a string literal and no format arguments" and when I execute the program it runs successfully.I read some other questions here that they had the same warning from the compiler but I didn't find an answer that fits me. I understood the reason why i get this message:
This warning is gcc's way of telling you that it cannot verify the format string argument to the printf style function (printf, fprintf... etc). This warning is generated when the compiler can't manually peek into the string and ensure that everything will go as you intend during runtime...
Case 3. Now this is somewhat your case. You are taking a string generated at runtime and trying to print it. The warning you are getting is the compiler warning you that there could be a format specifier in the string. Say for eg "bad%sdata". In this case, the runtime will try to access a non-existent argument to match the %s. Even worse, this could be a user trying to exploit your program (causing it to read data that is not safe to read).
(See the answer)
but what i have to add in my case to in order to have not warnings from the compiler?
Change it to printf("%s", quotes); which adds the specifier that quotes is a 'string', or array of char.
You need to tell printf what is it that you are printing. %s descriptor will tell printf that you are printing a string.
Format of printf = ("descriptor of what type of data you are printing",variable holding the data);
descriptor for strings is %s, for characters %c, for int %d
Change printf to:
printf("%s",quotes);
You have to specify format string - in simplest form:
char *quotes = "One good thing about music(...)\n";
printf("%s", quotes);
or, you can use format string to decorate output:
char *quotes = "One good thing about music(...)"; // no newline
printf("%s\n", quotes); // newline added here
or, if you don't want to mess with format strings:
char *quotes = "One good thing about music(...)"; // no newline
puts(quotes); // puts() adds newline
or
char *quotes = "One good thing about music(...)\n";
fputs(quotes,stdout);
This warning is gcc's way of telling you that it cannot verify the format string argument to the printf style function (printf, fprintf... etc). This warning is generated when the compiler can't manually peek into the string and ensure that everything will go as you intend during runtime. Lets look at a couple of examples.
So as other suggested explicitly use format specifier to tell the compiler...i.e.
printf("%s",quotes);
You are getting the warning because it is dangerous when the string you are printing contains '%'. In this line it makes no sense for percents but when you want to print this for instance:
int main ()
{
int percent = 10;
char *s = "%discount: %d\n";
printf(s, percent);
return 0;
}
your program will likely crash when printf encounters the second percent and it tries to pop a value from the stack from printf.
When you want to print a percent sign use: "%%discount:"
Try this:
#include <stdio.h>
main ()
{
char *quotes = "One good thing about music, when it hits you, you feel no pain. \"Bob Marley\"\n";
puts(quotes); //Either
printf("%s",quotes);//or
return 0;
}
I'm writing a program for a school project that is supposed to emulate the Unix shell, in a very basic form. It's basically parsing input, then doing a fork/exec. I need to be able to read arguments in the program (not as arguments passed to the program from the command line) individually. For example, I will prompt:
Please enter a command:
...and I need to be able to parse both...
ls
OR
ls -l
but the trouble is that there seems to be no easy way to do this. scanf() will pull each argument individually, but I see no way to place them into differing slots in a char* array. For example, if I do...
char * user_input[10];
for (int i=0; i<10; i++){
user_input[i] = (char *) malloc(100*sizeof(char));
}
for (int i=0; *(user_input[i]) != '#'; i++)
{
scanf("%s", user_input[index]);
index++;
}
...then user_input[0] will get "ls", then the loop will start over, then user_input[0] will get "-l".
gets and fgets just take the whole line. Obviously this problem can be logically solved by going through and plucking out each individual argument...but I'd like to avoid having to do that if there is an easy way that I'm missing. Is there?
Thanks!
If your use case is simple enough, you can do this with strtok:
char *strtok(char *str, const char *delim);
char *strtok_r(char *str, const char *delim, char **saveptr);
The strtok() function parses a string into a sequence of tokens. On the first call to strtok() the string to be parsed should be specified in str. In each subsequent call that should parse the same string, str should be NULL.
You can use strtok or strtok_r to split the string on spaces.
If you're doing something more complex, where some of the arguments could have (quoted) spaces in them, you're pretty much stuck parsing it yourself - though you could have a look at the source of a shell (e.g. bash) to see how it handles it.
kilanash helpfully reminds me of my obvious omission - GNU getopt. You'll still have to have parsed into separate arguments yourself first, though.
Forget that scanf exists for it rarely does what you want. Get the whole line at once and then write code to split it up. strtok - the second most favored answer to this question - is also problem ridden.
You can use strtok_r to break the string up on whitespace. Note that it is a destructive operation (modifies the input string).
Try to see if anything of this will help you:
ANSI C Command Line Option Parsing Library
The Argtable Homepage
Regards,
Tiho
I'm creating a function in C to convert an index value into a string, which is a verbose description of the "field" represented by the index.
So, I have a nice array with all the verbose descriptions indexed by, well the index.
To dump it into a buffer I use code like this
#define BUFFER_SIZE 40
void format_verbose(uint32_t my_index,
char my_buffer[BUFFER_SIZE])
{
snprintf(mY_buffer, BUFFER_SIZE, "%s", MY_ARRAY[my_index].description);
}
The problem comes for some cases I need to insert some other strings into the string when formatting it. So what I want is something like this (where the description in this case contains a %s).
void format_verbose_with_data(uint32_t my_index,
char my_buffer[BUFFER_SIZE])
{
// ...
snprintf(mY_buffer, BUFFER_SIZE, MY_ARRAY[my_index].description,
some_string);
}
Our make file is set up to make this (dangerous) use of snprintf() warn, and warnings are treated as errors. So, it won't compile. I would like to turn off the warning for just this line, where although it is somewhat dangerous, I will control the string, and I can test to ensure it works with every value it's called with.
Alternatively, I would be happy to do this some other way, but I'm really not keen to use this solution
void format_verbose_with_data(uint32_t my_index,
char my_buffer[BUFFER_SIZE])
{
// ...
snprintf(mY_buffer, BUFFER_SIZE, "%s%s%s"
MY_ARRAY[my_index].description1, some_string,
MY_ARRAY[my_index].description2);
}
Because it makes my description array ugly, especially for the ones where I don't need to add extra values.
GCC doesn't have the ability to turn off warnings on a line by line basis, so I suspect you are out of luck. And anyway, if your coding standards say you shouldn't be doing something, you should not be looking for ways to defeat them.
Another point, when you say:
void format_verbose(uint32_t my_index,
char my_buffer[BUFFER_SIZE])
you are really wasting your time typing - it is clearer and more idiomatic to say:
void format_verbose(uint32_t my_index,
char my_buffer[])
or:
void format_verbose(uint32_t my_index,
char * my_buffer)
If all you ever do with snprintf() is copying strings (as seems to be the case), i.e. all you ever have is one or more "%s" as format string, why are you not using strcpy(), perhaps with a strlen() first to check the source's length?
Note that strncpy(), while looking like a good idea, isn't. It always pads the target buffer with zeroes, and in case the source exceeds the buffer size, doesn't null-terminate the string.
After a night of thought, I plan on manually dividing the input string up, by looking for the %s marker myself, and then sending the strings separetley into snprintf() with their own %s. For this case, where only 1 type of format string is allowed, this less onerous, however it would suck to try to completely re-implement a printf() style parser.
void format_verbose_with_data(uint32_t my_index,
char my_buffer[BUFFER_SIZE])
{
char pre_description[BUFFER_SIZE];
char post_description[BUFFER_SIZE];
int32_t offset = -1;
offset = find_string(MY_ARRAY[my_index].description, "%s");
ASSERT(offset >=0, "No split location!");
// Use offset to copy the pre and post descriptions
// Exercise left to the reader :-)
snprintf(mY_buffer, BUFFER_SIZE, "%s%s%s"
pre_description, some_string, post_description);
}
sscanf(input_str, "%5s", buf); //reads at max 5 characters from input_str to buf
But I need to use something like %MACRO_SIZEs instead of %5s
A trivial solution is to create a format string for the same
char fmt_str[100] = "";
snprintf(fmt_str, 100, "%%%ds", MACRO_SIZE);
sscanf(input_str, fmt_str, buf);
Is there a better way to achieve the same?
Like Stefan said, but for sscanf() to more properly answer the question, and with a bit more macro trickery:
#define MACRO_SIZE 5
#define FORMAT(S) "%" #S "s"
#define RESOLVE(S) FORMAT(S)
char buf[MACRO_SIZE + 1];
sscanf(input_str, RESOLVE(MACRO_SIZE), buf);
This uses C's automatic joining together of adjacent string literals, to form the required formatting string at compile-time. This only works if MACRO_SIZE is a preprocessor macro, not if it's a normal runtime variable.
The extra macro call through RESOLVE() is needed since otherwise the argument would not be resolved to its #defined value, and we'd end up with a formatting string of "%MACRO_SIZEs", which is not what we want.
if your MACRO_SIZE is const at compile time, you can try this:
#define MACRO_SIZE "5"
snprintf(fmt_str, 100, "%" MACRO_SIZE "s", buf);
The "correct" solution is what you call the trivial one. All these clever macros(I'd use m4 myself) are just going to make your code less manageable then if you just left it as a constant.
The problem you have here is strings are not a first class data structure in C. They are an array of bytes. Therefor you have to build the array you want to get the meaning you want, and you build that array with sprintf. It's not pretty, but it's correct.
If you're having performance issues and you've tracked it down to here then yes, eliminate the function calls. But unless the value for MACRO_SIZE is repeated a hundred times or spread out over multiple files I'd just change the literal. A macro is just faking having more flexibility, using sprintf actually gives you flexibility.