Home > Net >  How to shift a character by another character in C
How to shift a character by another character in C

Time:08-07

How would I go about circle left shifting every character in a string by the corresponding character in a 'password' string.

char *shift_encrypt(char *plaintext, char *password) {
    for (int i = 0; plaintext[i] != '\0'; i  ) {
        plaintext[i] = (plaintext[i] << password[i]) | (plaintext[i] >> (8 - password[i]));
    }
   return plaintext;
}

EDIT: To clarify what I am asking, if I wanted to circle shift for example the character 'A' by the character 'p', I mean something along the lines of:

0100 0001 ('A') << 0x70 ('p') shown bit-shifted left bit by bit

 1. 1000 0010
 2. 0000 0101
      .
      .
      .
 110. 0101 0000
 111. 1010 0000
 112. 0100 0001

So basically shifting by 1, 126 times?

CodePudding user response:

Disclaimer: as pointed out in the comments and explained here the C standard does not guarantee that letters are contiguous. However the idea behind this answer still holds.

Characters are defined as linear entries in an ASCII table. This means that each character is represented by a number. Adding 1 to a character brings you to the next one and so on.

You should also be familiar with modular arithmetic. What is "Z" 1? It goes back to "a".

Putting together these information you can see how the first representable character in a string in an ASCII table is represented by the number 33 decimal and the last one is represented by 126.

You can then make a shift function to shift a letter by n:

shift_letter(L,n)
    ret 33   (((L-33) n)%(126-33))
  • The L-33 is done to start from 0.
  • Then we add n.
  • We cycle back in case the result is grater than the number of possible letters. %(126-33)
  • We add offset again

PS:

As I said in the comments, your are shifting in the mathematical sense which not only makes no sense for the operation you want to do, but it also throws an error because shifting by 112 means multiplying by 2^112 which is just a bit too much.

CodePudding user response:

To circular shift an 8-bit object with large values like 112 ('p'), mod the shift by 8u. % with a negative char and 8 is not mod so use unsigned math.

Access plaintext[i] as an unsigned char [] to avoid sign extension on right shifts.

Use size_t to index string to handle even very long strings.

Sample fix:

char *shift_encrypt2(char *plaintext, const char *password) {
  unsigned char *uplaintext = (unsigned char *) plaintext;   
  for (size_t i = 0; uplaintext[i]; i  ) {
    unsigned shift = password[i] % 8u;
    uplaintext[i] = (uplaintext[i] << shift) | (uplaintext[i] >> (8u - shift));
  }
  return plaintext;
}

Note: if the password string is shorter than than plaintext string, we have trouble. A possible fix would re-cycle through the password[].


Advanced: use restrict to allow the compiler to assume plaintext[] and password[] do not overlap and emit potentially faster code.

char *shift_encrypt2(char * restrict plaintext, const char * restrict password) {

Advanced: Code really should access password[] as an unsigned char array too, yet with common and ubiquitous 2's compliment, password[i] % 8u makes no difference.

char *shift_encrypt3(char * restrict plaintext, const char * restrict password) {
  if (password[0]) {
    unsigned char *uplaintext = (unsigned char *) plaintext;   
    const unsigned char *upassword = (const unsigned char *) password;
    for (size_t i = 0; uplaintext[i]; i  ) {
      if (*upassword == 0) {
        upassword = (const unsigned char *) password;
      }
      unsigned shift = *upassword   % 8u;
      uplaintext[i] = (uplaintext[i] << shift) | (uplaintext[i] >> (8u - shift));
    }
  }
  return plaintext;
}
  • Related