I am working through a programming assignment. I had the key validation working within main but decided to try to make it a separate function. I do not understand functions very well yet so I am unable to see where I am going wrong. Whenever I run the program, I always just get "Key is Valid" even when I know it's not. As I said, the program was running fine in main.
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
int validate (int c, string v[]); //prototpe validate function
int main (int argc, string argv[])
{
int validate (int argc, string argv[]); //run validate for argc and argv
printf("Key is valid\n"); //if valid, print message
}
int validate (int c, string v[])
{
//Validate that only one Command Line Argument was entered
if (c != 2) //Check the number if entered commands at execution
{
//If more than one, print error message
printf("Key must be the only Command Line Argument\n");
return 1; //Return false
}
//Validate that Key length is 26 characters
if (strlen(v[1]) != 26) //Don't forget null 0
{
printf("Key must contain exactly 26 characters\n");
return 1; //Return false
}
//Validate that all Key characters are alphabetic
for (int i = 0, n = strlen(v[1]); i < n; i++)
{
//Check each element of the array
if (isalpha(v[1][i]))
{
continue; //Continue if alphabetic
}
else
{
//if non-alphabetic, print error code
printf("Key must contain only alphabetic characters\n");
return 1; //Return false
}
}
//Validate that each Key character is unique
for (int x = 0, z = strlen(v[1]) - 1; x < z; x++)
{
//Create a second loop to compare every element to the first before incrementing the first
for (int y = x + 1; y < z; y++)
{
//Cast array elements to int, check if each element equals next element in array
if (v[1][x] == v[1][y])
{
printf("Key must contain exactly 26 unique characters\n");
return 1;
}
}
}
return 0; //Key is valid, so return true
}
You are just declaring the function validate instead of running that and printing Key is valid unconditionally.
To run the function validate and print Key is valid only if it returns 0, the main function should be like this:
int main (int argc, string argv[])
{
if (validate (argc, argv) == 0) //run validate for argc and argv and check its response
{
// put printing inside if statement so that it runs only if the condition is true
printf("Key is valid\n"); //if valid, print message
}
}
Function declaration hints the compiler that I will return a particular data type and I will accept the given data types in the arguments section. For example,
int check(int a, int b); -> return type is int and the function will accept 2 integer parameters.
int mul(int a, float b); -> return type is int and the function will accept 1 integer parameter and 1 float.
void check(); -> returns nothing, accepts nothing
function calling is like your code is calling the function to execute.
int c = check(2, 3);
int b = mul(3, 0.12);
check();
Your function is returning some value. You have to get that value and execute rest of the code based on the value as below.
if (validate (argc, argv) == 0) {
printf("key is valid");
} else {
printf("key is not valid");
}
Related
I'm going to make a palindrome which should ignore spaces and special characters and should convert all uppercase letters in the string to lowercase. I have done everything, but when I run my program, neither of these two functions work. Convert uppercase to lowercase and ignore all non-uppercase letters. Could any of you help me to solve what is the problem?
#include<stdio.h>
#define SIZE 1000
#include <ctype.h>
#include<string.h>
// function that checks if it is a palindrome
int isPalindrome(char inputString[]) {
int l = 0;
int r = strlen(inputString) - 1;
while (r > l)
{
// will check all letters are equal to each other
if (inputString[l++] != inputString[r--]) {
return 0;
}// return 0 if not palindrome
}
// palindrome
return 1;
}
// function that ignores all non - letters
int no_special_characters(char inputString[])
{
char temp[SIZE];
int temp_index = 0;
int abc = 0;
int r = strlen(inputString);
for (int i = 0; i < r; i++)
{
char abc = inputString[i];
if (isalpha(abc) != 0)
{
temp[temp_index++] = abc;
}
}
temp[temp_index] = '\0';
return isPalindrome(temp);
}
// function that converts uppercase letters to lowercase
void to_lower(char inputstring[]) {
int length = strlen(inputstring);
for (int i = 0; i < length; i++)
{
if (isupper(inputstring[i]))
inputstring[i] = tolower(inputstring[i]);
else if (islower(inputstring[i]))
inputstring[i] = toupper(inputstring[i]);
}
return 0;
}
int main(void) {
int try_again = 1;
int end_run = 0;
while (try_again == 1) {
int try_again;
char inputString[SIZE] = "";
printf("Enter a string to check if it is a palindrome!\n");
//Scans the input string.
scanf_s("%s", &inputString, SIZE);
//Sends the string to the isPalindrome function. //If the return value is 1(true), the if statement is executed, otherwise the else statement.
if (isPalindrome(inputString)) {
printf("That is a palindrome!\n");
}
else {
printf("This is not a palindrome!\n");
}
printf("Do you want to try again: 1 for yes 0 for No?");
scanf_s("%d", &try_again);
//Changes the value of running depending on whether you want to continue or not.
if (try_again != 1) {
end_run = 0;
}
}
return 0;
}
1 - Don't use scanf
You shouldn't use scanf to read input from the console, especially in a loop. I'm not an expert in scanf_s in particular, but that whole family of functions can cause weird bugs if you don't know how they work. I recommend you read this article, it explains it better that I can.
2 - Your loop doesn't work
You are defining try_again in function scope and then redefining it in block scope: they are two different variables.
Your code is essentially this:
int main(void) {
int try_again = 1;
while (try_again == 1) {
int try_again;
scanf_s("%d", &try_again);
}
}
Which will run forever, since the while loop is checking the first variable, while you are assigning to the second one. It should look more like this:
int main(void) {
int try_again = 1;
while (try_again == 1) {
// int try_again;
// ^^ Don't refefine - use the one already defined
try_again = some_function_to_read_input();
}
}
3 - to_lower doesn't actually convert to lowercase
It converts uppercase to lowercase and lowercase to uppercase. This means "Aa" becomes "aA", which is not a palindrome even though it should be by your definition.
The function doesn't need to return anything, since it changes the string in place. You can simply call it like this:
int isPalindrome(char inputString[]) {
to_lower(inputString);
// Rest of code
}
4 - You aren't calling no_special_characters
You simply aren't calling it. Either you do it in your main, something like:
// main
if (no_special_characters(inputString)) {
// Rest of code
}
Or you change it to return the modified string and call it from inside isPalindrome:
void no_special_characters(char inputString[]) {
// Remove all special characters, in place
}
int isPalindrome(char inputString[]) {
no_special_characters(inputString);
to_lower(inputString);
// Rest of code
}
I'm following along with cs50x and in problem set 2. This is the idea I had for solving the Caesar problem. I'm yet to implement the key idea due to the fact that it won't print out the word. I'm new to arrays and have searched a bit about why this is occurring. I think that I'm overcomplicating the code and could just use the string given by the user instead of transferring it to a function but now that I've started the idea I want to know why it isn't working and if there is a way to make it work. When ran, the program should accept a command line of a single number, if it has no command line it should fail, if the number is negative it should fail, if it is not a number it should fail and if it has more than 1 argument it should fail. Thanks
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
string cipher(string word, int key);
int main(int argc, string argv[])
{
// Checks whether the user inputted only 1 argument
if (argc == 2)
{
// Convert argv to an int
int key = atoi(argv[1]);
string plainText = get_string("plaintext: ");
// Use function to return the (soon to be encrypted) string
string cipherText = cipher(plainText, key);
// Print for how long the word is
int n = strlen(plainText);
for (int i = 0; i < n; i++)
{
// Print the char of the array based upon the iteration of the loop which runs for however long the word is
printf("%c", cipherText[i]);
}
printf("\n");
// If input is not a positive integer then it will fail
if (key < 1)
{
printf("Usage: ./caesar key\n");
}
}
else
{
// If user inputted too many or no inputs then it will fail
printf("Usage: ./caesar key\n");
}
return 0;
}
string cipher(string word, int key)
{
// Find the length of the word in order to set the size of the array
// This is so that both strings, the word inputted and the word to return are the same to add to
int n = strlen(word);
string cipherText[n];
// Loop through the input word
for (int i = 0; i < n; i++)
{
// If char[i] is a letter then copy that letter into ciphertext
if (isalpha(word[i]))
{
cipherText[i] =& word[i];
}
else
{
cipherText[i] =& word[i];
}
}
// Return the array which, for example the input word is nobody
// Return array[n, o, b, o, d, y]
return cipherText[0-n];
}
The issue is that you are attempting to copy the address of the "word" character array characters into the associated cipher text array element which will print out unknown characters (noted in the above comments).
// Loop through the input word
for (int i = 0; i < n; i++)
{
// If char[i] is a letter then copy that letter into ciphertext
if (isalpha(word[i]))
{
cipherText[i] = &word[i];
}
else
{
cipherText[i] = &word[i];
}
}
When I ran your program with the code like that, I indeed got a series of question marks.
#Una:~/C_Programs/Console/CypherCS50/bin/Release$ ./CypherCS50 12
plaintext: Hello
?????
I then revised it to perform a copy of character elements from "word" to "cipherText".
// Loop through the input word
for (int i = 0; i < n; i++)
{
// If char[i] is a letter then copy that letter into ciphertext
if (isalpha(word[i]))
{
cipherText[i] = word[i];
}
else
{
cipherText[i] = word[i];
}
}
Then, reran the program.
#Una:~/C_Programs/Console/CypherCS50/bin/Release$ ./CypherCS50 12
plaintext: Hello
Hello
Seeing that the same data came out, my guess is that you still need to work on the actual encryption bits. But, the crux of the issue was referencing the memory of the work array elements.
Give that a try.
This does not fix your OP issue, but addresses another issue and responds to our exchange in comments above. Here is a "skeleton" demonstrating how you might approach incrementally developing code for this task. The 'excessive' printf's serve to prove that things are proceeding as you want as the source code becomes more elaborate..
// Functions defined before use do not need to be prototyped
// do-nothing "skeleton" to be completed
string cipher(string word, int key)
{
printf( "In cipher with key %d and str '%s'\n", key, word ); // temp code confirmation
return word; // for now...
}
int main(int argc, string argv[])
{
if (argc != 2)
{
printf("Usage: ./caesar key\n");
return -1; // early termination
}
printf( "argv[1] = %s\n", argv[1] ); // prove functionality
int key = atoi(argv[1]);
printf( "key = %d\n", key ); // prove functionality
if (key <= 0)
{
printf("Key must be positive integer");
return -1; // early termination
}
string plainText = get_string("plaintext: ");
printf( "plain = %s\n", plainText ); // prove functionality
string cipherText = cipher(plainText, key);
printf( "cipher = %s\n", cipherText ); // prove functionality
return 0; // All done!
}
I'm trying to create a program that accepts cmd line arguments to encipher a plaintext!
The program must accept one cmd line argument after its name when making it and this would be the key which by the plaintext (only) alphabetical characters are rotated by this key (e.g. it's number is added to the real alphabet ASCII number resulting in another alphabet to be printed!
it is supposed to print an error message when one argument is present (e.g. here:/make encipher)
instead of here:/make encipher 12 <-- 12 = key!
I am getting a segmentation fault when running the program without the key argument, why?
This is the full code. I'm posting it because I need to learn where is my fault's exact location
and why is it triggered?!
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h> // To use atoi (converting a string to an int)
#include <ctype.h>
#include <string.h>
bool key_is_numb(string argv[]);
void encipher(string txt, int key);
int main(int argc, string argv[])
{
if (key_is_numb(argv) == false)
{
printf("Usage: ./caesar key\n");
return 1;
}
else
{
int key = atoi(argv[1]);
string plaintext;
if (argc == 2 && key > 0)
{
plaintext = get_string("plaintext: ");
encipher(plaintext, key); // A function that prints the ciphered text
return 0; // returns Zero as main return value which means "All good"!
}
else if (argc == 1 || argc > 2 || key <= 0)
{
printf("Usage: ./caesar key\n");
return 1;
}
} // End else.
} // End main()å func.
bool key_is_numb(string argv[])
{
int n = strlen(argv[1]);
for (int i = 0; i < n; i++) // checking element by element in the second string of the argv[] array of strings
{
if (isdigit(argv[1][i]) == 0) // if the entered string "key" contains chars other than digits.
{
return false; // break out of the if statement & the entire function key_is_numb()
// and return false as soon as a letter is encountered.
}
else
{
continue; // go up & start the next iteration for the for loop.
}
// if only digits encountered then this for loop will come to an end and exist from here.
} // End for loop
return true; // function exits and return boolean true from here.
} // End key_is_numb() func.
void encipher(string txt, int key)
{
printf("ciphertext: ");
for (int i = 0, n = strlen(txt); i <= n; i++) // strlen counts the number of elements in a string excluding '\0'
{
char c = txt[i];
if (isalpha(c))
{
if (isupper(c))
{
char m = 'A'; // This is a modifyer character equals to 'A' = 65 so that it is indexed # ZERO!
printf("%c", (c - m + key) % 26 + m );
//c = ((((int)txt[i] - 65) + key) % 26) + 65; // char c = 65 <-- 65 is an ASCII code equals 'A'
}
else if (islower(c))
{
char m = 'a'; // This is a modifying character 'a' = 97
printf("%c", (c - m + key) % 26 + m );
}
}// End if(alpha).
else
{
printf("%c", c);
}
} // End for().
printf("\n");
} // End encipher() func.
int n = strlen(argv[1]);
in key_is_numb() and
int key = atoi(argv[1]);
in main().
If you didn't enter a key argument, argv[1] as equal as argv[argc] is a null pointer as stated in C17, §5.1.2.2.1/2.
Any attempt to access its data is undefined behavior and probably caused the segmentation fault.
Well you are assuming that argv[1] is defined in key_is_numb. However, in C and C++, the second parameter of the main function contains command line arguments. Which, in your case will be the name of the binary as the first element, then any other arguments. This is why when you are running the program without arguments, it will segfault, as there are no argument to put in argv, and no default value either.
You should always check the size of argv, by using the number stored in argc, before trying to read anything in argv.
Your segmentation fault comes from this line int n = strlen(argv[1]);, but I'd highly suggest you to learn to use debugger software like valgrind, which if the program has been compiled with debug flag will tell you the exact line.
Other debugger are really useful too, so you should learn to use them, as they usually report this kind of errors.
Your code asumes there is always an argv[1]. You should check argc which tells the number of arguments. For example:
int main(int argc, string argv[])
{
if (argc < 2) {
printf("Key required\n");
exit (1);
}
This code is not finished, but I'm having two problems.
First, when the key has non-alphabetic characters in it, the error message "Key must only contain letters" is printed as many times as there are non alphabetic characters ex: if there is four numbers in the key, the error message will print four times.
Second, the error message is not printed when the key contains repeated letters, instead the program acts as though the key is valid.
Thank you for any advice.
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[])
{
if (argc != 2)
{
printf("Usage: ./caesarsub key\n");
return 1;
}
// Check key contains only letters
for (int i = 0, n = strlen(argv[1]); i < n; i++)
{
if (!isalpha(argv[1] [i]))
{
printf("Key must only contain letters\n");
}
// Check letters are not repeated
else
{
int count = 0;
for (int m = i + 1; n < strlen(argv[1]); m++)
{
if (argv[1][i] == argv[1][m])
{
count++;
}
}
if (count > 0)
printf("Key must not contain repeated characters\n");
}
}
// Check key contains 26 letters
if (strlen(argv[1]) != 26)
{
printf("Key must be 26 letters\n");
}
}
Look carefully at the difference between the routine that checks for the existence of the argument (ie if (argc != 2)) and the routine that checks for alpha only (ie if (!isalpha(argv[1] [i]))). One exits the program (which is to see "returns from the main function") and one does not.
The other problem looks like a typo here for (int m = i + 1; n < strlen(argv[1]); m++). The for loop doesn't execute because n < strlen... is never true.
This is the error I am getting:
test.c:110:21: error: expected expression
else if(isupper(p_i))
^
1 error generated.
In an else if statement towards the end of the code—"else if(isupper(p_i))"—the error is generated.
I have commented above this 'else if' statement. Please let me know what's wrong. Thank you.
#include <stdlib.h> // The library which contains the 'atoi()' function
#include <stdio.h> //
#include <cs50.h> // typedef char *string; and GetString()
#include <string.h> //
// 'argv[]' is an array of strings. (Fun fact: A string is an array of characters.)
// 'argc' is the integer variable which stores the number of strings that are in 'argv[]'.
int main(int argc, string argv[])
{
// VARIABLE DECLARATIONS
int k; // Integer variable for the non-negative, encryption key
string plaintext; // Variable to store the information to be encrypted
int n; // Integer variable for the string length
string ciphertext = NULL; // Variable to store the encrypted information
// This loop analyzes the command-line argument(s): We need exactly one argument (i.e. argc = 2)
if (argc > 2)
{
printf("\n");
printf("Too many arguments. Please try again.\n");
return 1;
}
else if (argc < 2)
{
printf("\n");
printf("This program requires that you provide an argument. Please try again.\n");
return 1;
}
else if (argc == 2)
{
k = atoi(argv[1]);
if (k == 0 || k < 0)
{
printf("\n");
printf("Invalid input: encryption key needs to be a non-negative integer.\n");
return 1;
}
}
// Prompt the user for a string input to be encrypted:
printf("\n");
printf("Please enter the information to be encrypted:\n");
plaintext = GetString();
n = strlen(plaintext);
printf("n = %d \n", n);
// We need to implement Caesar's Cipher algorithm:
// But first, we select for alphabets only by using the 'isalpha()' function:
for (int i = 0; i < n; i++)
{
int p_i = plaintext[i];
int isalpha(int p_i);
if (isalpha(p_i))
{
int islower(int p_i);
if (islower(p_i))
{
printf("Caesar's algorithm for the lower case goes here.\n");
}
int isupper(int p_i);
//-----------------------------------------------------------
// THE FOLLOWING else if STATEMENT IS THE SOURCE OF THE ERROR
//-----------------------------------------------------------
else if(isupper(p_i))
{
printf("Caesar's algorithm for the upper case goes here. \n");
}
}
else
{
for (int j = 0; j < n; j++)
ciphertext[i] = plaintext[i];
}
}
// Program terminates
return 0;
}
You have a function prototype between the if and the else:
if (islower(p_i))
{
printf("Caesar's algorithm for the lower case goes here.\n");
}
int isupper(int p_i);
//-----------------------------------------------------------
// THE FOLLOWING else if STATEMENT IS THE SOURCE OF THE ERROR
//-----------------------------------------------------------
else if(isupper(p_i))\
The else block must follow immediately after the if block. If you had put the line int isupper(int p_i); pretty much anywhere else (before the first time you use it), you would not have this error. Even better, you should load this prototype via #include <ctype.h> at the top of your file.
int isupper(int p_i);
//-----------------------------------------------------------
// THE FOLLOWING else if STATEMENT IS THE SOURCE OF THE ERROR
//-----------------------------------------------------------
else if(isupper(p_i))
Remove this declaration: int isupper(int p_i);.
Use the correct #include directive at the top of your source file to declare isupper function:
#include <ctype.h>
You cannot use any statement between if and else statement. Example -
if()
{
//some code
}
//some Code ---> This is wrong //this should not be in between if and else
else
{
//some code
}
Your function prototypes:
int islower(int p_i);
int isupper(int p_i);
do not belong within the main() function (or any function) - though that's technically legal which is why the first is not causing a problem.
However, they cannot live within an 'if / else' construct - you have the second sandwiched between an if clause and an else clause.
The best solution is to put them at the head of the file, or better just use:
#include <ctype.h>
to get the relevant header file with the prototypes in included, then remove the prototypes from the function.