CS50 Caesar segmentation fault explanation - c

I can compile this code with no issues, the idea right now is just to test that I can successfully enter the cipher key(command line argument) without any issues before moving on. I was having no luck with finding out why my code would always return a segmentation fault, then I saw a tip to put a '*' before argv[1] in the 4th line, and that seems to solve all my issues. Can someone please explain to me why this is?
int main(int argc, string argv[]) // command line argument for the cipher key
{
int key;
if (argc == 2 && isdigit(*argv[1])) //the line in question
{
key = atoi(argv[1]); // convert the digit string into an integer
}
else
{
printf("Usage:./caeser key\n");
return 1;
}
printf ("%i\n",key);
}

then I saw a tip to put a '*' before argv[1] in the 4th line, and that seems to solve all my issues. Can someone please explain to me why this is?
This means, before you saw that tip, it looked like this:
isdigit(argv[1])
You are mixing types here.
argv is an array of char*. This means, argv[1] is of type char*.
But isdigit is expecting an argument of type int. You should get some compiler warning for that.
The function expects to get a value that is in range of char or EOF. Passing a pointer is very likely to provide some value way out of range.
If you add the * you dereference your char pointer and take the first character of that string. That is what is expected by isdigit and it works as it should.

Related

Why do command line arguments generate segmentation fault when accessed?

I am getting segmentation faults when trying to read command line arguments char-by-char.
I have the following code:
int main(int argc, char** argv)
{
int i,j;
for(i=1;i<argc;i++)
{
for(j=0;argv[i][j]!=0;j++)
{
printf("%c",argv[i][j]);
}
//printf("%c",argv[i][0]);
}
}
This code runs, but when I uncomment the line printf("%c",argv[i][0]); I get a segmentation fault.
Why exactly is this considered access to unallocated memory and how am I supposed to parse command line arguments without checking their content?
Specifically, I want to know if the argument is of the form: <+->e<0-9A-F> meaning plus or minus followed by 'e' followed by a single hex digit. How would one go about this in c?
The condition for your inner loop is incorrect:
argv[i][j]!="\0"
You're comparing a single char against a string constant. That string constant decays into the address it's stored at, which is what's actually being compared against the elements of the argument string. As a result, because you're not correctly checking for the terminating null byte, you read past the end of the string. Doing so triggers undefined behavior which in your case causes you code to sometimes crash and sometimes not.
What you want here is to check for the value 0:
argv[i][j]!=0

Code supposed to check for non digits doesn't work in c [duplicate]

This question already has an answer here:
How to identify what is causing the Segmentation fault
(1 answer)
Closed 2 years ago.
I have this code that's supposed to check if the "key" ie argv(1) but it's not working. "Segmentation fault"
Here it is:
string plain; //this is in advance for my for loop
//was unable to find a way to actually get the thing to handle a non-numeric key :(
int main(int argc, string argv[])
{
if (argc == 2 && isdigit(argv[1]))
{
plain = get_string("plaintext:");
printf("ciphertext:");
}
else
{
printf("Usage : ./caesar key\n");
return 1;
}
Thanks for help, if you need any more details please ask!
Let me start by mentioning that all of your problems are caused by CS-50, a horrible, harmful class that will make you a bad C programmer.
In reality the format of argv is char* argv[], an array of character pointers. By doing isdigit(argv[1]) you pass a char* to the function, not a character as it expects. Here a standard C compiler will complain along the lines of "expected character, got pointer" since the code isn't valid.
If the purpose was to check the first digit of the string, you should have done isdigit(argv[1][0]). Also note that isdigit resides in ctype.h so you must include that header. If the purpose was to check if all characters in the string were digits, you will need a loop.
argv[1] has the type char * while the function isdigit expects an argument of the type char. So this if statement
if (argc == 2 && isdigit(argv[1]))
is incorrect.
Either you need to write a separate function that checks that each character in the string pointed to by the expression argv[1] represents a digit. Or you could use the standard function strtol and check that the conversion of the string to integer performed by the function was successful.

How to identify what is causing the Segmentation fault

My code's aim is to take in 2 command line arguments (inclusive of programme name), and to print out responses as shown based on the given 2nd command line argument. If the command line argument is an integer, the user's input is accepted or "Success"and if it as anything else (e.g. a string or more than one command line argument), it will be Null and the error message will be shown. This is for CS50 caesar for those who are familiar
My Code is as follows:
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
int main(int argc,string argv[])
{
char *x = argv[1];
if (argc == 2 && isdigit(x))
{
printf("Success\n");
}
else
{
printf("Usage: ./caesar key\n");
}
}
The code compiles but I am given a segmentation fault. I am aware segmentation fault is the programme trying to access something outside of the allocated memory for the array (correct me if I am wrong) but I did specify argv[1].
The isdigit takes as an argument, an int, that contains a character code as an unsigned character.
Unfortunately isdigit is often implemented as a macro, and written in such a way that you do not get a warning if you pass in the wrong kind of data. You're passing in a pointer to a character, which is not a single character. The first character of the first command line argument is x[0]. However this must be converted to unsigned char.
Therefore these are wrong
isdigit(x)
isdigit(x[0])
What you could use to check if the first character of the first argument is a digit is
isdigit((unsigned char)x[0]))
but in the end what you want is not to check if any of these are digits, but to convert the string to an integer, and see if an error occurs - use strto(u)l for x and see if it succeeded; then check the range of the resulting value.

Command line argument condition pset2 caesar

I'm just started with a course for learning C, and am bumping in a problem with command line arguments. The assignment is this (there is more, but this is the part about the command line argument at the start):
- Your program must accept a single command-line argument, a non-negative integer.
- If your program is executed without any command-line arguments or with more than one command-line argument, your program should print an error message of your choice and return 1.
- You can assume that, if a user does provide a command-line argument, it will be a non-negative integer (e.g., 1). No need to check that it’s indeed numeric.
So I came up with this code:
#include <stdio.h>
#include <cs50.h>
#include <string.h>
int main(int key, string plain[]) {
if (key < 0 || plain[key] > 1)
{
printf("error\n");
return 1;
}
else
etc...code continues.
Now I've tried several things, but I'm running into a wall.The compiler doesn't want to accept the if-condition I came up with, saying there is an error with comparison between pointer and integer which refers to the bold condition on the list of the assignment. So I understood that the argv part of the command line argument is the array of strings that the user put in. So my thought was to tell the compiler that when the user gives more than one string it should give an error message, so I wrote "plain[key] > 1)". Or is my understanding of command line arguments completely off here? Thanks.
plain[key] access the key element of plain array of string pointers (argv).
The size of that array is expressed by key (argc).
So what you want is
if (key > 1)
{
//..
}
Moreover plain last element is key-1, 'cause is 0 based index.
You misunderstood the purpose of the arguments to main. The first int argument (usually named argc) is the number of items in the array argument.
And the array argument (usually called argv) contains all the arguments to your program (including the executable name) as text.
So if your executable is called foo, and you invoked it as foo 1 a bar, the arguments to main will be as follows:
int argc == 4
char **argv => {"foo", "1", "a", "bar"}
So if your program must accept a single argument, it must hold that argc == 2 and argv[1] is the argument, that you must convert to a number from a string.

Interesting observations of argc, argv, and its usage

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.

Resources