C 为什么我的密文会有负值?

C 为什么我的密文会有负值?,c,cs50,caesar-cipher,C,Cs50,Caesar Cipher,这是CS50x中的一个问题集。这是凯撒密码。它需要通过在运行时输入的键来移动所有字母大写和小写。它应该保留大小写、符号和数字 我认为代码是正确的,但对于较大的键值,我不断得到负的ASCII值。在纸上/数学上,它不应该给出负值。有些事我不明白 // Caesar cipher// #include <cs50.h> #include <stdio.h> #include <string.h> #include <stdlib.h> //cipher

这是CS50x中的一个问题集。这是凯撒密码。它需要通过在运行时输入的键来移动所有字母大写和小写。它应该保留大小写、符号和数字

我认为代码是正确的,但对于较大的键值,我不断得到负的ASCII值。在纸上/数学上,它不应该给出负值。有些事我不明白

// 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]中的大写拉丁字母旋转0范围内用户指定的键值k。。。25,并且您愿意假设大写拉丁字母被编码为连续的词汇顺序值,就像它们在ASCII中一样,但不是在其他一些编码中,计算将具有以下形式:

c[i] = (((p[i] - 'A') + k) % 26) + 'A';
最初减去“A”的值会产生一个范围为0的结果。。。25根据上述假设。在该范围内的旋转通过增加键值和减少求和模26来实现。最后,添加“A”将旋转后的结果恢复到大写拉丁字母的范围内。这种方法或类似的方法是求和模式旋转所需要的,而问题中程序中的数学不是等价的

请注意,同样根据指定的假设,任何中间结果都不能超出与本例不直接相关的char类型的范围,更不用说实际分配给相关且重要的char变量的任何此类值了


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

错误在于C实现中的char是有符号的,这一行:

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

可以通过更改以下内容轻松解决此问题:

char c[strlen(p)];
致:


本声明int K[];声明一个不完整的类型。所以程序有未定义的行为。c是一个有符号字符,所以可以有负值…但看起来问题更像是你的maths@AjanthKumarakuruparan,不要将幻数用作64或91。@VladfromMoscow,假设我编辑了代码,并将数组K[]的大小设为K[100]。对于该输入,我仍然得到与以前相同的负值。所以我想这不是问题的原因。@ChrisTurner在我看来,数学在纸上看起来是正确的。你能找出数学中的问题吗?虽然所用的数学既繁琐又不必要,但它并没有错。简单地在一个地方插入未签名将使程序按预期运行。@EricPostpischil是的,我同意数学计算非常繁琐,可能会更好,但我认为它在逻辑上是错误的。是的,@Ajanthkumarakuran,我拒绝为您编写完整的解决方案,因为这是您的家庭作业。正如我所说,小写的计算与我已经介绍的类似。当然,大于25的键可以通过先将其模化为26来使用,甚至可以直接使用,前提是它们不太接近INT_MAX。非常感谢,这正是我所期望的答案,因为即使是你,数学也非常繁琐,我无法发现它是错误的。
unsigned char c[strlen(p)];