Why am I getting segmentation error in a Caesar cipher program? - c

I'm trying to write a program that uses Caesar's algorithm to cipher a string input. I'm a beginner to C but I can understand basic codes. So to cipher the text I wrote this code, but when I enter the input, I get an error that says
Segmentation fault (core dumped)
I tried to do some debugging by removing the else condition at the end and the program kind of worked for short inputs of 2-3 letters
Can someone please help me with this issue?
I'm using the CS50's header only to get the string in the first place.
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main(int argc, char * argv[])
{
char name[] = "";
strcat(name, argv[1]);
int key = atoi(name);
string plaintext = get_string("plaintext: ");
int length = strlen(plaintext);
char ciphertext[] = "";
for(int i = 0; i < length; i++)
{
int skipCount = 0;
if(isalpha(plaintext[i]))
{
while(skipCount < key)
{
char tmp = (char) ((int) plaintext[i] + 1);
if(isalpha(tmp))
{
ciphertext[i] = tmp;
skipCount++;
}
else
{
if (isupper(plaintext[i]))
{
tmp = 'A';
skipCount++;
}
if (islower(plaintext[i]))
{
tmp = 'a';
skipCount++;
}
}
}
}
else ciphertext[i] = plaintext[i];
}
printf("%s\n", ciphertext);
}

What you need to understand about C, is that it does not automatically allocate memory.
You have to it your self!
This line:
char name[] = "";
creates an array of size 1, which holds a single character - the "null" character = '\0';
It signifies an empty string.
You can not copy any larger string in to it, because all strings in C must have a null character at the end, so there isn't enough room for even a single readable character.
As a beginner, you would need to decide what the maximum length of the string you want will be, and declare the array to be of proper size:
char name[255];
This is one example that can hold up to 254 characters, plus the terminating null character.

Related

C - Changing a string seems to change unrelated strings in the same way (CS50)

Here is the code:
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main(int argc, string argv[])
{
string key = argv[1];
string keyupper = argv[1];
string keylower = argv[1];
if (argc != 2) //makes sure there is exactly 2 arguments (the program executable and the key)
{
printf("Please input a key.\n");
return 1;
}
else if (strlen(key) != 26) //makes sure the key is exactly 26 letters
{
printf("Please make sure the key is 26 unique letters.\n");
return 1;
}
for (int i = 0; i < 26; i++) //the loop to make the uppercase key
{
keyupper[i] = toupper(keyupper[i]);
}
for (int i = 0; i < 26; i++) //the loop to make the lowercase key
{
keylower[i] = tolower(keylower[i]);
}
Essentially, I want to make a very basic encryption using a key entered while executing the program, it needs to contain 26 unique letters. I want to create two arrays, an uppercase and a lowercase one, to make everything else much easier for me, but when running this code, all keys become either uppercase or lowercase depending on which loop is created last (in this case, they all become lowercase). Even key gets changed to lowercase even though it's used only once as a declaration. Everything else works but this.
This is for the CS50 course so functions such as toupper() are included in libraries.
This is my first ever question so sorry if it's worded poorly. Thank you!
Code failed to copy the string contents
[Talking about string here, not the type string]
In C, a string is "... is a contiguous sequence of characters terminated by and including the first null character."
Code only copied pointers and not the string contents.
string key = argv[1];
string keyupper = argv[1];
string keylower = argv[1];
Comment discussion indicates OP now sees why code is in error.
Repaired code
//#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
// Avoid naked magic numbers, instead define them
#define KEY_N 26
int main(int argc, string argv[]) {
// Do not code argv[1] until after argc check
// string key = argv[1];
// string keyupper = argv[1];
// string keylower = argv[1];
if (argc != 2) {
printf("Please input a key.\n");
return 1;
}
char *key = argv[1];
// else if (strlen(key) != 26)letters
if (strlen(key) != KEY_N) {
printf("Please make sure the key is 26 unique letters.\n");
return 1;
}
char keyupper[KEY_N + 1];
char keylower[KEY_N + 1];
// for (int i = 0; i < 26; i++)
for (size_t i = 0; i < KEY_N; i++) {
// keyupper[i] = toupper(keyupper[i]);
keyupper[i] = toupper((unsigned char) key[i]);
}
keyupper[KEY_N] = '\0';
...

If i input too many characters my program either has a 'mremap_chunk(): invalid pointer' or a 'realloc(): invalid old size'

my program was built as a test to input as many sentences as the user want (until he enters -1), and then concatenate all sentences (\n included). If i input some characters is fine, but if i input more then 25 characters i have the two errors listed above, i tried simulating what would happen in paper and i canĀ“t find the problem, help is appreciated, thanks in advance.
The code is displayed below:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
char *s = malloc(1), *sentence = malloc(0);
int sSize = 0;
printf("Insert sentences, press '-1' if you want to exit:\n");
do
{
fgets(s,100,stdin);
if(strcmp(s,"-1\n") != 0)
{
sSize += strlen(s);
sentence = realloc(sentence, sSize * sizeof(char));
//s[strcspn(s, "\0")] = '\n';
strcat(sentence, s);
}
}while(strcmp(s,"-1\n") != 0);
printf("==================sentence================\n");
printf("%s", sentence);
return 0;
}
This is a classic buffer overrun problem:
s = malloc(1) - s now points to a one-character buffer.
fgets(s,100,stdin); - reads up to 100 characters into s - which is a one-character buffer.
EDIT
Here's a version which works and doesn't use a separate "sentence buffer":
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
const char *terminator = "-1\n";
char *sentences = malloc(100);
char *pNext_sentence;
printf("Insert sentences, press '-1' if you want to exit:\n");
*sentences = '\0';
do
{
sentences = realloc(sentences, strlen(sentences)+100);
pNext_sentence = sentences + strlen(sentences);
fgets(pNext_sentence, 100, stdin);
} while(strcmp(pNext_sentence, terminator) != 0);
*(sentences + (strlen(sentences) < strlen(terminator) ? 0 : strlen(sentences) - strlen(terminator))) = '\0';
printf("==================sentences================\n");
printf("%s", sentences);
free(sentences);
return 0;
}
You must use reallocate memory with realloc before using fgets, which, in your case, reads 100 bytes.
Your string has the initial size of 1.

