c optarg atoi with no args - c

Consider the following code:
int number;
while((w = getopt(argc, argv, "n:s:")) != -1) {
switch (w){
case 'n': {
opfile->filename = optarg;
}break;
case 's': {
number = atoi(optarg);
}break;
}
}
Now, when I leave both options or the option s blank, for example I start my program with no command line args, then the number variable still gets a random value.
What am I missing here? Some if-statement in the case of s? Specifically, I want to cover the case where the user doesn't assign a specific value/option to s in the command line arguments.

When there is no 's' option passed to the program, the case 's' branch is not executed at all, and nothing else sets number to a value, which means that subsequent reads trigger undefined behavior. (This is potentially much worse than just giving you a random value when you read from it later. It's a must-fix bug.)
But because nothing else touches number, it will be enough to change
int number;
to
int number = 0;
or whatever else you want your default to be.
(By the way, you should really be using strtol instead of atoi, because atoi ignores syntax errors.)

Related

C - Two types of formatted user input with an unknown number of inputs

I want the user to be able to input two types of input like: "C[some size_t]" and "O[some memory address]" and depending on whether it's a C or O at the beginning, I'll call different functions. I also want to be able to take in an unknown number of those inputs and in any order.
My way of going around it was something like this:
int main()
{
// variables
while (1) { // Infinite loop to take in multiple unknown amount of inputs?
while (fgets(input, BUFFER_SIZE, stdin)) {
if (sscanf(input, "%c%zu", &branch, &num) == 2) {
if (strcmp(&branch, "C")
// function call
} else if (sscanf(input, "%c%c", &branch, (char *)addr) == 2) {
if (strcmp(&branch, "O")
// function call
}
}
}
return 0;
}
I understand why it's not working of course and I know my not-solution is wrong but I have no idea how else to go about this. The code takes in the first input and just hangs and if I start with an input beginning with O, it'll go into the first if statement when it's not supposed to. I'm also not sure if my while(1) loop is the correct way to deal with multiple user inputs.
You try to read the branch char and the respective argument in one single go – however the argument differs in both cases. So you need first to read the character, then decide and only then scan the argument – as soon as you know what to scan at all and thus are able to select the appropriate format string.
As you just read in any character you could do so a bit simpler with getc, by the way:
char branch = getc(); // instead of `if(scanf("%c", &branch) == 1)`
// note: would have been one single scan (branch) only!
if(branch == 'C')
{
// scan num
}
else if(branch == 'O')
{
// scan addr
}
else
{
// error handling
}
or alternatively (I personally would prefer)
char branch = getc();
switch(branch)
{
case 'C':
// scan num
break;
case 'O':
// scan addr
break;
default:
// error handling
break;
}
Note that strcmp requires null-terminated strings (char arrays) and cannot be used for comparing single characters – these need to be compared via equality operator, see above.

I was writing this code for a pizza delivery system. When run from the command prompt it displayed an error. How should I run it?

I am building this pizza program using command-line arguments and interface. It should return the ingredients from the arguments.
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
int main(int argc, char *argv[])
{
char *delivery = "";
int thick = 0;
int count = 0;
char ch;
while(ch = getopt(argc, argv, "d:t") != EOF)
switch(ch)
{
case 'd' :
delivery = optarg;
break;
case 't' :
thick = 1;
break;
default:
fprintf(stderr, "Invalid option : %s", optarg);
return 1;
}
argc -= optind;
argv += optind;
if(thick)
puts("thick crust");
if(delivery[0])
printf("To be delivered %s", delivery);
puts("ingredients :");
for(count = 0; count<argc; count++)
{
puts(argv[count]);
}
return 0;
}
When running this program using command prompt in windows as :
program -d now -t
it was returning an error :
invalid option: now
How should I run this program and why is this error showing?
You've made five mistakes (two important, three less so) and one of the important mistakes is masking the other.
while(ch = getopt(argc, argv, "d:t") != EOF)
Important mistake #1: The operator precedence of = is lower than !=, so this assigns the result of the comparison to ch, not the return value of getopt as you expected. Less important mistake #1: getopt returns −1 when it reaches the end of the options. EOF is not necessarily equal to −1.
You should have written
while ((ch = getopt(argc, argv, "d:t")) != -1)
Now, when invoked as you described, in the first iteration the value returned from getopt will be 'd', which is not equal to EOF (nor to −1), so the value assigned to ch will be the number 1 (also known as Control-A, or U+0001 START OF HEADING). This number is not equal to either 'd' or 't' (the C standard guarantees 0 < 'a' < 'b' < 'c' < 'd' < ... < 'z', so even if we don't assume ASCII, 1 could only ever be equal to 'a') so the default branch of the switch is taken, and we do this:
fprintf(stderr, "Invalid option : %s", optarg);
return 1;
And here is important mistake 2: you should print ch, not optarg, when you get an invalid option. ch is the option; optarg is the argument to the option, if any. If you had printed ch you would have realized that the value in ch was not what you expected it to be.
The other less important mistakes are
char ch;
This should be int ch; Like in a getchar loop, the value returned from getopt when it reaches the end of the arguments is outside the range of char.
fprintf(stderr, "Invalid option : %s", optarg);
printf("To be delivered %s", delivery);
Both of these need a \n at the end of the string to be printed.
In the original form of your question, your code was badly indented, but I suspect that was because you used 4 spaces for a single level of indentation and an 8-space-wide hard TAB for a second level; it is a long-standing bug in the Stack Overflow interface that this kind of code gets mangled when pasted into a question. So it's not your fault. (You should indent with spaces only, though.) The more serious style problem with your code is that some of your single-statement loops have curly braces around them and some of them don't. Whether or not one should put curly braces around single-statement blocks in C is one of the oldest holy wars; I personally think both sides are wrong, but never mind that; the important thing you should learn is, pick one style or the other and stick to it consistently throughout your code.

Ending a loop when fscanf reads a certain value from a text file

I'm trying to write a program that reads a character and a float from a text file and then saves these values to a variable.
The text file is formatted in this manner:
S 15.24
G 28.00
S 56.90
H 90.00
0
I'm attempting to have the character be read and then have it run through a switch to do a few calculations based on which character comes before the float(all S values are grouped together, all G values are grouped together, etc)
To have it go through all the lines available, I'm using a while loop that terminates when the read character reaches the 0 at the end of the list.
Just for testing purposes I'm having the program print "Done" when it exits the loop. However when I run the program, it runs through every character and prints it properly to the terminal until its printed the values before 0. once it reaches the 0, the program doesn't do anything else, but it doesn't print "Done" like I assumed it should.
This is the code I'm using:
int main()
{
int s_num,g_num,h_num,S,H,G,n_total;
float amount,s_total,s_sum,g_sum,h_sum;
char char_name;
FILE*Input;
Input = fopen("Input.txt","r");
FILE*Output;
Output = fopen("Output.txt","w+");
char_name = 1; //Just using 1 as a way to make the while loop occure
while(char_name != '0')
{
fscanf(Input,"%c%f",&char_name,&amount);
switch(char_name)
{
case 'S':
printf("S,%f\n",amount);
break;
case 'G':
printf("G,%f\n",amount);
break;
case 'H':
printf("H,%f\n",amount);
break;
}
}
printf("done");
return 0;
}
(disregard the unused values, those are to be used in the actual program)
What changes would I need to make to get the loop to terminate once it reaches 0?
The following code:
fscanf(Input,"%c%f",&char_name,&amount);
will fail to read a float upon hitting the last data line in the file. The switch statement is not needed. Use an if statement to check if the first char is '0' and either break or read the floating point number and print both the character and number.
while (TRUE)
{
fscanf(Input,"%c",&char_name);
if (char_name == '0')
break;
fscanf(Input,"%f",&amount);
printf("%c,%f\n",char_name,amount);
}
Using fgets() and then parsing the line is the best way to go. But using fscanf(), recommend:
Change format so white-space is ignored. otherwise '%c' will take on white-space values
// fscanf(Input,"%c%f",&char_name,&amount);
fscanf(Input," %c%f",&char_name,&amount); // add space
Check the return value from fscanf(). do not use values char_name,amount unless the return value from fscanf() indicates those values were indeed filled.
int cnt = fscanf(Input," %c%f",&char_name,&amount);
switch(cnt) {
case 1: Handle_OnlyCharName(); break; // Candidate only "0" line
case 2: Handle_BothCharNameAndAmount(); break; // OP's switch(char_name)
case EOF: Handle_IOErrorOrEndOfFile(); break; // No more data
default: NeverShouldGetHere();
}

Switch Case working differently

Consider two codes. Why are they giving different outputs though same value hass been assigned to i ,'i' being a char in both the codes.
first code-->
(here value is assigned to i directly)
void main()
{
char i=3;
clrscr();
switch(i)
{
default : printf("\nHi..\n");
break;
case 1:printf("\na");
break;
case 2:printf("\nb\n");
break;
case 3:printf("\nc");
break;
}
}
second using printf-scanf--->
void main()
{
char i;
printf("ENTER i");
scanf("%c",&i);
clrscr();
switch(i)
{
default : printf("\nHi..\n");
break;
case 1:printf("\n\na");
break;
case 2:printf("\nb\n");
break;
case 3:printf("\nc");
break;
}
}
in the second code when i m giving 3 as input, i get "Hi.." as output. What makes the two codes work differently..??
In the first you're using
char i = 3
But when you use scanf you essentially use:
char i = '3'
These two contain different values
See the following ASCII http://www.asciitable.com/
After reading the character from stdin:
scanf("%c",&i);
i will contain the ASCII code of 3 (51) and not the value 3, leading to taking the default branch of switch.
The solution is declaring your variable as int and using
scanf("%d",&i);
to read it.
In the first example you assigned to i the integer value 3, which is not the same as assigning the character '3'. In C, when you assign a value to a char variable that value would represent a code specific to a certain character (considering a standard).
In the second example the scanf function read a character from stdin, which was interpreted as a character due to the use of %c, and assigned to the given variable the code specific to the read character.
It is not related to the switch statement, but to the scanf function.
Read its documentation ie scanf(3) man page. See also this answer to a very related question.
Notice that the char '3' is not encoded as 3, but as 51 in ASCII
Learn to enable all warnings and debugging info (e.g. compile using gcc -Wall -g) and learn to use the debugger (i.e. gdb)
Change your code to
char i -> int i;
scanf("%c",&i) -> scanf(" %d",&i);

Pass multiple arguments in C command line

How am i able to pass multiple arguments in a C program like this, using different switches
program -d <argument1> -p <argument2>
I'm using getopt to enable me to pass arguments.
int main(int argc, char **argv)
{
while(1)
{
unsigned int c = getopt(argc, argv, "-dD:hHgGp:");
if( c == -1 ) break;
switch( c )
{
case 'D':
case 'd':
printf("\nd=");
strcpy(D,optarg);
printf(D);
break;
case 'g':
case 'G':
printf("g");
break;
case 'p':
printf("\nPath=");
strcpy(pathFile,optarg);
printf(pathFile);
break;
case 'H':
case 'h':
usage(); //For help
return 0;
default:
return 0;
}
}
}
EDIT: The code here is a dummy code which I use for testing. It returns the argument that is passed as a string.
Is it just a case of you forgetting the “:” after the “d” in getopt arguments?
unsigned int c = getopt(argc, argv, "-d:D:hHgGp:");
It seems rather odd to write this:
while (1)
{
unsigned int c = getopt(argc, argv, "-dD:hHgGp:");
if( c == -1 ) break;
The return value of getopt() is an int; why would you save it in an unsigned int?
int opt;
while ((opt = getopt(argc, argv, "-dD:hHgGp:")) != -1)
{
switch (opt)
{
case ...
}
}
If you're going to make options case-insensitive (not a good idea, IMO), then be consistent about it and handle P: too. Also, as first noted by kmkaplan's answer, you have D: and d being handled by the same switch; they should both be followed by a colon for sanity's sake: "-d:D:hHgGp:P:" would at least be self-consistent.
Also, under most circumstances, you don't need to copy the argument string (optarg) anywhere; you simply save a pointer to its current value in a convenient variable. If you do copy the argument string, you must check the length of the argument to ensure you are not overflowing buffers.
The first character of the option string is not normally a dash; it isn't a standard behaviour. The Mac OS X documentation for getopt() does note that it is a GNU extension and advises against ever starting an option string with a dash (and the option string should only contain a dash for backwards compatibility, not in new code — again, on Mac OS X or BSD). Under GNU getopt(), the leading dash means that non-option arguments are reported as if they were options. As long as you're aware that you're using a GNU getopt() extension, there's no harm in doing so.

Resources