I have been writing a program for CS50 (caesar) that is supposed to use a key given by the user in the command line to encrypt a message. The program is supposed to test the key given for a number of conditions. I want my program to use return values to stop running when the command line argument given is invalid.
Code below: (I have not finished it, so far it only checks for the command line input)
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int char_repeat(string key);
int key_check(string key);
int main(int argc, string argv[])
{
string key = argv[1];
for (int i = 0; i < strlen(key); i++)
{
char c = toupper(key[i]);
// conditions for key (string) written in argv[1]
if (argc < 2 || strlen(key) != 26 || c < 65 || c > 90)
{
printf("Usage: ./substitution key\n");
return 1;
}
}
char_repeat(key);
printf("this should not print if invalid...");
return 0;
}
int char_repeat(string key)
{
// check for repeated characters in key
for (int i = 0; i < strlen(key); i++)
{
for (int j = i + 1; j < strlen(key); j++)
{
if (toupper(key[i]) == toupper(key[j]))
{
printf("Usage: ./substitution key\n");
return 1;
}
}
}
printf("this should not print if invalid...");
return 0;
}
When I give 'invalid' inputs such as
$ ./sub AB*DEFGHIJKLMNOPQRSTUVWXYZ
$ ./sub ABC
$ ./sub ABCABCABCABCABCABCABCABCAB
$ ./sub ABCDEFGHIJKLMNOPQRSTUVWXYZ-
The code works perfectly by printing "Usage: ./substitution key\n" skipping my test print message, "this should not print if invalid...".
YET, and this is the problem, if I run ./sub without giving it a second argument. That is,
$ ./sub
I get an error message
segmentation fault (core dumped)
In the code itself I use the condition (argc < 2) in one of the if statements so shouldn't it work as all the other conditions and print "Usage: ./substitution key\n" ? I know segmentation fault is an error with trying to access memory that shouldn't be accessed or some other type of memory corruption, I just don't see how this relates to my program. I would imagine it is a problem the way I am using return.
Thank you in advance!
A segmentation fault is an error caused by attempting to access memory that does not belong to you.
When you run your program without providing additional arguments by using the command ./sub the code in the program tries to access argument 2 which does not exist, so you do not have access to that memory.
string key = argv[1];
Instead, you should check before you try to access that memory that arguments have been passed in.
if(argc < 2)
{
printf("Usage: ./substitution key\n");
return 1;
}
string key = argv[1];
Related
So I've been working on this code for about a day now and finally managed to create the file without an error message popping up. As soon as I run it, though, this pops up:
Segmentation fault (core dumped)
`
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
int k=atoi(argv[1]);
if (argc!=2)
{
printf("Usage: ./caesar key\n");
return 1;
}
else if (argv[1]<0)
{
printf("Usage: ./caesar key\n");
return 2;
}
else if (argv[1]== NULL)
{
printf("Usage: ./caesar key\n");
return 3;
}
string plaintext= get_string("plaintext: ");
printf("ciphertext: ");
for(int i=0; i<strlen(plaintext); i++)
{
if(plaintext[i]>='a' && plaintext[i]<='z')
{
printf("%c", ((plaintext[i]-97+k)%26)+97);
}
else if(plaintext[i]>='A' && plaintext[i]<='Z')
{
printf("%c", ((plaintext[i]-65+k)%26)+65);
}
}
printf("\n");
}
`
Can anyone tell me what I'm doing wrong here?
Haven't really tried anything to resolve this yet.
Even without seeing where it core dumps, I can see a number of issues with the code.
First thing is that you try to dereference argv[1] before checking how many parameters there are. i.e.
int k=atoi(argv[1]);
comes before
if (argc!=2)
{
printf("Usage: ./caesar key\n");
return 1;
}
If you run your program without putting in a value for the offset, this will cause a segmentation fault because C runtimes put a null pointer after the pointer to the last argument. So if you just typed ./caesar on the command line, argc is 1 and argv[1] is NULL.
This is the most likely cause of your crash.
This line:
else if (argv[1]<0)
doesn't really make any sense. You're comparing a pointer to 0. In many (all?) implementations, pointers behave like unsigned integers under comparison, so no pointer is ever less than zero.
This line
else if (argv[1]== NULL)
Is redundant if you have already checked that argc is two. Because, if argc is two argv[0] and argv[1] are guaranteed to point to strings.
On an unrelated note, I'd say that you should never do what that cs50.h header file does, which is have a typedef of something to char *. It just obfuscates what a string is.
I've written a program that allows me take a command argument of 26 alpha chars to be used as a key at a latter stage of my program to encipher text.
To do this, I had to (a) verify user input alpha chars (b) chars are 26 and (c) none of them repeat.
I decided to put this all in one function(as you'll see below).
This has been successful.
How ever, when I was trying to check whether the key is indeed 26 chars (in my main function) using an else if condition else if (argc != 2 || (is_valid_key(argv[1]) != 1 || is_valid_key(argv[2]) == 2)) I thought that (is_valid_key(argv[1]) != 1 may be useless here sinced I used it in if.
When I did this(regardless of my command argument) my program returned Segmentation fault (core dumped) and I am trying to understand why.
The line causing the problem is else if in my main function.
for context:
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int is_valid_key(string verify);
int main(int argc, string argv[])
{
// check whether the user passed a valid command line argument
if (argc != 2 || is_valid_key(argv[1]) == 1)
{
printf("Usage: ./substitution key\n"); //re-prompt user if they didn't enter a digit
return 1;
}
else if (argc != 2 || is_valid_key(argv[2]) == 2))
{
printf("Key must only contain alphabetic characters.\n");
return 1;
}
string plain_text = get_string("plaintext: ");
printf("%s\n", plain_text);
}
int is_valid_key(string verify)
{
int i;
for (i = 0; i < verify[i]; i++)
{
char ch = verify[i];
if (!isalpha(ch))
{
return 1; //return for alpha
}
}
if (i != 26)
{
return 2; //return for 26 chars
}
for (int a = 0; verify[a] != '\0'; a++)
{
for (int b = 0; b < a; b++)
{
// if 2 characters match, the key is not valid
if (verify[a] == verify[b])
return 3; // return for no repeat
}
}
return 4; //return all correct
}
The code is not checking the correct argument in main().
It's testing is_valid_key(argv[2]) but in your test case of only a single command-line parameter:
# ./main qwertyuiopasdfghjklzxcvbnm
There is only:
argv[0] which points to the string "./main" (or whatever your executable is called)
argv[1] which points to the string "qwertyuiopasdfghjklzxcvbnm"
The referenced argv[2] does not exist, and references "off the end" of argv, hence the segfault.
Changing this line to:
else if (argc >= 2 || is_valid_key(argv[1]) == 2) // <<-- HERE
{
printf("Key must only contain alphabetic characters.\n");
return 1;
}
will fix it.
Segmentation fault means that you are trying to access a memory location which you shouldn't have. The indexing of arguments starts from 0. And there should be only 2 command-line arguments in this program. So, you are trying to access the index 2 which is out of bounds currently. So, your program exits saying 'Segmentation Fault'.
I'm taking a intro CS course (CS50x) and came across a slight problem.
Why is this C code producing a segmentation fault when there is only argc[1]?
It should print the statement. Why won't it print the error?
Thanks in advance!
int main(int argc, string argv[])
{
//HOUSEKEEPING
//Get/Validate Key
string key = argv[1];
int finalKey = atoi(key) % 26;
while (argc != 2)
{
for (int i = 0; i < strlen(key); i++)
if (!isdigit(key[i]))
{
printf("Usage: ./caesar key\n");
return 1;
}
else
{
printf("Usage: ./caesar key\n");
return 1;
}
//...
argc is an integer argument that keeps track of the number of arguments in the command line, there is no argc[1], there might be an argv[1] if you provide a second command line argument, otherwise trying to read from a non existing argv[x] amounts to undefined behavior, and is the likely culprit of the segmentation fault you are experiencing.
In light of that information you'll notice that the while statement makes little sense, argc remains unchanged so the loop will either never execute, or will be infinite if argc is not 2 (and the program doesn't crash).
The if-else is also fishy, it will always print the message and return, if the character is a digit or is not a digit alike.
Notwithstanding the goal of the program, a syntactically correct code should look more like this:
int main(int argc, string argv[])
{
if (argc == 2)
{
string key = argv[1];
int finalKey = atoi(key) % 26; //*
for (int i = 0; i < strlen(key); i++)
{
if (!isdigit(key[i]))
{
printf("Usage: ./caesar key\n");
return 1;
}
}
}
else
{
puts("Wrong number of arguments, usage: ./caesar key\n");
}
}
* Note that atoi is quite unsafe, consider using strtol. And you can operate directly in argv[1] i.e. int finalKey = atoi(argv[1]) % 26;
I think you are not clear about the operation of argc and argv. argc indicates the number of parameters passed to the main function. At least it will receive one argument (argv[0], which will be the name of the compiled C code). In addition, it will receive all the parameters that you pass through the command line.
In summary, the correct way to do it would be:
if (argc >=2){
string key = argv[1];
}
Otherwise, you will have a segmentation fault when you try to access the argv[1] value.
This segment of code is meant to check if a user has entered only one numeric command-line argument, and return an error code of "1" if this is not the case. I have the code set up so that it first checks if argc is anything other than 2. Unfortunately, I am still receiving Segmentation Faults if no command line argument is entered, and I'm not sure why this code doesn't catch a null amount of command line arguments.
I tried moving the "if (argc !=2)" formula above the entire "for" statement to try and catch the command line argument issue right from the beginning, but I received the same result.
My question is, why am I receiving a Segmentation Fault when no command line argument is provided, and what am I missing to ensure the program doesn't Seg Fault with no command line argument?
Due to course policy, I will only be providing the segment of code in question.
#include <unistd.h>
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
// add'l variables //
int k = atoi(argv[1]);
for (int i = 0; i < strlen(argv[1]); i++)
{
if (argc != 2)
{
printf("Please enter only 1 command-line argument.\n");
return 1;
}
else if (!isdigit(argv[1][i]))
{
printf("Usage: ./caesar key\n");
return 1;
}
}
// add'l code //
Error as shown in Terminal
You must first check the argc before using the argv[1], because argv[1] may not have a valid pointer if argc < 2. A corrected version of your code could be like that:
#include <stdio.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
int i = 0;
if (argc != 2) {
printf("Please enter only 1 command-line argument.\n");
return 1;
}
while (isdigit(argv[1][i]))
++i;
if (argv[1][i] != '\0') {
printf("Usage: ./caesar key\n");
return 1;
}
return 0;
}
I don't understand why it isn't working. It complains when there are 3 or more args but not when there is only the one Vigenere arg. I have looked at other people who had same problem and they said this worked to resolve.....not sure what I am missing here. When I run ./vigenere, I get a segmentation fault. It works normally with 2 arg like ./vigenere bard and complains when extra arguments are given like ./vigenere bard dfads.
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main(int argc,string argv[])
{
string sKeyWord = argv[1];
int iKeyLength = strlen(sKeyWord);
int iKey[iKeyLength];
string sPlainText = "";
int counter = 0;
int iAccept = 0;
do
{
if(argc != 2) // <-----this should work whats wrong?
{
printf("Invalid argument! Please enter program name and keyword.\n");
return 1;
}
else if(argv[1])
{
for(int i = 0; i < iKeyLength; i++)
{
if (!isalpha(argv[1][i]))
{
printf("Invalid entry, please use letters only.\n");
return 1;
}
else
{
iAccept = 1;
}
}
}
}while(iAccept == 0);
for(int i = 0; i < iKeyLength; i++)
{
iKey[i] = toupper(sKeyWord[i]) - 65;
}
sPlainText = GetString();
int iPlainText = strlen(sPlainText);
for(int j = 0; j < iPlainText; j++)
{
if(!isalpha(sPlainText[j]))
{
printf("%c",sPlainText[j]);
counter++;
}
if(islower(sPlainText[j]))
{
printf("%c",((((sPlainText[j] - 97) + iKey[(j - counter)%iKeyLength])%26)+ 97));
}
if(isupper(sPlainText[j]))
{
printf("%c",((((sPlainText[j] - 65) + iKey[(j - counter)%iKeyLength])%26)+ 65));
}
}
printf("\n");
return 0;
}
I'd rewrite the top, argument handling section of your program like this.
int main(int argc, char **argv)
{
if (argc != 2)
{
fprintf(stderr, "Usage: %s key\n", argv[0]);
return 1;
}
char *sKeyWord = argv[1];
int iKeyLength = strlen(sKeyWord);
int iKey[iKeyLength];
for (int i = 0; i < iKeyLength; i++)
{
if (!isalpha(sKeyword[i]))
{
fprintf(stderr, "%s: Invalid character '%c' in key; please use letters only.\n",
argv[0], sKeyword[i]);
return 1;
}
iKey[i] = toupper(sKeyWord[i]) - 'A';
}
…your code to read the text to be enciphered and encipher it, etc…
The key point is to check that there is an argv[1] before trying to do anything with it. I eliminated a do { … } while (…); loop because the argument isn't going to change on a second iteration. That allows the iAccept variable to be eliminated. Note that errors are reported on standard error, not standard output. Also notice that the messages are preceded by the program name (argv[0]). A 'Usage' message is often the best way to report a problem; it's a simple reminder to those who run the program what's required. Note too that the error message for the alphabetic check reports the erroneous character; that helps people see what the program thinks is wrong.
This is more or less what the comments were suggesting should be done.
I've not reviewed the enciphering code; there could be undiagnosed problems in that too. There are many related questions on SO that would provide you with answers to any such problems, though.