String manipulation in argument - c

I have got two functions. One function calls the print functions.
Calling function (key is a number between 0 and 42 symbolizing keys of a remote and the keymap just maps names to the keys):
void calling_function(int key){
print_info("[KEY]", ("%#4x %s", key, keymap[key - 1]));
...
}
And the printing function:
print_info(char *prefix, char *message){
print("%s %s\n", prefix, message);
}
But the output looks like this:
[KEY] COLOR_RED
So the int is missing, why is it not inserting the integer? I would like to not work with sprintf()

The second parameter to print_info is char *message. You've passed it ("%#4x %s", key, keymap[key - 1]) which knowing the comma operator evaluates to just the last item keymap[key - 1] which seems to also be a char * lucky enough. You need to use some type of printf function to fill in the printf style format specifiers in "%4x %s"

So the int is missing, why is it not inserting the integer?
Why would it?
I would like to not work with sprintf()
Yet you seem to think that the expression ("%#4x %s", key, keymap[key - 1]) should be a shortcut. It is not. C does not have a Pythonesque syntax for string interpolation. That's what sprintf() is for. The expression you have used employs C's comma operator (,), which evaluates its left operand, discards the result, and then evaluates its right operand, returning that as the value of the expression.
Thus, your print_info() call is equivalent to
print_info("[KEY]", keymap[key - 1]);
(Note that the comma there is not a comma operator; it is part of the syntax of a function call.)
sprintf() is one of the standard ways to produce a formatted string, and it seems the most appropriate for this case. You might approach it like this:
char message[KEYMAP_VALUE_LIMIT + 6];
sprintf(message, "%#4x %s", key, keymap[key - 1]);
print_info("[KEY]", message);
If you do not have and cannot reasonably create an appropriate KEYMAP_VALUE_LIMIT macro then you probably should dynamically allocate the message buffer based on the actual length of the string corresponding to key. In this specific case, however, it looks like you could avoid that by redefining what is considered "prefix" and what "message":
char prefix[13];
sprintf(prefix, "[KEY] %#4x", key);
print_info(prefix, keymap[key - 1]);

Related

Why are conversion characters needed?

When I run this:
#include <stdio.h>
int main() {
int x = 1;
printf(x, "\n");
return 0;
}
It gives me these errors:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x1)
Format string is not a string literal (potentially insecure)
Treat the string as an argument to avoid this
Incompatible integer to pointer conversion passing 'int' to parameter of type 'const char *'
And it outputs:
(lldb)
However, when I change it to:
printf("%s", x);
It works perfectly fine. (outputs "1", as expected)
Why are the conversion characters (e.g. %s, %d, etc...) needed?
They're needed to tell the printf function what the types (and number) of the parameters are that you are passing.
The language has no mechanism to allow a function to determine this dynamically, so the format string gives it the clues to decode them.
The format string is always the first parameter, because the called function can always access that in the same place. Functions like printf are still written in C typically, so can only use those functions that the language provides.
They parameters are not "conversion characters". I think your confusion comes from the fact that you think printf simply prints all its arguments and automatically deduces how to print each. However, printf cannot work like that because C does not have support for overloading.
Concretely, the first argument is not like the rest. It is not something to print, but a format string. You can see easily what it means by trying this:
printf("My friend %s has %d coins!", "John", 123);
which will print:
My friend John has 123 coins!
%s here specifies the first argument (after the format string) will be interpreted as a string, and %d means the second argument (again, after the format string) will be understood as an integer. Both will be replaced with the actual value from the argument.

comma (,) in C Macro Definition

