Command line arguments in C, cant understand its behaviour - c

I've the following code :
#include <stdio.h>
int main(int argc, char* argv[]){
int a = argv[1]?atoi(argv[1]):10;
int b = argv[2]?atoi(argv[2]):20;
printf("a = %d, b = %d\n", a, b);
return 0;
}
If I do not provide any command line inputs, the values in "a" and "b"
should be 10 and 20 respectively, but what happens instead is "a" gets value as 10 whereas "b" gets 0.
I cant understand why this is happening, since I am doing exactly same
thing in both cases.
Thanks.

The runtime (often thru crt0 & kernel) guarantees (per C99 or POSIX standards) that (for main's arguments argc & argv) :
argc is positive
argv is a valid non NULL pointer to an array of argc+1 pointers
argv[argc] is the NULL pointer
for each i between 0 and argc-1 included, argv[i] is a (valid non NULL) pointer to a zero-terminated string
hence access to argv[j] with j>argc (or j<0) is undefined behavior (UB) - so explaining what happens is only possible by diving into implementation details...
the valid argv[i] are not pointer aliases, so if i & j are both between 0 and argc-1 included, and i != j, argv[i] != argv[j]
therefore, your code is wrong when argc==1 because accessing argv[2] is forbidden (UB).
You should code:
int main(int argc, char* argv[]){
int a = (argc>1)?atoi(argv[1]):10;
int b = (argc>2)?atoi(argv[2]):20;
Be very scared of undefined behavior (see also the references here). Sadly, a program with UB might sometimes appear to "work" (that is might not crash). But at other times bad things may happen. So you should imagine the worst.

If I do not provide any command line inputs, the values in "a" and "b"
should be 10 and 20 respectively.
No, if you don't provide any command-line inputs, you're accessing beyond the end of the argv array and anything can happen (including your program crashing and burning).
Your checks should be:
int a = argc > 1 ? atoi(argv[1]) : 10;
int b = argc > 2 ? atoi(argv[2]) : 20;
That's what argc is for, to tell you how many entries argv has in it that you can access.

Related

How to pass ints and char variables to function in command line

I wrote a function where you pass two integers and an operator character (+-*/) and give a result of the two numbers. The code takes the character and checks it using its ascii value. If I pass a number in place of the operator (42 instead of *) then it works, but I don't know how to pass it as a char because the function has int argc and int * argv parameters (the function prototype was written by an instructor.) How do I make it so I can pass the char straight from the command line?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int result;
int main(int argc, char *argv[]){
printf("main_add pid: %d\n", getpid());
int a = 0;
int b = 0;
char c;
//printf("%s\n", argv[1]);
//printf("%s\n", argv[2]);
//printf("%s\n", argv[3]);
a = atoi(argv[1]);
b = atoi(argv[2]);
c = atoi(argv[3]);
if (c=='*'){
result = a * b;
printf("%d * %d = %d\n", a, b, result);
}
else if(c=='+'){
result = a + b;
printf("%d + %d = %d\n", a, b, result);
}
else if(c=='-'){
result = a - b;
printf("%d - %d = %d\n", a, b, result);
}
else if(c=='/'){
result = a / b;
printf("%d / %d = %d\n", a, b, result);
}
else{
printf("fail");
}
}
```[enter image description here][2]
[1]: https://i.stack.imgur.com/X0eiN.png
[2]: https://i.stack.imgur.com/ij5FR.png
the function has int argc and int * argv parameters (the function prototype was written by an instructor.)
That was not written by an instructor, in the sense of having been devised by one of your instructors. int main(int argc, char *argv[]) is one of only two standard variants for the signature of the entry point to a C program, and the only one of them that provides for handling command-line arguments.
How do I make it so I can pass the char straight from the command line?
Your program already receives character data from the command line, in the form of C strings. Why else do you think it makes sense to convert some of those strings to numbers via atoi()?
But what purpose do you think is served by applying atoi() to an argument that you do not expect to have the form of a numeric string? That is exactly what produces ...
If I pass a number in place of the operator (42 instead of *) then it works
... which is not what you want.
The argv array contains pointers to strings from which the characters of the command-line arguments can be read. If you want to read the characters of the arguments then do so directly. For example,
c = argv[3][0];
You would be wise, however, to first verify, by checking the value of argv, that at least 3 arguments were actually passed. Since argv also provides the program name, that means you're looking for argv to be at least 4.
The C standard defines only two valid declarations for the main function. They are int main(void), which is for programs that do not accept command-line inputs and int main(int args, char *argv[]) for programs that do.
In the second case int args will represent the number of elements in the C-string array char *argv[], which is an array of pointers to C-string representations of each of the arguments. As to the how do I pass a character to my program directly from the command line question, you are forced to use the string representation, but this is of little consequence.
Let's assume you compile the program and call it "calculator.elf"
If you were to run: ./calculator.elf 2 7 *
Your main function would be informed of these arguments by being passed a args with the value 4, and an argv[] with four pointers to C strings. The first one would be "./calculator.elf", the second "2", the third "7", and the fourth "*".
You are correct by looking in indices 1, 2 and 3 to look for your arguments because you have no need for the name of your program as an argument, and atoi("2") and atoi("7") should give you 2 and 7 respectively, but atoi("*") is not going to give you a useful result because "*" is not a numerical string.
Keep in mind that "*" is a string, not a single character, so argv[3] is going to be a pointer to a '*' character which is immediately followed by a '\0' character.
A valid check to see if you were passed "*" in argv[3] would be strcmp(argv[3],"*") == 0 or argv[3][0] == '*' && argv[3][1] == '\0', but atoi(argv[3]) should return 0 in accordance with the C specification for atoi in the case that it is passed a pointer to a non-numerical string

How to check arguments passed in command line in C?

I am writing a program in C for a basic calculator. I am trying to do this using what I have learned so far: printf() and scanf() functions. I am passing arguments into my program through the command line. I am assuming three arguments will be passed at a time which includes: first int, an operator, and the second int. I want to check if the second arg passed is an operator and then check if it's +,-,*... so on. Here is what I came up with:
int main(int argc, char **argv) {
scanf("%d %c %d", &a, &oper, &b);
if (oper != 43) {
printf("Error: Operator is not a +");
return(1);
}
}
So obviously, I have omitted a lot of the code and kept the relevant part. Here I am just checking if the oper is a +. The ASCII key is 43 so I thought this would work but no luck! Any ideas? (I would like to see if I can do this just with printf and scanf if possible)
EDIT: For example if 12 b 13 was entered, it should return the error above. Same goes for '10 +a 10' or '10 ++ 10'.
Firstly I would highly recommend looking at the man-pages for any C library function you come across, they have a lot of useful information. It seems like you are using scanf() improperly as it is not made to be used with command line arguments.
You can check for matches for a single character by comparing the argument like this:
if(argv[2][0] == '+') ...
(argv[0] is the program's file name).
If would would like to compare string you can use strcmp(). But for the operator example you can get away with just checking the first and second characters in the argument like this:
if(argv[2][0] == '+' && argv[2][0] == '\0') ...
What this does is compare the first two characters of the argument. It first checks for the '+' and then checks if that is the end of the string with by checking for the null terminator '\0'.
We can make the assumption that any argument has at least two characters, the visible character and a null terminator. Performing this on other strings has no guarantee of this however.
The other characters, specifically the numbers need to be converted from their respective ASCII values to integers. You can use atoi or strtol to do this, although atoi will most likely be easier for you.
As David C. Rankin pointed out, **argv is a double pointer which at a high level and in most cases you can treat as a double array. In C a string is actually just an array of type char, so what argv[2] is doing above is first accessing the third index of **argv, this is now de-referenced to a type char * where the string (char array) is located. This can then further be de-referenced by the [0] in argv[2][0] to look at the first char of the string.
Code example:
char **my_arrays = argv; // a array of arrays
char *array = *argv; // de-references to index 0 in argv
char *array = *(argv + 1); // de-references to index 1 in argv
char *array = argv[0]; // de-references to index 0 in argv
char *array = argv[1]; // de-references to index 1 in argv
char first_char = *(*argv) // the first char of the first array of argv
char first_char = *(argv[0]) // the same as above
char first_char = argv[0][0] // the same as above
A side note. All strings in C should end in a null terminator which can be represented by NULL, 0, or '\0' values. This will represent the end of the string and many C functions rely on this to know when to stop.
Also NULL is technically a C macro, but you don't need to treat it any differently than 0 because it literally just expands to 0.
It's char **argv. As Some programmer dude said, you should reread your book/tutorial.
scanf doesn't read arguments. It reads from stdin.
Arguments are of type char* and are stored in argv. To convert these arguments to integers, use atoi or strtol (preferably strtol). See this for more info.
If you want to read from stdin using scanf, that is fine, and what you have will work as long as you instead input the data into stdin, and not as command line arguments.

How do I deal with segfault ? C language

Suppose we are given a task to write a function that add 2 numbers.
#include <stdio.h>
int main(int argc, char ** av) {
int a = atoi(av[1]);
int b = atoi(av[2]);
add_and_print(a, b)
return 0;
}
It works fine untill I pass following code:
./a.out
Just passing empty strings. Then it writes the following:
1495 segmentation fault (core dumped)
Could you please explain what is the problem and how do I deal with it ?
argc contains the number of arguments provided to the program, and if you don't check it then you might get a segfault trying to read from argv. You can display an error message and exit if there aren't enough arguments:
if (argc < 3) {
puts("Please provide 2 numbers as command line arguments.");
return 1;
}
How do I deal with segfault
The segfault happens because of a bug in your code.
So, you prevent it happening in the first place, by not writing buggy code.
In general though, the segfault makes it easy to find out exactly what bug triggered it: just run your program under the debugger, and it will stop exactly where the segfault occurs.
Could you please explain what is the problem
In this code:
int a = atoi(av[1]);
the expression av[1] is only legal if there are at least two elements in array av (since we start indexing at zero). If there is only one element, this code attempts to read beyond the end of the array.
Since the array is based on the command-line arguments, you must check it. You need to do this for all inputs from users, files, even other parts of your own code. Don't just assume the user did what you expected (or the file contained what you expected, or the caller passed the right values). This is a bug.
if (argc >= 2) {
// now it is safe to refer to av[1]
a = atoi(av[1]);
}
You have to do something similar for av[2], for the same reason.
A common solution might be instead
int main(int argc, char **argv) {
if (argc < 3) {
printf("Syntax: %s a b\n"
"\n"
"Two integer arguments are required.", argv[0]);
return -1;
}
int a = atoi(argv[1]);
int b = atoi(argv[2]);
add_and_print(a, b)
}
I'm just assuming that argc is at least 1, and that argv[0] is the name of the program. You can check this too if you want perfectly portable code.
Note that you might also want to check whether the arguments are really integers.

CS50 Pset 2- ceasar cipher

It isn't showing what I want it to show that is the ciphered version of the input text but instead symbols, as I guess, looks kinda like a '?' comes out as the output in the terminal. could anyone help me in finding what I missed or did wrong?
#include <cs50.h>
#include <stdio.h>
#include <string.h>
int main(int argc, string argv[])
{
if (argc == 2)
{
string ptext = get_string("plaintext: ");
int key = (int) argv[1];
printf("ciphertext: ");
for (int i = 0, n = strlen(ptext); i < n; i++)
{
printf("%c", (( ptext[i] + key ) % 26);
}
printf("\n");
}
else
{
printf("Invalid input. \n");
}
}
I expect the output of 'hello' to be 'ifmmp' but instead, it doesn't.
This code is wrong:
int key = (int) argv[1];
argv is an array of string, which in CS50 is nothing more than an obfuscated char * pointer.
Per 5.1.2.2.1 Program startup of the C standard:
The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters:
int main(void) { /* ... */ }
or with two parameters (referred to here as argc and argv, though any names may be used, as they are local to the function in which they are declared):
int main(int argc, char *argv[]) { /* ... */ }
or equivalent; ...
So argv[1] is a char * pointer value, which you then assign to an int value. That's taking the address of some memory (say the value in argv[1] is 0xFF0403220020480C) and trying to stuff it into the likely 4 bytes of the int variable key (which in this case would be assigned the truncated value 0x0020480C.)
That's not what you appear to be trying to do.
(IMO, your problem here is a perfect example of why CS50's obfuscation of char * with the string type is a tremendously bad idea. You simply can't understand C without understanding pointers and NUL-terminated char strings accessed via a char * pointer, and the obfuscation that string does makes that harder.)
If you want to convert a string to a numeric value, you likely want something like strtol() (never use atoi() as it has no error checking and its use can invoke undefined behavior):
char firstCharNotConverted;
// set errno to zero as strtol()
errno = 0;
long key = strtol( argv[ 1 ], &firstCharNotConverted, 0 );
// if errno is now non-zero, the call to strtol() failed (per Linux man page)
// need to examine key and the contents of firstCharNotConverted
// to figure out why
if ( errno != 0 )
{
...
}
Proper headers omitted as an exercise for anyone trying to use this code ;-)
Note that I used long for key, as you can't do complete and proper error checking on strtol() if you cast the return value to int.
Error checking strtol() can be somewhat complex as the value returned (and assigned to key in the above code) can be any value and there are no values possible that aren't legitimate long values that strtol() can return to indicate an error, so for proper error checking you need to check the values of both errno and firstCharNotConverted to properly determine if an error did occur. The Linux man page states:
Since strtol() can legitimately return 0, LONG_MAX, or LONG_MIN
(LLONG_MAX or LLONG_MIN for strtoll()) on both success and failure,
the calling program should set errno to 0 before the call, and then
determine if an error occurred by checking whether errno has a
nonzero value after the call.
After that call to strtol(), you need to check if key is LONG_MIN, or LONG_MAX with errno equal to ERANGE for underflow or overflow, or if key is 0 you need to check the contents of firstCharNotConverted to determine why the conversion may have failed. Note that if key is zero and firstCharNotConverted isn't equal to argv[ 1 ], then input string was properly converted from zero.
Your Ceaser cipher implementation is also wrong:
for (int i = 0, n = strlen(ptext); i < n; i++)
{
printf("%c", (( ptext[i] + key ) % 26);
}
printf("\n");
will just print out characters with values from 0 through 25 - which aren't letters in the ASCII character set.
There a numerous Ceaser ciphers questions already posted here, so I'm not going to write the code. See Caesar's Cipher Code for one example question.
The problem is here printf("%c", (( ptext[i] + key ) % 26);. Specifically with % 26. It certainly looks exactly like the problem set:
More formally, if p is some plaintext (i.e., an unencrypted message),
pi is the ith character in p, and k is a secret key (i.e., a
non-negative integer), then each letter, ci, in the ciphertext, c, is
computed as
ci = (pi + k) % 26
wherein % 26 here means “remainder when dividing by 26.”
But, the pset goes on to say:
think of A (or a) as 0, B (or b) as 1, …, H (or h) as 7, I (or i) as
8, …, and Z (or z) as 25.
The problem is that the character ptext[i] is the ascii value of the letter, not the "alphabet index".
Perhaps review the spoiler in the pseudocode section of the Lab, particularly #5:
Iterate over each character of the plaintext:
If it is an uppercase letter, rotate it, preserving case, then print out the rotated character
If it is a lowercase letter, rotate it, preserving case, then print out the rotated character
If it is neither, print out the character as is
You may find this walkthrough (from an earlier iteration of the course) helpful.

behaviour of pointer ; char ** argv

when I was testing double pointer behaviour I got a result that I don't understand well.
==> code 1 :
int main (int argc , char **argv)
{
if(*argv+1 ==NULL)
{
printf("NULL pointer \n");
exit(0) ;
}
else
{
printf("test double pointer[] : %s \n ",*argv+1);
}
return(0);
}
====> result 1
root#root:/home/aa/test# ./geip 1255
test double pointer[] : /geip
root#root:/home/aa/test#
===> code 2 :
int main (int argc , char **argv)
{
if(*argv+9 ==NULL)
{
printf("NULL pointer \n");
exit(0) ;
}
else
{
printf("test double pointer[] : %s \n ",*argv+9);
}
return(0);
}
==> result 2 :
root#root:/home/aa/test# ./geip 1255
test double pointer[] : 55
root#root:/home/aa/test#
==> result 3 :
root#root:/home/aa/test# ./geip
test double pointer[] : ELL=/bin/bash
root#root:/home/aa/test#
it seems that printf display from n th word (1 and 9)
how we can explain this behaviour of pointer ?
You're using it wrong.
*argv+1 will be interpreted as (argv[0])+1 and since argv[0] is "./geip" you get "/geip".
*argv+9 will be interpreted as (argv[0])+9 but since argv[0] only has length 6 the outcome is undefined.
In your case, argv is probably stored as :
. / g e i p \0 1 2 5 5 \0
0 1 2 3 4 5 6 7 8 9 10 11
Which explains why +9 gets you "55"
But you should really forget that because it's never going to be useful! This is undefined behavior and should not ever be used.
char **argv is a pointer to a char * (sometimes referred to more simply as a string). You dereference this pointer when you do *argv. The result of this dereference is a char * or in other words it is the address of a char. When you do addition with the result, your code is computing a new address. So, for example while *argv would be the address of the first character in your string, *argv+1 is the address of the second character in your string.
When you add in a number that is longer than the length of your string you are stepping out of "safety". Remember that C will let you do pointer arithmetic that takes you past the end of your string. In your second example you are asking printf to go 9 bytes past the start of *argv and print characters from there to the next \0 (or NULL) byte. You are effectively reading arbitrary memory from your program's process space, which explains what is being printed.
There are actually more than one problem.
please, please, do not work as a root. Just don't.
your syntax (*argv + 9) means literally: "defeference argv and move pointer 9 chars", and really, if you move 9 characters from ./geip 1255 you'll arrive to 55. So either use argv[i] (i = 1..N denotes argument index) or if you want to do it the hard way, you must add parentheses: *(argv + i).
try to format your code better - it will be more readable not only for stackoverflow guys, but also for you.
For example when you run ./geip a b c 123:
argv[0] is string holding program name - ./geip
argv[1] is string holding the first argument - a
argv[2] is string holding the second argument - b
argv[3] is string holding the third argument - c
argv[4] is string holding the fourth argument - 123
argv[5] is NULL as argc will bw 5 (see comments)
argv[>5] is not a good idea, because there are no more arguments. So you better check argc to see how many arguments there are.
You just do a pointer arithmetics:
**argv is a pointer to list of pointers
*argv is a head of the list
//char **argv is given from outthere
char *p;
p = *argv; // the same as "p = *argv[0]"
for (int i = 0; i < 100) {
printf("Next: %s\n", p+i);
}
Try to run it and see the dump of memory, from head of the list to next 100 bytes.

Resources