Want to pass non-alphabetic characters to an array in C but getting a segmentation fault

I really appreciate any help/advice provided! Am noob.
This program is supposed to take text and cipher it using a key the user inputs. So when the user runs the program with their 26 letter key, the program asks them for plaintext (Whatever they want ciphered), then the program should output the ciphered text. I left out the validation & append functions in this question.
=====
My question is, in the for loop, alphabetic letters work fine. The issue is when there are NON-ALPHABETIC chars, the program returns a segmentation fault.
Does anyone have any idea why this happens, or how I can fix this? In the meantime I will try ctype.h's "ispunc" "isspace" etc etc. Just wondering if there is a BASIC other way to do it that is shorter than typing that all out...we haven't learned pointers yet.
Let me know if any further info is needed...
// test key: ./substitution JTREKYAVOGDXPSNCUIZLFBMWHQ
// test 2: ./substitution VcHpRzGjNtLsKfBdQwAxEuYmOi
// test 3: ./substitution vchprzgjntlskfbdqwaxeuymoi
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int validatekey(int argc, string argv[]);
void append(char* s, char c);
int main(int argc, string argv[]) // argc = # of command line args (name of program & input), argv saves them both.
{
if (validatekey(argc, argv) == 0) //everything is fine
{
string key = argv[1]; //user's inputted key
int keylength = strlen(key);
string plaintext = get_string("plaintext: ");
string plainkey = "abcdefghijklmnopqrstuvwxyz";
int plength = strlen(plaintext);
char cipher[plength]; //outputted ciphertext
for (int i = 0; i < plength; i++)
{
char p = tolower(plaintext[i]); //make plaintext lowercase so things match up
char *where_is_p = strchr(plainkey, p);
int index = (int)(where_is_p - plainkey); // get index of letter in plainkey
char c = key[index]; //corresponding cipher index location
if (isalpha(plaintext[i])) // check if letter is ALPHABET first
{
if (isupper(plaintext[i])) // if current plaintext char is uppercase
{
//append(cipher, toupper(c));
printf("is upper");
}
else if (islower(plaintext[i])) // if it's lowercase
{
//append(cipher, tolower(c));
printf("is lower");
}
}
else //(isalpha(plaintext[i]) == 0 ) // if it's not a-z NOTE: SEGMENTATION FAULT
{
//append(cipher, plaintext[i]);
printf("not alpha");
}
}
//printf("ciphertext: %s\n", cipher);
}
}
My question is, in the for loop, alphabetic letters work fine. The issue is when there are NON-ALPHABETIC chars, the program returns a segmentation fault.
Does anyone have any idea why this happens,
strchr(plainkey, p); returns NULL when p not found in plainkey. Code below dies on pointer subtraction and out of range array index.
char *where_is_p = strchr(plainkey, p);
int index = (int)(where_is_p - plainkey);
char c = key[index];
or how I can fix this?
When where_is_p == NULL, perform the is-not-alpha code.
BTW, char cipher[plength]; is 1 too small to form a string of ciphered text.

