为什么我的密文得到负值?
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)];
这是从 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)];