C: Vigenere cipher compiles but segmentation error - pointers? - c

I'm getting a segmentation error somewhere in this code, though it compiles successfully. The code is to iterate repeatedly through a key to encipher a plaintext word. I'm assuming it's pointer-related but I don't know how to fix it.
Also: is that an appropriate way to express a negative for isalpha? It didn't throw an error. It did throw an error when I tried to use ==FALSE after it.
int main(int argc, char* argv[])
{
if ((argc<2) || (argc>2) || (!isalpha(argv)))//exits if non-alpha key or < or > than 2 command line inputs
{
printf("Please enter your single word key"); //prompts for appropriate key
return 1;
}
if(argc==2) // requires two command line inputs with non-integer key to continue
{
printf("plaintext:"); //Printed on terminal before word to be enciphered
char* word = GetString(); //Prompts user to enter word required to be enciphered
printf("ciphertext:");
char* key = (argv[1]); //key = initial user command line entry
int i = 0;
int k = 0; //defines characters in key
for(i=0; i<strlen(word); i++) //iterates through word entered by user as plaintext
{
if(isalpha(key[k]))
{
if(isupper(word[i])) //if original characters are uppercase
{
int cipher = (word[i] + key[k] -65) % 26 + 65;
printf("%c", cipher);
}
else if(islower(word[i])) //if original characters are lowercase
{
int cipher = (word[i] + key[k] - 97) % 26 + 97;
printf("%c", cipher);
}
else //for all other types of characters
{
printf("%c", word[i]);
}
}
else if(!isalpha(key[k]))
{
return 1;
}
if (word[i]==strlen(word)) //if reaches end of plaintext word
{
printf("\n"); //print a new line and exit
return 1;
}
else if(word[i]<strlen(word)) //if haven't reached end of word, increment k
{
k++;
}
if (key[k]==strlen(key))
{
k = 0;
}
}
printf("\n");
}
}

Related

Trying to write a loop to replace letters in a string with letters from a different string at the same index, but loop is ending early

I'm working on a program that is supposed to take a key as an input argument, and encrypt a user input word using this key.
The program should:
Ask the user for a plaintext word to encrypt
Standardize the letter case
Take each letter from the plaintext and find the index of this letter (A = 0, B = 1,...)
Look at the letter indexed at this location in the key string (input argument)
Assign this encrypted letter to a new sting called cypher
Print the new cyphertext string.
The code I'm using is this:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[])
{
//Check that key has 26 letters or end program
string key = argv[1];
if (strlen(argv[1]) != 26)
{
printf("Key must contain 26 characters\n");
return 1;
}
//Get plaintext
string plain = get_string("plaintext: ");
//Make key all letters upper case
for (int i = 0; i < plain[i]; i++)
{
if (islower(plain[i]))
{
plain[i] = plain[i] - 32;
}
printf("%c", plain[i]);
}
printf("\n");
//Encrypt
int index[] = {};
int cypher[] = {};
//Cycle through the letters in the word to be encoded
//printf("cyphertext: ");
printf("%c\n", key[79 - 65]);
for (int i = 0; i < strlen(plain); i++)
{
printf("index in key: %i\n", plain[i] - 65);
cypher[i] = key[plain[i] - 65];
printf("cypher: %c\n", cypher[i]);
}
printf("\n");
}
Everything executes fine until the fourth loop of the for loop that assigns the new values to the cypher string. When the program tries to set i = 4, I get the error Segmentation fault (core dumped)
I was expecting the last for loop to loop once for each letter of the input (e.g. input: hello; loops: 5), but I found that it stops at 4 and only outputs: 'HELL'.
I tried:
Words with 4 characters - executes the correct number of loops, but I still get Segmentation fault (core dumped) after the final loop
Words with 3 characters - executes fine, no error
Words with 5+ letters - Still loops 4 times before error
Please help!
The for loop should iterate from 0 to length of plain.
//Get plaintext
string plain = get_string("plaintext: ");
//Make key all letters upper case
for (int i = 0; i < strlen(plain); i++)
{
if (islower(plain[i]))
{
plain[i] = plain[i] - 32;
}
printf("%c", plain[i]);
}
//*** Must allocate memory for array
//Encrypt
int index[100] = {};
int cypher[100] = {};
//Cycle through the letters in the word to be encoded
//printf("cyphertext: ");
printf("%c\n", key[79 - 65]);
for (int i = 0; i < strlen(plain); i++)
{
printf("index in key: %i\n", plain[i] - 65);
cypher[i] = key[plain[i] - 65];
printf("cypher: %c\n", cypher[i]);
}
printf("\n");

