So, I am working on a Linux based command line utility, that has to accept a few flags, and I have noticed some interesting behavior. I will be posting testing code that I was using outside of the main utility. I was using this code, so I did not have to alter the actual utility until I had working code that I could just insert. So here is the code that I have been fiddling with:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
while(--argc && (*++argv)[0] == '-')
putchar('*');
printf("\n%s\n", argv[0]);
}
Please ignore the fact that all this program does is print an asterisk and print its own name when invoked with one argument -b. The printing of the asterik was just to show that the loop ran once. So, I run this in a terminal as "./test -n", and I expected the output to be:
*./test
Much to my surprise, the output was:
*-b
I have a working theory of what the statement
(*++argv)[0] is doing, but I am still a little hazy on it. My assumption is that it steps across the array of pointers looking at the first character in each string pointed to, (*++argv)[0] is now dereferencing *argv[0] or element zero of the first argument string.
So, basically I have three questions:
What exactly is that statement doing?
Why can I not get back to argv[0] or argv[0][0], no matter what I try?
Is storing the value at the address pointed to by argv[0] in another char *, this is the only way that I have been able to access that value at this point, the normal way around this?
I am really confounded by this at the moment and have tried everything that I can think of to work this out. At one point I had a loop that would print the alphabet, I don't know what part of memory the program was accessing. The most interesting permutation was pulling sshid variables from somewhere.
Thank you all in advance for your help with this.
++argv changes argv to point to the next argument.
Try something like
int i = 0;
while(--argc && argv[++i][0] == '-')
Which maintains a separate index, instead of overwriting argv.
Or
char** argp = argv;
while(--argc && (*++argp)[0] == '-')
which works the same as the original, except it changes a copy of argv instead of the original.
Related
I'm trying to input only one filename from command line. Then I want to use an If statement to compare the filename to 4 different names I'm expecting to see. If I don't get one of the 4 expected file names then I need to print it back to the user with what was inputted and then exit the program safely.
int main(int argc, char *argv[])
{
....
}
I've been trying a lot of different methods of getting this done, but I just can't figure it out. I was thinking maybe the way I take the input argument is wrong. Any help would be greatly appreciated.
-edit
I just want to be clear I don't want you to be a leech and solve the question I have for me. Just at least point me in the correct direction. I can't figure how to make a for loop work with the filename.
for(argv == "UnexpectedFile.csv"){
printf("this is an unexpected file: %c", argv[1]);
}
You could use strcmp() from string.h to compare strings like
strcmp(argv[1], "unexpectedfile.csv");
It returns 0 when the strings are equal.
If you have the 4 file names in as an array of strings, say expectedFile, do
for(i=0; i<4 && strcmp(argv[1], expectedFile[i])!=0; ++i);
If the value of i is the total number of file names (ie, 4) after this loop, argv[1] is an unexpected file.
Otherwise, value of i would be the index of the file name string in the expectedFile array.
The command line arguments are stored in the 2-dimensional char array argv.
argv[0] would be the name of the executed file. The arguments you give start only from argv[1] onwards.
argc will have the total number of command line arguments including the file name stored in argc. So if there are 'no' arguments, argc would be 1.
In your case the file name is the only argument, so argc would be 2.
You must check if argc is at least 2 before you access argv[1] to prevent the program from accessing argv[1] when it isn't there.
(You do not ask for complete code solution (and do not provide enough of your code for that). So here are the pointers in the right direction you want.)
The comparison you do with a simple pointer == pointer does not really compare the content of the strings. That is what e.g. strcmp () is for, as proposed by #user3629249.
And in order to print out what was given as commandline argument, you should use "%s\n".
And in order to set up a for loop, you will have to do the syntax right: for(init assignment; condition; step operation).
(If you need more help, you will have to provide more details on what behaviour you get and what you do not like about it. Currently your code looks more like compiler errors, which you did not quote, than a problem with the behaviour goal...)
I was just wondering if you could clear something up for me.
Let's have some example code to explain my question:
#include <stdio.h>
int main(void)
{
char test[100];
printf("%s",test);
return 0;
}
If I am not totally mistaken, this should output randomly either some character that was at this memory address before I declared it or nothing if it was empty like in a virtual environment. So, this is my understanding. The memory held before I put something in is understood as a char and written to the terminal. For instance ascii 'a' = 97 = 01100001. That's why it outputs 'a'. Could have been anything else. Or nothing. And then it stops.
But if I put 'a' in the first position and then print it like this:
test[0] = 'a'
printf("%s",test);
It will output 'a' and additionally to that some character or nothing and then stop.
This is how I understand arrays: An array is a pointer to the first address and the brackets are a dereferences of the address after adding the number times sizeof(type) to it.
So, in that case, the random 01100001 (Ascii 'a') found in the memory in the first example should be indistinguishable for printf from the deliberately placed 01100001 (Ascii 'a') in the second example. Yet, when I run printf, I don't get 100 random outputs. I get one. And I don't assume random fields are in general set to '\0'.
Which means, my understanding must be wrong somewhere. Please help me understand where I make my mistake.
It doesn't, it's undefined behavior. Your program just accidentally prints the un"expected" value.
#include <stdio.h>
int main(void)
{
char test[100];
printf("%s",test);
return 0;
}
You can't expect the code above to do anything predictable, it might print something, it could segfault, there is no way to predict what will actually happen because the behavior of such program is strictly undefined.
So I'm trying to create a C program where you must input the password on the command line, like
./login password1
And if the password is password1, it'll say something. If not, it prints another message. This is the code I have now:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
if (argc < 2) {
printf("usage: %s <password>\n", argv[0]);
}
char pass = "password";
if (argc == pass) {
printf("Right\n");
} else {
printf("Wrong\n");
}
}
But it wont work.
char pass = "password";
You're trying to assign a string to a char. That won't work! Instead, you need need to declare pass as a char[] like this:
char pass[] = "password";
Next problem:
if(argc == pass)
argc is the number of command line arguments passed to your program (including the program name as the first). What you want is argv, which contains the actual arguments. Specifically, you probably want argv[1].
You can't just go argv[1] == pass as that compares the location of the two strings. To compare strings, you need to use strcmp(). This function compares two strings and returns 0 if they're equal (there's good reason for that, but leave it for now). The former is like comparing two houses by checking if they have exactly the same street address; the latter is like comparing the houses with each other brick-by-brick. (sniped from #caf)
So the line becomes:
if (strcmp(argv[1], pass) == 0)
Put those fixes together and it should work. Please also work on improving the indentation of your code. It'll make it much easier to read, not only for others but yourself in a few weeks time.
You're comparing argc - the count of command line arguments - with the "password" string pointer.
For a start, you need to use argv[1] instead of argc. You also need to use a suitable strcmp function rather than just comparing the pointers.
Finally, inputting passwords via the command line is usually a bad idea due to security considerations. On many systems the command line may be visible to other users (eg via the "ps" command).
This question already has answers here:
Pass arguments into C program from command line
(6 answers)
Closed 6 years ago.
I don't know what to do! I have a great understanding of C basics. Structures, file IO, strings, etc. Everything but CLA. For some reason I cant grasp the concept. Any suggestions, help, or advice. PS I am a linux user
The signature of main is:
int main(int argc, char **argv);
argc refers to the number of command line arguments passed in, which includes the actual name of the program, as invoked by the user. argv contains the actual arguments, starting with index 1. Index 0 is the program name.
So, if you ran your program like this:
./program hello world
Then:
argc would be 3.
argv[0] would be "./program".
argv[1] would be "hello".
argv[2] would be "world".
Imagine it this way
*main() is also a function which is called by something else (like another FunctioN)
*the arguments to it is decided by the FunctioN
*the second argument is an array of strings
*the first argument is a number representing the number of strings
*do something with the strings
Maybe a example program woluld help.
int main(int argc,char *argv[])
{
printf("you entered in reverse order:\n");
while(argc--)
{
printf("%s\n",argv[argc]);
}
return 0;
}
it just prints everything you enter as args in reverse order but YOU should make new programs that do something more useful.
compile it (as say hello) run it from the terminal with the arguments like
./hello am i here
then try to modify it so that it tries to check if two strings are reverses of each other or not then you will need to check if argc parameter is exactly three if anything else print an error
if(argc!=3)/*3 because even the executables name string is on argc*/
{
printf("unexpected number of arguments\n");
return -1;
}
then check if argv[2] is the reverse of argv[1]
and print the result
./hello asdf fdsa
should output
they are exact reverses of each other
the best example is a file copy program try it it's like cp
cp file1 file2
cp is the first argument (argv[0] not argv[1]) and mostly you should ignore the first argument unless you need to reference or something
if you made the cp program you understood the main args really...
For parsing command line arguments on posix systems, the standard is to use the getopt() family of library routines to handle command line arguments.
A good reference is the GNU getopt manual
Siamore, I keep seeing everyone using the command line to compile programs. I use x11 terminal from ide via code::blocks, a gnu gcc compiler on my linux box. I have never compiled a program from command line. So Siamore, if I want the programs name to be cp, do I initialize argv[0]="cp"; Cp being a string literal. And anything going to stdout goes on the command line??? The example you gave me Siamore I understood! Even though the string you entered was a few words long, it was still only one arg. Because it was encased in double quotations. So arg[0], the prog name, is actually your string literal with a new line character?? So I understand why you use if(argc!=3) print error. Because the prog name = argv[0] and there are 2 more args after that, and anymore an error has occured. What other reason would I use that? I really think that my lack of understanding about how to compile from the command line or terminal is my reason for lack understanding in this area!! Siamore, you have helped me understand cla's much better! Still don't fully understand but I am not oblivious to the concept. I'm gonna learn to compile from the terminal then re-read what you wrote. I bet, then I will fully understand! With a little more help from you lol
<>
Code that I have not written myself, but from my book.
#include <stdio.h>
int main(int argc, char *argv[])
{
int i;
printf("The following arguments were passed to main(): ");
for(i=1; i<argc; i++) printf("%s ", argv[i]);
printf("\n");
return 0;
}
This is the output:
anthony#anthony:~\Documents/C_Programming/CLA$ ./CLA hey man
The follow arguments were passed to main(): hey man
anthony#anthony:~\Documents/C_Programming/CLA$ ./CLA hi how are you doing?
The follow arguments were passed to main(): hi how are you doing?
So argv is a table of string literals, and argc is the number of them. Now argv[0] is
the name of the program. So if I type ./CLA to run the program ./CLA is argv[0]. The above
program sets the command line to take an infinite amount of arguments. I can set them to
only take 3 or 4 if I wanted. Like one or your examples showed, Siamore... if(argc!=3) printf("Some error goes here");
Thank you Siamore, couldn't have done it without you! thanks to the rest of the post for their time and effort also!
PS in case there is a problem like this in the future...you never know lol the problem was because I was using the IDE
AKA Code::Blocks. If I were to run that program above it would print the path/directory of the program. Example: ~/Documents/C/CLA.c it has to be ran from the terminal and compiled using the command line. gcc -o CLA main.c and you must be in the directory of the file.
Main is just like any other function and argc and argv are just like any other function arguments, the difference is that main is called from C Runtime and it passes the argument to main, But C Runtime is defined in c library and you cannot modify it, So if we do execute program on shell or through some IDE, we need a mechanism to pass the argument to main function so that your main function can behave differently on the runtime depending on your parameters. The parameters are argc , which gives the number of arguments and argv which is pointer to array of pointers, which holds the value as strings, this way you can pass any number of arguments without restricting it, it's the other way of implementing var args.
Had made just a small change to #anthony code so we can get nicely formatted output with argument numbers and values. Somehow easier to read on output when you have multiple arguments:
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("The following arguments were passed to main():\n");
printf("argnum \t value \n");
for (int i = 0; i<argc; i++) printf("%d \t %s \n", i, argv[i]);
printf("\n");
return 0;
}
And output is similar to:
The following arguments were passed to main():
0 D:\Projects\test\vcpp\bcppcomp1\Debug\bcppcomp.exe
1 -P
2 TestHostAttoshiba
3 _http._tcp
4 local
5 80
6 MyNewArgument
7 200.124.211.235
8 type=NewHost
9 test=yes
10 result=output
Why does the following have the effect it does - it prints a terminal full of random characters and then exits leaving a command prompt that produces garbage when you type in it. (I tried it because I thought it would produce a seg fault).
#include <stdio.h>
int main(){
char* s = "lololololololol";
while(1){
printf("%c", *s);
s++;
}
}
it was compiled with:
gcc -std=c99 hello.c
It will eventually seg fault, but before that it'll print out whatever bytes are in the same page. That's why you see random chars on the screen.
Those may well include escape sequences to change (say) the character encoding of the console. That's why you end up with gibberish when you type on the console after it's exited, too.
Because you have an infinite loop (while(1)), and you keep getting the current value of pointer (*s), and then moving the pointer one char forward (s++). This has the effect of marching well past the end of the string into "garbage" (uninitialized memory), which gets printed to the console as a result.
In addition to what everyone else said in regards to you ignoring the string terminal character and just printing willy-nilly what's in memory past the string, the reason why your command prompt is also "garbage" is that by printing a particular "unprintable" character, your terminal session was left in a strange character mode. (I don't know which character it is or what mode change it does, but maybe someone else can pipe in about it that knows better than I.)
You are just printing out what is in memory because your loop doesn't stop at the end of the string. Each random byte is interpreted as a character. It will seg fault when you reach the end of the memory page (and get into unreadable territory).
Expanding ever so slightly on the answers given here (which are all excellent) ... I ran into this more than once myself when I was just beginning with C, and it's an easy mistake to make.
A quick tweak to your while loop will fix it. Everyone else has given you the why, I'll hook you up with the how:
#include <stdio.h>
int main() {
char *s = "lolololololololol";
while (*s != '\0') {
printf("%c", *s);
s++;
}
}
Note that instead of an infinite loop (while(1)), we're doing a loop check to ensure that the pointer we're pulling isn't the null-terminator for the string, thus avoiding the overrun you're encountering.
If you're stuck absolutely needing while(1) (for example, if this is homework and the instructor wants you to use it), use the break keyword to exit the loop. The following code smells, at least to me, but it works:
#include <stdio.h>
int main() {
char *s = "lolololololololol";
while (1) {
if (*s == '\0')
break;
printf("%c", *s);
s++;
}
}
Both produce the same console output, with no line break at the end:
lolololololololol
Your loop doesn't terminate, so println prints whatever is in the memory after the text you write; eventually it will access memory it is not allowed to read, causing it to segfault.
You can change the loop as the others suggested, or you can take advantage of fact that in c, zero is false and null (which terminates all strings) is also zero, so you can construct the loop as:
while (*s) {
Rather than:
while (*s != '\0')
The first one may be more difficult to understand, but it does have the advantage of brevity so it is often used to save a bit of typing.
Also, you can usually get back to your command prompt by using the 'reset' command, typing blindly of course. (type Enter, reset, Enter)