CS50 Pset2-凯撒密码

CS50 Pset 2- ceasar cipher

它没有显示我想要它显示的是输入文本的加密版本,而是符号,我猜,看起来有点像“?”作为终端中的输出出现。谁能帮我找出我错过或做错了什么?

#include <cs50.h>
#include <stdio.h>
#include <string.h>

int main(int argc, string argv[])
{
    if (argc == 2)
    {
        string ptext = get_string("plaintext: ");
        int key = (int) argv[1];
        printf("ciphertext: ");
        for (int i = 0, n = strlen(ptext); i < n; i++)
        {
            printf("%c", (( ptext[i] + key ) % 26);
        } 
        printf("\n");
    }
    else
    {
        printf("Invalid input. \n");
    }

}

我希望 'hello' 的输出是 'ifmmp' 但事实并非如此。

此代码错误:

int key = (int) argv[1];

argvstring的数组,在CS50中无非是一个混淆的char *指针。

根据 5.1.2.2.1 Program startup of the C standard:

The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters:

    int main(void) { /* ... */ }

or with two parameters (referred to here as argc and argv, though any names may be used, as they are local to the function in which they are declared):

    int main(int argc, char *argv[]) { /* ... */ }

or equivalent; ...

所以 argv[1] 是一个 char * 指针值,然后您将其分配给 int 值。这是获取一些内存的地址(比如 argv[1] 中的值是 0xFF0403220020480C)并试图将其填充到 int 变量 key 的可能的 4 个字节中(在这种情况将被分配截断值 0x0020480C。)

这不是你想要做的。

(IMO,你这里的问题是一个完美的例子,说明了为什么 CS50 将 char *string 类型混淆是一个非常糟糕的主意。如果不理解指针和NUL 终止的 char 字符串通过 char * 指针访问,而 string 所做的混淆使得这更难。)

如果你想convert a string to a numeric value, you likely want something like strtol() (never use atoi() as it has no error checking and its use can invoke undefined behavior):

char firstCharNotConverted;

// set errno to zero as strtol()
errno = 0;
long key = strtol( argv[ 1 ], &firstCharNotConverted, 0 );

// if errno is now non-zero, the call to strtol() failed (per Linux man page)
// need to examine key and the contents of firstCharNotConverted
// to figure out why
if ( errno != 0 )
{
    ...
} 

省略了适当的 headers,作为任何尝试使用此代码的人的练习 ;-)

请注意,我将 long 用于 key,因为如果将 return 值转换为 [,则无法对 strtol() 进行完整和正确的错误检查=20=].

错误检查 strtol() 可能有点复杂,因为值 returned(并在上面的代码中分配给 key)可以是任何值,并且没有可能的值不是合法的 long 值,strtol() 可以 return 指示错误,因此为了正确检查错误,您需要检查 errno 和 [=43= 的值] 以正确确定是否确实发生了错误。 Linux man page 状态:

Since strtol() can legitimately return 0, LONG_MAX, or LONG_MIN (LLONG_MAX or LLONG_MIN for strtoll()) on both success and failure, the calling program should set errno to 0 before the call, and then determine if an error occurred by checking whether errno has a nonzero value after the call.

调用 strtol() 后,您需要检查 key 是否为 LONG_MIN,或者 LONG_MAXerrno 等于 ERANGE 下溢或溢出,或者如果 key0,您需要检查 firstCharNotConverted 的内容以确定转换失败的原因。请注意,如果 key 为零且 firstCharNotConverted 不等于 argv[ 1 ],则输入字符串已从零正确转换。

你的 Ceaser 密码实现也是错误的:

    for (int i = 0, n = strlen(ptext); i < n; i++)
    {
        printf("%c", (( ptext[i] + key ) % 26);
    } 
    printf("\n");

将只打印出值从 025 的字符 - 这些字符不是 ASCII 字符集中的字母。

这里已经发布了很多 Ceaser 密码问题,所以我不打算编写代码。参见 Caesar's Cipher Code 的一个示例问题。

问题就在这里printf("%c", (( ptext[i] + key ) % 26);。特别是 % 26。它看起来确实与问题集完全一样:

More formally, if p is some plaintext (i.e., an unencrypted message), pi is the ith character in p, and k is a secret key (i.e., a non-negative integer), then each letter, ci, in the ciphertext, c, is computed as

ci = (pi + k) % 26

wherein % 26 here means “remainder when dividing by 26.”

但是,pset 继续说:

think of A (or a) as 0, B (or b) as 1, …, H (or h) as 7, I (or i) as 8, …, and Z (or z) as 25.

问题是字符 ptext[i]ascii value of the letter,而不是 "alphabet index"。

也许查看实验室伪代码部分的剧透,尤其是 #5:

Iterate over each character of the plaintext:

  • If it is an uppercase letter, rotate it, preserving case, then print out the rotated character
  • If it is a lowercase letter, rotate it, preserving case, then print out the rotated character
  • If it is neither, print out the character as is

您可能会发现此 walkthrough(来自课程的早期版本)很有帮助。