为什么我的密文得到负值?

Why do I get negative values for my ciphertext?

这是从 CS50x 开始设置的问题。这是凯撒密码。它需要通过在 运行 时间内输入的键来移动所有字母(大写和小写)。它应该保留大小写、符号和数字。

我认为代码是正确的,但对于较大的键值,我总是得到负的 ASCII 值。在 paper/mathematically 上,它不应该给出负值。有些东西我不明白。

// Caesar cipher//
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

//cipher key//
int K[];

int main(int argc, string argv[])
{

//checking for only one command-line argument//
    if(argc == 2)
    {

        //checking if key is digit//
          for(int i = 0, n=strlen(argv[1]); i < n; i++)
          {
              K[i] = (argv[1][i]-'0');
              if((K[i] < 0) || (K[i] > 9 ))
            {
                printf("Usage: ./caesar key\n");
                return 1;
                break;
            }
          }


          string p = get_string("plaintext: ");
          printf("ciphertext: ");
          char c[strlen(p)];
          for(int i = 0, n = strlen(p); i < n; i++)
          {

              if(((p[i] > 64) && (p[i] < 91)) || ((p[i] > 96) && (p[i] < 123)))
              {

                 int k = atoi(argv[1]);

                 c[i] = (p[i]+(k % 26));



                 if (c[i] > 122)
                 {
                     c[i] =  (c[i] % 122) + 96;
                 }

                 else if ((c[i] > 90) && (c[i] < 97))
                 {
                     c[i] = (c[i] % 90) + 64;
                 }


                 else
                 {
                     c[i] =  c[i];
                 }
              }
              else
              {
               c[i] = p[i];
              }
             printf("%c",c[i]);
          }

          printf("\n");

    }

    else
    {
      printf("Usage: ./caesar key\n");
      return 1;
    }


}

例如,密钥 100 和明文 "z" 应该给我一个值 118 (v),但我得到 -112(ASCII 中不存在)。

数学全错了,你的负值是由于将超出范围的值分配给 char.

类型的数组元素而产生的

假设您要将存储在 p[i] 中的大写拉丁字母按用户指定的键值 k 在 0 ... 25 范围内轮换,并且您愿意假设大写拉丁字母被编码为连续的词汇顺序值(因为它们在 ASCII 中,但不是在某些其他编码中),计算将采用这种形式:

c[i] = (((p[i] - 'A') + k) % 26) + 'A';

最初减去 'A' 的值会产生 0 ... 25 范围内的结果(根据上面给出的假设)。该范围内的旋转通过添加键值和减少总和模 26 来实现。最后,添加 'A' 将旋转结果带回到大写拉丁字母的范围内。这种方法或与其等效的方法是求和和模数样式旋转所需要的,并且问题中程序中的数学是 not 等价的。

请注意,再次根据指定的假设,中间结果甚至不能超出类型 char 的范围(在这种情况下不直接相关),更不用说任何此类值了实际上分配给了一个 char 变量( 相关且重要的)。

小写字母的计算是类似的(在类似的假设下),但不完全相同。

错误在于 char 在您的 C 实现中签名并在这一行中:

c[i] = (p[i]+(k % 26));

p[i] + k % 26(不需要括号)超过127时,结果不适合你实现中的char,在实现中转换为char-定义的方式,可能通过包装模 256(等效地,使用低八位)。因此,如果键值为 100,字符 'z' 的值为 122,则结果为 122 + 100 % 26 = 122 + 22 + 144,它作为 −112.[=19 存储在 c[i] 中=]

这可以通过更改轻松解决:

char c[strlen(p)];

至:

unsigned char c[strlen(p)];