How to identify what is causing the Segmentation fault - c

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.

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

CS50 Caesar segmentation fault explanation

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.

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.

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.

Check whether an input string consists out of specific characters -> Floating point Number

I am an absolute beginner in (c) programming and I have to find a solution for the following problem:
I have to parse command line arguments to floats in a struct. This works quite well using argc, argv and atof. But is there an way to check, whether the user used valid "float-characters" (0-9 and .) in his input?
atof only returns its error 0.0 when there isn't any number at the beginning of the string to parse. If the user writes for example ./programname 4,5, atof would still write 4 into my float...
I would have to check every single submitted character and in addition, set a counter to check, whether the user used more than one .. How can I achieve this efficiently?
EDIT:
Thank you vor your answers! My program is intended to convert only strings that consist of integers from 0 to 9 and a .. I forgot to mention that! I now try to write a function, that compares the input string to a string containing the valid chars using one for and one while loop. As I only want to check the second and the third argmunent of argv (argv[1] and argv[2]), I use a second for loop outside the rest to achieve that...
You can use sscanf and a dummy character to see if the complete string was converted or not, as in this answer:
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char* argv[])
{
char dummy;
float f;
if(sscanf(argv[1], "%f%c", &f,&dummy) != 1)
printf("Input error.\n");
else
printf("%f\n", f);
}
Live demo here

Resources