这是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中不存在)。
答案 0 :(得分:1)
数学都是错误的,并且您的负值是由于将超出范围的值分配给类型为char
的数组元素而产生的。
假设您要通过用户指定的键值p[i]
旋转存储在k
中的大写拉丁字母,其范围为0 ... 25,并且您愿意假定该大写拉丁字母被编码为连续的,按字母顺序排列的值(因为它们采用ASCII格式,但不采用某些其他编码),计算将采用以下形式:
c[i] = (((p[i] - 'A') + k) % 26) + 'A';
最初减去'A'
的值将产生0 ... 25范围内的结果(取决于上面给出的假设)。通过在该范围内进行轮换,可以通过添加键值并减少模26的总和来实现。最后,添加'A'
可以将轮换后的结果返回到大写拉丁字母的范围内。这种方法或与其等效的方法是您进行求和模样式旋转所需要的,而问题程序中的数学是 not 等效的。
请注意,再次遵循指定的假设,任何中间结果甚至都不会超出类型char
的范围(在这种情况下不直接相关),实际分配的任何此类值都更少到一个char
变量(与相关和重要的变量)。
小写字母的计算是类似的(在类似的假设下),但不完全相同。
答案 1 :(得分:1)
错误是因为char
已在C实现中并在此行中签名:
c[i] = (p[i]+(k % 26));
当p[i] + k % 26
(不需要括号)超过127时,结果不适合您的实现中的char
,并且以实现定义的方式将其转换为char
,可能是通过对256模进行包装(等效地,使用低8位)。因此,在键值为100且字符'z'
的值为122的情况下,结果为122 + 100%26 = 122 + 22 + 144,该结果存储在c[i]
中为-112。
可以通过以下方法轻松解决:
char c[strlen(p)];
收件人:
unsigned char c[strlen(p)];