c command line int is wrong - c

Whenever I send in a number from the command line it errors and gives me a wrong number
edgeWidth=*argv[2];
printf("Border of %d pixels\n", edgeWidth);
fileLocation=3;
./hw3 -e 100 baboon.ascii.pgm is what I send in through the command line and when I print the number to the screen I get 49 as the number
int edgeWidth is defined at the beginning of the program.
Why is it not giving me 100?

argv contains an array of strings. So argv[1] is a string, you need to convert it to an integer:
edgeWidth = atoi(argv[1]);

The problem is that by doing
edgeWidth = *argv[2];
you're assigning the first character of "100" to edgeWidth. 49 happens to be the ASCII value for '1'.
If you want 100, you need to use something like atoi or strtol to parse the string into an int.
Addendum: Regarding numeric promotion, part two of 6.5.16.1 in the C99 spec states:
In simple assignment (=), the value of the right operand is converted
to the type of the assignment expression and replaces the value stored
in the object designated by the left operand.
so it does appear that numeric promotion happens here.

Because command line arguments are by default as char* (or may be char** somewhere) not int. you need proper conversion like atoi() to use it as int.
You should use edgeWidth = atoi(argv[2]) to get expected output.

Related

Scanf a Character into an Array in C

I'm trying to take a single character in an array and then print that character using a specific syntax. Here's my code :
int main(){
char in[18];
scanf("%c",in);
printf("%c",in);
return 0;
}
I know how to take a character from user in C & many other ways to do the same task but I'm curious to know Why this code prints nothing on the screen. Here's my explanation for this code. Kindly correct me if wrong.
First of all array of 18 characters is declared.
Using scanf, Character is stored in the 1st position of array.("in" refers to the address of its first element.)
Then when I'm trying to print that character, It prints nothing.
When I changed "in" to "in[0]" then Character prints on the screen.
I think "in" also points to the 1st element as well as in[0] too. Then Why I'm getting two different answers. ?
Thanks In Advance !!
in[0] does not point to the first element in the array. It is the first element in the array.
in has type char * (when passed to a function) while in[0] has type char. And the %c format specifier to printf expects a char, not a char *.
Your code invokes undefined behavior, the compiler might be warning about the fact that the "%c" specifier expects a char (rigorously speaking it expects an int parameter that is after converted to unsigned char) parameter but you passed a char * (an array of char).
To make it print the character use
printf("%c", in[0]);
Passing the wrong type for a given format specifier in both printf() and scanf() is undefined behavior.

printf() positional arguments with the left justify flag

I have been trying to understand how to do left justification with positional arguments using the printf function.
When I use something like the following, I get no errors:
printf("This is a %2$*1$s width var\n", 10, "ten");
However, when I try to use the left justify flag, I get an error:
printf("This is a %-2$*1$s width var\n", 10, "ten");
ex3.c:11:2: warning: unknown conversion type character ‘$’ in format [-Wformat=]
It almost seems like the flag is not being recognized, and instead argument "-2" is being looked for.
Is there a way I can format the string in printf to subvert this issue while still using positional arguments?
Here's what man printf says (on a system with glibc):
One can also specify explicitly which argument is taken, at each place where an argument is required, by writing %m$ instead of % and *m$ instead of *, where the decimal integer m denotes the position in the argument list of the desired argument, indexed starting from 1.
Or, if you prefer the standard, Posix says:
Each conversion specification is introduced by the '%' character or by the character sequence "%n$", after which the following appear in sequence...
In other words, you can't just throw the 2$ anywhere into the format. It needs to be right after the %:
printf("This is a %2$-*1$s width var\n", 10, "ten");

array subscript has type 'char'

I have the following code to read an argument from the command line. If the string is 1 character long and a digit I want to use that as the exit value. The compiler gives me a warning on the second line (array subscript has type 'char' ) This error comes from the second part after the "&&" .
if (args[1] != NULL) {
if ((strlen(args[1]) == 1) && isdigit(*args[1]))
exit(((int) args[1][0]));
else
exit(0);
}
}
Also, when I use a different compiler I get two errors on the next line (exit).
builtin.c: In function 'builtin_command':
builtin.c:55: warning: implicit declaration of function 'exit'
builtin.c:55: warning: incompatible implicit declaration of built-in function 'exit'
The trouble is that the isdigit() macro takes an argument which is an integer that is either the value EOF or the value of an unsigned char.
ISO/IEC 9899:1999 (C Standard – old), §7.4 Character handling <ctype.h>, ¶1:
In all cases the argument is an int, the value of which shall be
representable as an unsigned char or shall equal the value of the macro EOF. If the
argument has any other value, the behavior is undefined.
On your platform, char is signed, so if you have a character in the range 0x80..0xFF, it will be treated as a negative integer. The usual implementation of the isdigit() macros is to use the argument to index into an array of flag bits. Therefore, if you pass a char from the range 0x80..0xFF, you will be indexing before the start of the array, leading to undefined behaviour.
#define isdigit(x) (_CharType[(x)+1]&_Digit)
You can safely use isdigit() in either of two ways:
int c = getchar();
if (isdigit(c))
...
or:
if (isdigit((unsigned char)*args[1]))
...
In the latter case, you know that the value won't be EOF. Note that this is not OK:
int c = *args[1];
if (isdigit(c)) // Undefined behaviour if *args[1] in range 0x80..0xFF
...
The warning about 'implicit definition of function exit' means you did not include <stdlib.h> but you should have done so.
You might also notice that if the user gives you a 2 as the first character of the first argument, the exit status will be 50, not 2, because '2' is (normally, in ASCII and UTF-8 and 8859-1, etc) character code 50 ('0' is 48, etc). You'd get 2 (no quotes) by using *args[1] - '0' as the argument to exit(). You don't need a cast on that expression, though it won't do much harm.
It seems that the compiler you use has a macro for isdigit (and not a function, you wouldn't have a warning if it was the case) that uses the argument as a subscript for an array.
That's why isdigit takes an INT as argument, not a char.
One way to remove the warning it to cast your char to int :
isdigit(*args[1]) => isdigit((int)(*args[1]))
The second warning means you want to use the exit function, but it has not been defined yet. Which means you have to do the required #include.
#include <stdlib.h>
is the standard in c-library to use exit(int) function.
BTW, if this code is in your "main" function, you mustn't check "arg[1] == NULL", which could lead to a segmentation fault if the user didn't provide any argument on the command-line. You must check that the argc value (the int parameter) is greater than 1.
It is not completely clear what you want to give as exit code, but probably you want args[1][0] - '0' for the decimal value that the character represents and not the code of the character.
If you do it like that, you'd have the side effect that the type of that expression is int and you wouldn't see the warning.
For exit you probably forgot to include the header file.
Try changing
isdigit(*args[1])
to
isdigit(args[1][0])
Your other errors are because you aren't using #include <stdlib.> which defines the exit function.

typecasting syntax not clear

I was reading a book and came across a program to read entries from a /proc file.
The program which they mentioned has following line
printf("%.*s", (int) n, line);
I am not clear with meaning of above line
what type of print if above "%.*s used instead of %s
The code can be read here
Abstract from here:
.* - The precision is not specified in
the format string, but as an
additional integer value argument
preceding the argument that has to be
formatted.
So this prints up to n characters from line string.
The cast expression (int) n converts the value of n to type int. This is because the formatting specifier requires a plain int, and I assume (since you didn't include it) the variable n has a different type.
Since a different type, like size_t might have another size, it would create problems with the argument passing to printf() if it wasn't explicitly converted to int.

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