Home > front end >  Caesar Cipher - Why while I'm iterating over a char array my program stop at a specific index?
Caesar Cipher - Why while I'm iterating over a char array my program stop at a specific index?

Time:03-25

I’m a novice in C and I’m practicing with some code in order to improve my knoowledge.

With the following code I’m trying to encrypt a given text returning, for every text character, a character that is the result of moving in the alphabet n positions forward.

So, for example, for the given text “hello” and the key 6 (so for every char of ‘hello‘ shifting of 6 positions in the alphabet chars array) the encrypted text will be “nkrru“.

My code use:

  • empty char array strResult where to store the resulting encrypted text;
  • temporary char array tmp where to store the shifted chars.

I also used two nested loop: in the first one I iterate, char by char, into the given text string and in the inner one I iterate into the alphabet chars in order to find the equivalent character. Then I populate the tmp array with the alphabet chars shifted by key times.

Meanwhile I manage the case if the shifting forward goes over the 26 letters of the alphabet and if one of the char is a white space.

At the end, using strcat() I concatenate tmp to strResult and print it.

#include <stdio.h>
#include <string.h>
#include <ctype.h>

int main()
{
    char ALPHABET[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
    int key = 9;
    string word = "Hello World";//text to encrypt

    char resultStr[strlen(word)];//string where to store the resulting encrypted text
    char tmp[strlen(word)];//temporary string

    int counter;//for the shifted index

        for (int i = 0, n = strlen(word); i < n; i  )
        {

            char wordcurrChar = tolower(word[i]);//convert every wordcurrChar to lowercase
            int checkSpace = isspace(wordcurrChar);//check if the character is a white space

            //inner loop to iterate trough alphabet array
            for (int j = 0, m = strlen(ALPHABET); j < m; j  )
            {
                counter = j key;//counter has now the value of the shifted index
                char ALPHcurrChar = ALPHABET[j];

                    if ((wordcurrChar == ALPHcurrChar) && (counter <= 26)) {//if counter is whitin the 26 characters of the alphabet
                        tmp[i] = ALPHABET[counter];//then shift at index counter
                    } else if ((wordcurrChar == ALPHcurrChar) && (counter > 26)) {//if counter is over the 26 characters of the alphabet
                        int newCounter = counter - 26;//then, once over Z, start from the beginning of the alphabet
                        tmp[i] = ALPHABET[newCounter];
                    } else if (checkSpace != 0) {//if the character is a white space
                        tmp[i] = ' ';//then return a white space also for the encrypted text
                    }
            }

        }
        strcat(resultStr, tmp);//concat resultStr and temporary string
        printf("%s\n", resultStr);//print the encrypted text
}

Everithing looks to work fine, except from the fact that when the shifting goes over the 26 alphabet chars and has to return as encrypted char the specific letter 'A' it stops.

So, for example, if I give as string to encrypt hello world with key=9, result is qnuux fx_ _ _. The last 3 letters are missing. Basically, the shifting of char r in hello world (that shifted of 9 position in the alphabet array goes over the 26th position and end in in char a position) stops everything.

shifting schema 2

Again, this happen only if the shifting goes over the 26 letters of the alphabet and end on the specific position of char a (with all other cases works fine). So with hello world and key = 15, the result will be wt_ _ _ _ _ _ _ _ stopping at the shifting of the first l of hello world. This because the shifting goes over the 26 alphabet letters and reach char a position.

shifting schema 3

I spent hours and hours to understand why is happening but with no success.

Hope someone can help.

Thanks a lot in advance.

P.s: I tried many different changing of the if statment that manage the condition of going over the 26 alphabet array positions but nothing really helped.

CodePudding user response:

Code seems to have missed the idea that with the C library, a string has a '\0'

A string is a contiguous sequence of characters terminated by and including the first null character. C17dr 7.1.1 1


At least these problems:

strlen() on a non-string

strlen(ALPHABET) leads to undefined behavior (UB) as strlen() expects a string and ALPHABET is not a string as it lacks a terminating null character.

// Bad
char ALPHABET[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
        for (int j = 0, m = strlen(ALPHABET); j < m; j  )

// Add null character
char ALPHABET[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '\0'};
// or simply
char ALPHABET[] = "abcde...wxyz";;

strcat() with a non-string

Similarly, strcat() below fails as strcat() expects resultStr and tmp to be strings. Neither are strings as they lack a terminating null character.

char resultStr[strlen(word)];
char tmp[strlen(word)];
....
// Bad
strcat(resultStr, tmp);

Alternate code would increase array size by 1 and add the final '\0'.

Not-productive to repeated walk the word to find its length.

size_t word_length = strlen(word);
char resultStr[word_length    1u];
char resultStr[0] = '\0';
char tmp[word_length   1u];
  ....
char tmp[word_length] = '\0';
strcat(resultStr, tmp);

CodePudding user response:

  1. You can simply initialise a char array with a string.
char ALPHABET[] = "abcdefghijklmnopqrstuvwxyz";
  1. It's better to #define magic numbers
#define CIPHER_ROTATIONS   9
  1. Every cipher should be recoverable, for data consistency. So, ignoring letter-case is loss of info.
char wordcurrChar = tolower(word[i]);

Simplified code:

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define CIPHER_ROTATIONS   27

// shift can be positive(encoding) or negative(decoding)
void caeser_cipher (const char* input, const int tlen, int shift, char* output)
{
    shift %= 26;                // more than 26 make cycle[s]
    if (shift < 0) shift  = 26; // moving x steps backward = moving (26-x) forward
    for (int ti=0; ti < tlen;   ti) {
        if (islower (input[ti]))
            output[ti] = 'a'   (input[ti] - 'a'   shift) % 26;
        else if (isupper (input[ti]))
            output[ti] = 'A'   (input[ti] - 'A'   shift) % 26;
        else
            output[ti] = input[ti];
    }
}


int main() {
    char text[] = "Caesar cipher, also known as Caesar's cipher, the shift cipher, Caesar's code or Caesar shift";
    int tlen = strlen(text);
    char cipher[tlen  1]; cipher[tlen] ='\0';
    int shift = CIPHER_ROTATIONS; // also called rotations

    printf ("Text :\t\t[%s]\n", text);
    caeser_cipher (text, tlen, shift, cipher);
    printf ("Cipher:\t\t[%s]\n", cipher);

    text[0] = '\0';
    caeser_cipher(cipher, tlen, -shift, text);
    printf ("DecipheredText: [%s]\n", text);

    return 0;
}

CodePudding user response:

So thank'you very much, to everyone, for helping. As I wrote, I'm a novice in C and probably I have to go deep in strings behavior.

What was inexplicable for me was the fact that the script stopped with such a specific case and not with all other cases.

I'll take all your suggestions as starting points for delve more in c string matter. Thanks again for helping.

  • Related