I've never seen this syntax before.
#define SNS(s) (s),(sizeof(s)-1)
The way i'm reading this is that SNS(s) = sizeof(s)-1. What is the comma doing? Is it necessary?
int ft_display_fatal(const char *err, unsigned len, int fd, int rcode)
{
UNUSED(write(fd, err, len));
return (rcode);
}
Main
return (ft_display_fatal(SNS("File name missing.\n"), 2, 1));
Macros are just text replacement, so they can expand to just about anything you want. In this case, the macro is being used to expand into two arguments to a function. The function expects a string and the number of characters in the string as arguments, and the SNS() macro generates them. So
ft_display_fatal(SNS("File name missing.\n"), 2, 1)
expands into
ft_display_fatal(("File name missing.\n"),(sizeof("File name missing.\n")-1), 2, 1)
This is basically only useful when the parameter is a string literal: sizeof("string") is the size of the char array including the trailing null byte, and -1 subtracts that byte to get the number of significant characters in the string. This is the len argument to the ft_display_fatal function (I'm not sure why it can't just use strlen() to get this by itself -- I guess it's a performance optimization).
The way i'm reading this is that SNS(s) = sizeof(s)-1.
You are reading it wrong.
What is the comma doing?
Macro expansion results in textual substitution. You can use SNS(a) to pass two arguments to a function.
ft_display_fatal(SNS("File name missing.\n"), 2, 1)
You can see that ft_display_fatal takes 4 arguments, but only 3 are provided. This works because SNS expands to 2 arguments. If it didn't, you'd get a compiler error.

Just started C today, trying to understand obtaining values using fgets, sscanf and fscanf

I have to understand someone else's code in C and I'm trying to understand how the variables are being defined using fgets, sscanf and fscanf.
input: ./test 0 testfile.txt
the code:
int main(argc,argv)
int argc;
char *argv[];
{
char line[LINEBUF+1] //I assume this is where line type is 'defined'?
FILE *fopen(), *pfin ;
int a, b;
char test_array[5]
...
pfin = stdin ;
fgets( line, LINEBUF, pfin);
sscanf(line," %s %d", test_array, &a)
fgets( line, LINEBUF, pfin);
fprintf(stderr,"%d\n",a);
It seems that this reads in the input ./test 0 testfile.txt, so a refers to 0. My questions are:
The value a is called in the end. So when using sscanf or fscan, do we just feed the address of any sort of input in, or are we using &a because it's an integer fed into fscan?
Why is fgets being used twice?
What do pfin and LINEBUF do?
This appears later in the code:
fgets( line, LINEBUF, pfin);
fscanf(pfin," b: %d", &b );
My question is:
I never saw b being given a value anywhere in the code until this section. Is this code taking content from within the file testfile.txt and giving value to variable b?
int main(argc,argv)
int argc;
char *argv[];
{
...
This is a very archaic style of defining function parameters, and should not be used in new code anymore (it's been out of date since the late 1980s); instead, it should be written
int main( int argc, char *argv[] )
{
...
The value a is called in the end. So when using sscanf or fscan, do we just feed the address of any sort of input in, or are we using &a because it's an integer fed into fscan?
Each conversion specifier (%s, %d, %f, etc.) expects the corresponding argument to have the correct type. In the line
sscanf(line," %s %d", test_array, &a)
%s expects its corresponding argument, test_array, to have type char *, and %d expects its corresponding argument, &a, to have type int *. Fortunately the arguments are the right type1, so everything should work. Note that conversion specifiers have different meanings for *scanf and *printf. Where %d expects an int * for *scanf, it expects a plain int for *printf.
Why is fgets being used twice?
You don't provide enough code to answer this question. It looks like the code is reading each input line as text (using fgets) and then parsing data from that line (using sscanf). Based on the limited snippet you've posted, it's expecting at least the first part of the input to be structured such that you have one line with a short string (max 4 characters long) followed by an integer, something like
AAAA 10
Since you don't show how line is processed after that, it's anybody's guess what it's supposed to contain.
What do pfin and LINEBUF do?
pfin is being set to point to the standard input stream; honestly, it's superfluous, and you can use the built-in variable stdin in its place.
LINEBUF is a macro that expands to either an integer literal or an integer expression, and it's being used to specify the size of the line array. An example would be
#define LINEBUF 100
...
char line[LINEBUF + 1];
After preprocessing, the above code becomes
char line[100 + 1];
Remember that in C, a string is a sequence of character values followed by a 0-valued terminator value. Thus, to store a n-character string, you need to set aside a n+1-element array of char.
The expression a has type int, so the expression &a has type int *. The expression test_array has type char [5]; under most circumstances, an array expression will be converted ("decay") to a pointer expression, and the value of the pointer expression will be the address of the first element of the array. This implicit converion only applies to arrays.

Ternary operator inside printf

After reading this I started thinking that I have learned a loot about printf(). Suddenly I found following code snippet from this book:
int main()
{
char str[]="Hello";
int i=5,j=10;
printf(i>j?"%50s":"%s",str); //unable to understand this
return 0;
}
Surprisingly above code is running without errors and it prints Hello.
As per my knowledge following is syntax of printf():
int printf(const char *format,argument_list);
So according to this syntax, printf() should start with format string. But as you can see in above code printf() is starting with i>j.
Does it mean I am wrong in interpreting syntax of printf()?
Does placing ternary operator inside printf() is a special case?
EDIT
I know about ternary operator I am asking about first argument of printf() which should be const char* which I seem not in my example.
The conditional operator:
i>j?"%50s":"%s"
is an expression and it has to be evaluated before the function call itself can be evaluated. We can see this by going to the draft C99 standard section 6.5.2.2 Function calls which says:
An argument may be an expression of any object type. In preparing for
the call to a function, the arguments are evaluated, and each
parameter is assigned the value of the corresponding argument.81)
So what is the result of the evaluation of the conditional operator? If we go to section 6.5.15 Conditional operator it says (emphasis mine):
The first operand is evaluated; there is a sequence point after its
evaluation. The second operand is evaluated only if the first compares
unequal to 0; the third operand is evaluated only if the first
compares equal to 0; the result is the value of the second or third
operand (whichever is evaluated), converted to the type described
below.95
so in either case the result is a string literal which will decay to pointer to char which satisfies the requirement for the first argument to printf.
This code is normal and is not any special case. The requirement for printf is that the first argument should be of the type const char*, but it does not necessarily mean that it needs to be a string literal like "%s". All it means that you need to pass as the first argument an expression of the type const char*. And i>j?"%50s":"%s" fulfils this requirement.
I think you well understood the printf syntax but i think you are missing something about C syntax.
It exist a form of "compact IF like" statement formatted like that : ( condition ? true : false )
For example you can do :
int a=5;
int b=(a==5 ? 128 : 256);
int c=(a!=5 ? 8 : 9);
In this case, b=128 and c=9.
An other example :
int flag=1;
printf("The value is: %s", (flag!=0 ? "true" : "false) );
In this case you can see : The value is true
On your example :
printf(i>j?"%50s":"%s",str);
if i is upper than j you use "%50s" format and, if i is lower you use "%s" format
It can be view like :
if (i>j)
printf("%50s",str);
else
printf("%s",str);
You can see the advantage of compact test.
It is a ternary operator and in this the condition i>j is false so %s will be passed as parameter to printf which will print the value of character array which is hello.
Does it mean I am wrong in interpreting syntax of printf()?
No you are not interpreting it wrong.
Does placing ternary operator inside printf() is a special case?
In C, you can say that its an expression instead of a statement
Your code is equivalent to:
if (i > j)
printf("%50s", str);
else
printf("%s", str);
The ternary operator is simply an inline if that's used as an expression (while a regular if is used to create a block). Your line is equal to this:
if (i > j)
printf("%50s", str);
else
printf("%s", str);
if(i>j)
printf("%50s",str);
else
printf("%s",str);
Therefore, Hello gets printed in both situations
Q: Does it mean I am wrong in interpreting syntax of printf()?
A: No, just need to expand what is allowable.
Q: Does placing ternary operator inside printf() is a special case?
A: No ?: is not special, but sometimes confusing at first glance.
The format supplied to printf() does not need to be a literal. It may be any string variable.
int main() {
char str[]="Hello";
char *format;
int i,j;
i = 5; j = 10;
format = i > j ? "%50s" : "%s";
printf(format, str);
i = 10; j = 5;
format = i > j ? "%50s" : "%s";
printf(format, str);
return 0;
}
There is a statement of the form: condition?consequent:alternative.
The condition is checked, and if it's true you'll get the consequent. otherwise you'll get the alternative.
For example:
5>3 ? 10 : 5
5>3 is true, so you'll get 10.

New to C: whats wrong with my program?

I know my way around ruby pretty well and am teaching myself C starting with a few toy programs. This one is just to calculate the average of a string of numbers I enter as an argument.
#include <stdio.h>
#include <string.h>
main(int argc, char *argv[])
{
char *token;
int sum = 0;
int count = 0;
token = strtok(argv[1],",");
while (token != NULL)
{
count++;
sum += (int)*token;
token = strtok(NULL, ",");
}
printf("Avg: %d", sum/count);
printf("\n");
return 0;
}
The output is:
mike#sleepycat:~/projects/cee$ ./avg 1,1
Avg: 49
Which clearly needs some adjustment.
Any improvements and an explanation would be appreciated.
Look for sscanf or atoi as functions to convert from a string (array of characters) to an integer.
Unlike higher-level languages, C doesn't automatically convert between string and integral/real data types.
49 is the ASCII value of '1' char.
It should be helpful to you....:D
The problem is the character "1" is 49. You have to convert the character value to an integer and then average.
In C if you cast a char to an int you just get the ASCII value of it. So, you're averaging the ascii value of the character 1 twice, and getting what you'd expect.
You probably want to use atoi().
EDIT: Note that this is generally true of all typecasts in C. C doesn't reinterpret values for you, it trusts you to know what exists at a given location.
strtok(
Please, please do not use this. Even its own documentation says never to use it. I don't know how you, as a Ruby programmer, found out about its existence, but please forget about it.
(int)*token
This is not even close to doing what you want. There are two fundamental problems:
1) A char* does not "contain" text. It points at text. token is of type char*; therefore *token is of type char. That is, a single byte, not a string. Note that I said "byte", not "character", because the name char is actually wrong - an understandable oversight on the part of the language designers, because Unicode did not exist back then. Please understand that char is fundamentally a numeric type. There is no real text type in C! Interpreting a sequence of char values as text is just a convention.
2) Casting in C does not perform any kind of magical conversions.
What your code does is to grab the byte that token points at (after the strtok() call), and cast that numeric value to int. The byte that is rendered with the symbol 1 actually has a value of 49. Again, interpreting a sequence of bytes as text is just a convention, and thus interpreting a byte as a character is just a convention - specifically, here we are using the convention known as ASCII. When you hit the 1 key on your keyboard, and later hit enter to run the program, the chain of events set in motion by the command window actually passed a byte with the value 49 to your program. (In the same way, the comma has a value of 44.)
Both of the above problems are solved by using the proper tools to parse the input. Look up sscanf(). However, you don't even want to pass the input to your program this way, because you can't put any spaces in the input - each "word" on the command line will be passed as a separate entry in the argv[] array.
What you should do, in fact, is take advantage of that, by just expecting each entry in argv[] to represent one number. You can again use sscanf() to parse each entry, and it will be much easier.
Finally:
printf("Avg: %d", sum/count)
The quotient sum/count will not give you a decimal result. Dividing an integer by another integer yields an integer in C, discarding the remainder.
In this line: sum += (int)*token;
Casting a char to an int takes the ASCII value of the char. for 1, this value is 49.
Use the atoi function instead:
sum += atoi(token);
Note atoi is found in the stdlib.h file, so you'll need to #include it as well.
You can't convert a string to an integer via
sum += (int)*token;
Instead you have to call a function like atoi():
sum += atoi (token);
when you cast a char (which is what *token is) to int you get its ascii value in C - which is 49... so the average of the chars ascii values is in fact 49. you need to use atoi to get the value of the number represented

Resources