Why does this program return a "?" symbol in C

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!
}

CS50 caesar.c - ciphertext printing out of ASCII code range

I'm going through CS50 (2021x version) of Caesar problem, and ran into problem. My program is printing outside of ASCII range (thanks to curiouskiwi over at discord for this hint). The error message says ":( encrypts "barfoo" as "yxocll" using 23 as key, output not valid ASCII text". Another one I'm having trouble is "world, say hello!", for same reason (Not valid ASCII text). Other ones are encrypting fine.
I've stepped through the debugger and found that 'letter' variable is sometimes becoming a negative integer like -119'/211', but can't figure out why that may be so. I expected to see positive value associated with an alphabet in ASCII. When this happens the letters will stop printing on the console.
If I type ./caesar 23 | cat -A and then give barfoo as plaintext, the cyphertext will come out as yxM-^IcM-^FM-^F$.
int main(int argc, string argv[])
{
// only 1 arugment, and positive argument only
if (argc == 2 && argv[1] > 0)
{
// check if each char of argument is digit
for (int i = 0, n = strlen(argv[1]); i < n; i++)
{
if (isdigit(argv[1][i]))
{
// do nothing
}
else
{
printf("Usage: ./caesar key\n");
return 1;
}
}
// change the key to how much letters should move over
int input = atoi(argv[1]);
int key = input % 26;
char letter;
// get the input
string text = get_string("plaintext: ");
printf("ciphertext: ");
for (int i = 0, n = strlen(text); i < n; i++)
{
if (isalpha(text[i])) // if it is an alphabet
{
if (islower(text[i])) // if it is lowercase
{
letter = text[i] + key; // add key to text[i]
if (letter > 122)
{
// loop around the alphabet
letter -= 26;
}
printf("%c", letter);
}
else // if it is uppercase
{
letter = text[i] + key; // add key to text[i]
if (letter > 90)
{
// loop around the alphabet
letter -= 26;
}
printf("%c", letter);
}
}
else // if it is not an alphabet
{
printf("%c", text[i]);
}
}
printf("\n");
}
else
{
printf("Usage: ./caesar key\n");
return 1;
}
}
You should use int letter instead of char letter.
This is because
letter = text[i] + key;
will overflow signed char for, say, 'z' + 23.

why I get the random output from the loop in c?

