3

In the CS50 2019 Caesar assignment, I am supposed to perform Caesar shifts on characters by a given number of letters (key).

To do this, I add the value of key to each letter, as such:

for each character in plaintext:
    plaintext[character] += key

Since I want z to loop back to a, I then wrote:

while (ciphered_character > 122)
{
    ciphered_character -= 26;
}

Running the program with plaintext of z and a key of 26 causes 26 to be added to z (which is represented by 122). This causes plaintext[character] to overflow (past 127, I presume) and become negative before the while loop even kicks in. This gives a garbage output.

I know I can check for potential overflows beforehand and subtract 26 first, but that complicates my code. Can I give the variable 'more room' to prevent it from overflowing?

Do I use a different data type? What is the best practice here?

EsmaeelE
  • 2,331
  • 6
  • 22
  • 31
yeojunjie
  • 41
  • 4
  • What result do you expect for `z + 26`? I could see multiple correct answers to that! – abelenky Aug 30 '19 at 16:33
  • 3
    One natural approach is to first shift your characters to 0 to 25 (by subtracting `'a'`) then do the actual Caesar shift using modular arithmetic, then shift back to ascii by adding `'a'`) – John Coleman Aug 30 '19 at 16:33
  • @yeojunjie It is the same problem as with binary shift operator for integer types. Your program should just set an error if the passed key is equal to the number of characters in the alphabet. – Vlad from Moscow Aug 30 '19 at 16:42
  • Don't use characters. `'a'` is an int. `'z'` is an int. `'z' + 26` does not overflow. – William Pursell Aug 30 '19 at 17:11

2 Answers2

4

If you only care about lower case then this will work for you:

for each character in plaintext:
   plaintext[character] = (plaintext[character] - 'a' + key) % 26 + 'a'

Subtracting 'a' to give you a value of 0-25, then add the key. If there is a overflow the modulo will give you the updated value in the 0-25 range which is added back to 'a' to get the correct character.

If you do need to handle both upper and lower case then you will need two different cases - one using 'a' and the other using 'A'. Select the the correct case for each character by checking isupper(plaintext[character])

jmq
  • 1,559
  • 9
  • 21
  • Thank you -- your answer works best for my case. This also reminds me that I can directly do math and comparisons with `'a'` instead of having to look up its integer equivalent, `97`. – yeojunjie Aug 31 '19 at 08:07
1

Fun project;

I did it like this, assuming ASCII and using the full range of printable characters, from Space to ~

void caeser_shift(char* text, int key)
{ while (*text) { *text++ = ((*text-' ') + key) %('~'-' ') + ' '; } }

int main(void)
{
    char plaintext[] = "Hello World; This is a test.";

    caeser_shift(plaintext, 26);

    printf("%s\n", plaintext);

    return 0;
}

Output

Success #stdin #stdout 0s 4520KB
b!((+:q+.( U:n$%/:%/:{:0!/0H
abelenky
  • 63,815
  • 23
  • 109
  • 159