当前位置: 首页 > 知识库问答 >
问题:

C-strlen()似乎返回6的最小值

施辉
2023-03-14

以下是CS50课程练习的一部分。完整的问题在这里描述:https://cs50.harvard.edu/x/2021/psets/2/substitution/

简而言之:在命令行上,您提供了一个26个长的字母数组作为参数,这些字母将用于“加密”一个字符串,该字符串在运行时被提示为明文。

然后将明文数组循环,并将其ascii整数值(略为简化)用于索引到作为命令行参数提供的26个字母密钥中,从而加密初始明文字符串(ptxt)并将其存储在新的密文字符串(ctxt)中。

我遇到的问题是,对于纯文本小于6的输入,我用来在'n'中存储ptxt长度的strlen()函数似乎返回6。因此,如果我在明文提示下只键入字母“a”,n似乎被设置为6。

以下例子:

$ ./替换YTNSHVEFXRBAUQZCLWDMIPGJO

明文:a

密文:y.G[

密文有6长

预期的输出应该是'y',但显然有些东西超出了范围——长度不应该是6,而应该是1。让我发疯的是——如果你在初始化'n'后取消对printf语句的注释,那么代码会突然起作用,你会得到以下结果:

$ ./替换YTNSHVEFXRBAUQZCLWDMIPGJO

明文:a

明文长度为1

密文:y

密文的长度是1

我错过了什么?printf调用是如何解决这个问题的?

快把我逼疯了:)

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

bool is_letter(string array);
char encrypt(string key, char c);

//Command Line input a key to 'encrypt' some plaintext inputted at runtime
int main(int argc, string argv[])
{
    // if there are NOT 2 arguments OR the first argument is NOT just letters OR is not 26 letters
    if (argc != 2 || !is_letter(argv[1]) || strlen(argv[1]) != 26)
    {
        printf("Usage: ./caesar key (where key must be 26 letters)\n");
        return 1;
    }

    // prompt user for a plaintext string, store the length in n and initialize a ciphertext string with same length
    string ptxt = get_string("plaintext: ");
    int n = strlen(ptxt);
    //printf("plaintext is %i long\n", n); //this is here to correct n (try commenting out this line and see what happens for ptxt < 6)
    char ctxt[n];
    for (int i = 0; i < n; i++)
    {
        ctxt[i] = encrypt(argv[1], ptxt[i]);
    }
    printf("ciphertext: %s\n", ctxt);
    printf("ciphertext is %i long\n", (int) strlen(ctxt));
    return 0;
}


// function that checks whether command line argument is all letters
bool is_letter(string array)
{
    int n = strlen(array);
    for (int i = 0; i < n; i++)
    {
        if (!isalpha(array[i])) //loop over string - if any char is not a letter - return false
        {
            return false;
        }
    }
    return true; //reaching this means all chars in the string are a letter - return true
}

//function that takes a key and a char and returns the "encrypted" char
char encrypt(string key, char c)
{
    if (isalpha(c))
    {
        int n = 0;
        char letter = 0;
        if (isupper(c))
        {
            n = c - 65;
            letter = key[n];
            return toupper(letter);
        }
        else
        {
            n = c - 97;
            letter = key[n];
            return tolower(letter);
        }
    }
    else
    {
        return c;
    }
}

共有1个答案

嵇弘新
2023-03-14

C中没有“字符串”这个词。C中的“字符串”实际上是字节数组,char*。C中的数组不知道它们有多长,没有内置的边界检查。你要么需要知道它们的大小,要么就有一个终结者。“字符串”以0结尾,称为“空字节”,通常表示为\0

strlen读取字节,直到看到空字节。如果没有空字节,strlen将愉快地从数组末尾进入垃圾内存,直到它碰巧看到空字节,或者操作系统阻止程序超出其内存边界,这是一个分段错误。

// A basic strlen() implementation.
size_t my_strlen(const char *string) {
    size_t len;

    // no body, just counting until it sees a null byte.
    for( len = 0; string[len] != '\0'; len++ );
    
    return len;
}

(IMO CS50在学习C时试图隐藏这一点是有害的。有一个悠久的传统,就是试图把C当作不是C来对待。C的裸机、热棒、没有护栏的特性不能零碎地隐藏起来。你要么会得到一个混乱的结果,要么会得到一种新的语言。如果你想要字符串,请使用C或像GLib这样完全实现的库。)

当逐字节创建新字符串时,必须终止它。它必须有一个额外的字节来存储0。

    // Allocate an extra byte for the terminating null.
    // At this point ctxt contains garbage.
    char ctxt[n+1];
    for (int i = 0; i < n; i++)
    {
        ctxt[i] = encrypt(argv[1], ptxt[i]);
    }

    // Terminate the string.
    ctxt[n] = '\0';

printf调用是如何解决这个问题的?

当您像char ctxt[n 1]这样分配内存时,它是未初始化的。它不会自动归零。它包含了记忆中的所有垃圾。你可以走运,得到全零。它可能包含其他字符串。它可能包含看起来像随机垃圾的东西。

在分配ctxt之前添加printf会稍微改变分配给ctxt的内存块printf还必须分配内存,因此ctxt可能会得到一个恰好以零开头的稍有不同的内存块ctxt可能会得到printf分配、归零和释放的内存块。由于内存是一种全局资源,程序某个部分的更改可能会暴露或隐藏程序另一部分的内存错误。

valgrind和AddressSanitizer等工具可以帮助发现这些微妙的错误。

 类似资料:
  • 我正在尝试运行一个in查询(springboot jpa mysql)。我已经启用了调试日志,查询似乎很好,但是SpringJPA没有返回任何结果。 以下是配置: 这是我的存储库: //或 这里需要注意的是,“in”查询中的列不是主键。 生成的查询:

  • 本章目录 1. 一个特殊的参数:return_value 2. 引用与函数的执行结果 3. 小结 PHP语言中函数的返回值是通过return来完成的,就像下面的程序: <?php function sample_long() { return 42; } $bar = sample_long(); C语言也一样使用return关键字 int sample_long(void) { r

  • 问题内容: 我想我已经找到了错误所在: 我进行调试,得到以下结果(我输入了555和欧元): 因此看来,用于解码JSON对象的PHP函数在某处做错了。有什么帮助吗? 问题答案: Google给您的响应不是有效的JSON,因为未引用标签。您必须自己解析。 输出:

  • 我们想写一个函数,它将二叉树的根作为输入,并使用类PairAns返回该树的最大值和最小值。 我在这个问题的基础案例中遇到了一些问题 我希望答案是正确的,但在所有测试用例中都出现了运行时错误。

  • 我正在处理LeetCode问题111。二叉树的最小深度: 给定一棵二叉树,求其最小深度。 最小深度是从根节点到最近的叶节点的最短路径上的节点数。 注意:叶是没有子节点的节点。 我使用了广度优先的算法,并试图改变它以使其与问题保持一致。但是函数返回的是。 有人能解释为什么会这样吗?

  • 本文向大家介绍返回JavaScript中数组的最小值和最大值的函数,包括了返回JavaScript中数组的最小值和最大值的函数的使用技巧和注意事项,需要的朋友参考一下 问题 我们需要编写一个接受一个数组并返回另一个数组的JavaScript函数,该数组的第一个元素应该是输入数组的最小元素,第二个应该是输入数组的最大元素。 示例 以下是代码- 输出结果