I try to solve the problem in pset2 of the CS50 lecture and when I run that code:
# include <stdio.h>
# include <string.h>
# include <cs50.h>
# include <ctype.h>
int main(int argc, string argv[])
{
// check if key is exists
if (argc != 2)
{
printf("%s", "usage: ./substitution key\n");
return 1;
}
else
{
// key
string ciphertext = argv[1]; // "VCHRPZGJNTLSKFBDQWAXEUYMOI"
int cipher_length = strlen(ciphertext);
string msg_error = "\0";
int x = 0;
if (cipher_length == 26)
{
int msg_num = 0;
for (int i = 0; i < strlen(ciphertext); i++)
{
if (isdigit(ciphertext[i]))
{
msg_num = 0; // false
}
else
{
msg_num = 1; // true
}
}
if (msg_num == 1)
{
string plaintext = get_string("plaintext: ");
int n = strlen(plaintext);
int a_letter = 'a';
char plaintext_cp[n]; // copy from the original text
char text[n];
strcpy(plaintext_cp, plaintext);
// to read the chars into plaintext
for (int i = 0; i < n; i++)
{
char lower_char = tolower(plaintext_cp[i]); // e.g -> d
if (lower_char >= 'a' && lower_char <= 'z')
{
int covert_to_ascii = lower_char; // 101
int index = covert_to_ascii - a_letter; // 101 - 98 = 4
if (islower(plaintext[i]))
{
text[i] = tolower(ciphertext[index]);
}
else
{
text[i] = toupper(ciphertext[index]);
}
}
else
{
text[i] = lower_char;
}
}
printf("ciphertext: %s\n", text);
return 0;
}
else
{
printf("%s\n", "key must only contain alphabetic characters.");
return 0;
}
}
else
{
printf("%s\n", "Key must contain 26 characters.");
return 1;
}
}
}
I get the output but also I get random output like this screen-shoot
I tried to check the condition if ciphertext[i] is '\0' but I get the same issue. can anyone explain what is going on and How I can solve this problem?
Looking at the screenshot you have provided, I do not think it is unintended output. The output comes out as should be, but, either while you were testing and CTRl+V'd into console, you did it too fast for the IDE and the string output followed behind it before going to a new line for a new input command; OR, it was just an accidental push onto the key and you haven't noticed. Otherwise it would have output that same (or if it is a flaw) another character every time you ran the command, but it seems to have only happened on the last command input.
(I have done this several times to myself when rushing or not paying attention)
The "printf" command under (// check if key exists) also has a %s (string pointer) but no string in the printf command, and isn't needed.
The last two printf's at the end of the file seem to have similar issues. The %s is not needed unless you have a string variable you plan on calling.

The non-alphabetic problem in Vigenere problem set

I just have done with Vigenere problem in CS50, but still, there's one wrong only, the non-alphabetic characters, when you write in plaintext anything without spaces, comma, any non-alphabetic, the program will run well, but if you wrote any non-alphabetic character, like space, the next character will take the wrong key, this my code :
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int main(int argc, string argv[])
{
// Make sure there is a command-line argment
if (argc != 2)
{
printf("Error\n");
return 1;
}
// Variables
int key[strlen(argv[1])];
string plaintext;
// Make sure the comman-line argment is Alphabets then make the key
for (int i = 0, n = strlen(argv[1]); i < n; i++)
{
if (!isalpha(argv[1][i]))
{
printf("Error 2\n");
return 1;
}
if (islower(argv[1][i]))
{
key[i] = argv[1][i] - 'a';
}
else if (isupper(argv[1][i]))
{
key[i] = argv[1][i] - 'A';
}
}
// Ask the user to write the message
plaintext = get_string("plaintext: ");
printf("ciphertext: ");
// Make sure the plaintext doesn't equal NULL
if (plaintext != NULL)
{
for (int i = 0, n = strlen(plaintext); i < n ; i++)
{
// Print in slower case
if (islower(plaintext[i]))
{
printf("%c", (((plaintext[i] + key[i % strlen(argv[1])]) - 'a') % 26) + 'a');
}
// Print in upper case
else if (isupper(plaintext[i]))
{
printf("%c", (((plaintext[i] + key[i % strlen(argv[1])]) - 'A') % 26) + 'A');
}
// Print the non alphabetic
else if (!isalpha(plaintext[i]))
{
printf("%c", plaintext[i]);
}
}
// Print a new line
printf("\n");
}
}
The problem is because you are using the same index for the plaintext and the key in this
for (int i = 0, n = strlen(plaintext); i < n ; i++) loop. The key will advance one position every time plaintext does. Obviously that is not what you want. You need to manage the key index independently of the plaintext index within that loop.
Suggest you rewatch the walkthrough and perhaps write out an example much the way Zamyla does the panda example. And it's never too soon to learn how to use debug50. If I recall correctly, there is a short for it in Week 2.
CS50x has a stack forum dedicated to questions and answers about CS50x and the psets.

Resources