C initials program returned "Segmentation fault"

I am working on a initials project where you enter a name and it prints the initials. When I try and combine the strings it returns Segmentation fault instead of the initials.
#include <stdio.h>
#include <stdlib.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
int main(void) {
printf("Name: ");
string name = GetString();
printf("\n");
int length = strlen(name);
string initials = "";
int arraylength = length + 1;
char string[arraylength];
string[0] = toupper(name[0]);
int count = 1;
for(int l = 1;l<=length;l++) {
if(name[l] == ' ') {
l++;
count++;
string[l] = toupper(name[l]);
}
}
count++;
string[count] = '\0';
for(int c = 0;c<=count;c++) {
strcat(initials, &string[c]);
}
printf("%s\n", initials);
}
That's why a string type would cause confusion, you make a pointer to a single char. And you then pass it to strcat() that's simply wrong.
A string, as expected by strlen() or strcat() or all str* functions, is not simple a char pointer which is what the type string in your code is.
In c a c-string is actually a sequence of bytes with the last byte being '\0', and it's not optional. You create a pointer to a single char and that is not the same as a string that I just described.
Any of the str* functions will try to find the '\0' but since you passed the address of a stack variable which is not an array, the behavior is undefined when any of these functions try to increment and dereference the passed pointer.
When you do understand how strings work in c you would see that using strcat() for the concatenation of many chunks of a large string together is not very efficient, you would also know that you just need to append a single char and that you can by simply using index notation, like
char string[8];
string[0] = 'E';
string[1] = 'x';
string[2] = 'a';
string[3] = 'm';
string[4] = 'p';
string[5] = 'l';
string[6] = 'e';
string[7] = '\0'; // The very necessary terminator

C Caesar Cipher error random string output

So I have been taking classes on C and one of the exercises was to program a caesar cipher program that both encrypts and decrypts. And when the input is "ab cd", the output should be "de#fg" but instead it outputs "de?g?". So my guess is the spacebar messes everything up. But also another error was found when I inputted "a" and it outputted "d?ad?". Thanks in advance.
#include <stdio.h>
#include <string.h>
void cipher(char plain_str[], char cipher_str[]);
void decipher(char cipher_str[], char decipher_str[]);
int main() {
char plain_str[30];
char cipher_str[30];
char decipher_str[30];
printf("Enter plain string: ");
scanf("%s", plain_str);
cipher(plain_str, cipher_str);
decipher(cipher_str, decipher_str);
}
void cipher(char plain_str[], char cipher_str[]) {
int i = 0;
while(plain_str[i] != '\0') {
if((plain_str[i]+3) >= 0 && (plain_str[i]+3) <= 127) {
cipher_str[i] = plain_str[i] + 3;
} else {
cipher_str[i] = plain_str[i] - 124;
}
i++;
}
printf("%s\n", cipher_str);
}
void decipher(char cipher_str[], char decipher_str[]) {
//asdf
}
The %s operator in scanf only reads a single word, not a whole line. So if you enter ab cd, only ab is put into plain_str. To read a whole line, use fgets():
fgets(plain_str, sizeof(plain_str), stdin);
size_t len = strlen(plain_str);
if (plain_str[len-1] == '\n') {
plain_str[len-1] = '\0'; // Remove newline
}
The other problem is that you're never adding the null terminator to cipher_str, so you're printing whatever garbage is in it after the encoded characters. The simplest way to resolve this is to initialize it to an all-zero array when you declare the variable:
char cipher_str[30] = {0};

Resources