0

I am very new to coding. Prior to starting CS50, I have had no experience in coding at all, so pardon me if my questions are trivial.

I am trying to write some code for 'Caesar' in Problem Set 2 and this is what I have come up with so far:

#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, string argv[])
{
    if (argc == 2 && isdigit(argv[1]))
    {
        printf("Success\n");
        return 0;
    }
    else
    {
        printf("Usage: ./caesar key\n");
        return 1;
    }
}

When I try to execute this code, it gives me a segmentation error as follows:

~/pset1/caesar/ $ make caesar
clang -ggdb3 -O0 -std=c11 -Wall -Werror -Wextra -Wno-sign-compare -Wno-unused-parameter -Wno-unused-variable -Wshadow    caesar.c  -lcrypt -lcs50 -lm -o caesar
~/pset1/caesar/ $ ./caesar 12
Segmentation fault

Below are a few questions I have regarding my code:

Firstly, what is a segmentation error? I believe it was not covered in lecture.

Secondly, may I know if it is "correct" to use the isdigit function in this way? I am trying to figure out if the 2nd string in the argv array is a digit. I also have a hunch that this is what is causing the so-called "segmentation error".

Thirdly, in lecture, the professor's source code stated that we need to return 0s and 1s, but I do not understand this concept clearly. Why, exactly, do we need to return values? Or rather, would there be any difference to my code if I deleted the "return 0" and "return 1" lines?

Thank you all in advance.

EDIT

Since posting this question, I have played around with my code a little and I have gotten rid of the segmentation error. However, my new code accepts alphanumeric arguments and I cannot figure out why.

My updated code is as follows:

#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, string argv[])
{
    if (argc == 2)
    {
        for (int i = 0, n = strlen(argv[1]); i < n; i++)
        {
            if (isdigit(argv[1][i]))
            {
                printf("Success\n");
                return 0;
            }
            else
            {
                printf("Usage: ./caesar key\n");
                return 1;
            }
        }
    }
    else
    {
        printf("Usage: ./caesar key\n");
        return 1;
    }
}

The terminal window shows the following:

~/pset1/caesar/ $ make caesar
clang -ggdb3 -O0 -std=c11 -Wall -Werror -Wextra -Wno-sign-compare -Wno-unused-parameter -Wno-unused-variable -Wshadow    caesar.c  -lcrypt -lcs50 -lm -o caesar
~/pset1/caesar/ $ ./caesar 1hey
Success

In addition to the three questions above which I still wish to be answered, I have three more:

Fourthly, why does my code overlook alphanumeric arguments? I thought that my for loop already checks for whether each character in the 2nd string of argv is a digit? How are letters still accepted then?

Fifthly, in my updated code, I am now trying to use the isdigit function and a for loop to check each character in argv[1]. May I know if my code is written correctly/logically? In particular, does "argv[1][i]" make sense, as in, does the program know that the [1] belongs to the name argv and that the [i] is a variable?

Lastly, how did I get rid of the segmentation error with the changes in my code?

Thank you all in advance again.

2
  • The isdigit loop will only check the first char. If it is a digit, it returns 0 and terminates the program. It shouldn't return at all for a digit. Even if it were supposed to return 0 for success, the code needs to check ALL the chars before returning instead of returning upon finding just one digit. You don't need an if/else construct here. All you need is if(!isdigit(...) ) error msg and return
    – Cliff B
    Jun 2, 2020 at 18:27
  • Thank you for your input! That indeed works too. I actually managed to get around this problem using the concept of counters. I counted for how many digits (say, x) and non-digits (say, y) there were. Then, x != 0 and y = 0 should be the only successful case, but perhaps your idea is more succint! :)
    – Ethan Mark
    Jun 3, 2020 at 2:45

1 Answer 1

1

Current operating systems allocate a portion of memory to each application. If an application tries to directly access a memory location that does not belong to it or an incorrect memory location or a protected memory area, the operating system will stop the application and generate an error (under Linux: Segmentation error).

The use of isdigit is not correct. Function Prototype of isdigit () int isdigit (int arg); Function isdigit () takes a single argument in the form of an integer and returns the value of type int.

Even though, isdigit () takes integer as an argument, character is passed to the function. Internally, the character is converted to its ASCII value for the check. Therefore when passing argv [1], a string (a pointer really), we have segmentation fault.

Third, if you look at main it returns an integer value: int main (); therefore it is customary to return 0 when the program ends correctly, and to return something other than zero if the program ends abnormally, we can use these values (1, 2, 3 ...) to represent different varieties of errors. I hope this clarifies your doubts.

EDIT:

        if (isdigit(argv[1][i]))
        {
            printf("Success\n");
            return 0;
        }

If you think about what this condition means a bit, the first time you find a digit you call printf(), and exit the program (that's the effect of return 0), without checking the rest, so you have to make sure to check all elements of argv.

EDIT 2:

The segmentation fault disappears when using the correct argument. To understand this you should know that it is an array of pointers, maybe it is a bit hasty in pset2, but this is what string argv [] is, this is really a double pointer, or a pointer to a pointer, char ** argv. Well, don't complicate yourself. From the command line we are going to write a series of arguments for the program, in this case two, and these arguments will be stored in argv, the first argument is the name of the program: argv [0], this is a string, the second argument is an integer, argv [1], which is also a string, so that each of its characters will be argv [1] [i]. I know, a little complicated.

EDIT3:

In antiquity (when I was born) it was common to write main(int argc, char ** argv) to indicate that the main program could accept arguments, this notation tends to disuse because it is unclear, or as Professor Malan writes, as syntactic sugar (although I also believe that there is a clear intention that his library cs50.h be considered a standard) int main(int argc, string argv []), so that the students are not struggling with pointers from the start of the course and now we write int main(int argc, char * argv []), to clearly indicate that argv is an array of character strings, which will store what we write by the console, of course it is not only used, but it is common in any program, such as example we can define our own array of pointers:

#define SIZE 7
char *str[SIZE] = {"Monday",
                "Tuesday",
                "Wednesday",
                "Thursday",
                "Friday",
                "Saturday",
                "Sunday"};

then str[0] = "Monday" etc..

5
  • Hi! Thank you for your reply. I think you have answered all of my doubts so far :) In your second edit, you used some asterisks i.e. char ** argv. May I know what those mean in code, if anything?
    – Ethan Mark
    Jun 2, 2020 at 14:43
  • You can read my edit3
    – MARS
    Jun 2, 2020 at 16:36
  • I see. Thank you for insightful comments :) It is definitely interesting to read about how coding has evolved over the years and not just learning how to actually code! I would like to clarify one last thing regarding the returning of values for main. So am I right to say that returning 0s for success and other integers for failures is more of a convention than anything else? In other words, if I did not specify in my code to return anything, the code would still run, just that it is "bad coding practice"?
    – Ethan Mark
    Jun 2, 2020 at 17:08
  • It is good practice to include return 0 at the end of the program, but it is not mandatory, most compilers will add it automatically at the end
    – MARS
    Jun 2, 2020 at 17:39
  • I see. Thank you so much for all your help! I think that would be all from me for now :)
    – Ethan Mark
    Jun 3, 2020 at 2:45

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .