为什么会出现分段错误?代码在 CLion 和 VSC 中工作正常

Why do I get a segmentation fault? The code works fine in CLion and VSC

提问人:BenG 提问时间:10/3/2023 最后编辑:Vlad from MoscowBenG 更新时间:10/4/2023 访问量:97

问:

我目前正在使用锻炼来提高我的编程技能。该代码在我的 IDE 中运行良好,但在网站上导致错误消息。我做错了什么,您将如何改进我的代码?提前谢谢:)

make: *** [makefile:22: test] 分段错误(核心转储)

#include "isogram.h"
#include <string.h>
#include <ctype.h>

bool is_isogram(const char phrase[]) {
    int length = strlen(phrase);
    char temp[length];
    
    for (int i = 0; i < length; i++)
        temp [i] = phrase[i];

    temp[length] = '\0';

    for(int i = 0; temp[i]; i++){
        temp[i] = tolower(temp[i]);
    }

    for(int i = 0; i < length; i++){
        for(int j = i+1; j < length;j++) {
            if (temp[i] == temp[j] && temp[i] != ' ' && temp[i] != '-')
                return false;
        }
    }
    return true;
}

int main(void) {
    char phrase[] = "lumberjacks";
    if (is_isogram(phrase))
        printf("true");
    else
        printf("false");
    return 0;
}

我问了 ChatGPT,它建议了这条线,但这并不能解决问题。temp[length] = '\0';

嵌套循环 c-strings function-definition tolower

评论

2赞 Retired Ninja 10/3/2023
char temp[length + 1];将为终止 0 留出空间。为什么不在复制时转换为小写?无需两个循环。最后两个循环看起来有点奇怪,你确定这些循环条件是正确的吗?您能否提供一个最小的可重现示例来说明您如何为预期的真和假结果调用它?
0赞 Retired Ninja 10/3/2023
举个例子,说明为什么底部的循环可能不正确。ASCII 中的小写字母 a 的值为 97。在这种情况下,你真的想在外循环中从 0 迭代到 96 吗?
1赞 Rogue 10/3/2023
如果您有一个(包含从 0 到 的边界),则访问是越界的。关于 null 终止符的注释是正确的,您仍然可以使用 ,因为您可能不想覆盖 null 终止符。即便如此,这些都是 VLA,除非您打算这样做,否则我不鼓励使用它们。char arr[length];length - 1arr[length]i < lengthmalloc
5赞 Andrew Henle 10/3/2023
代码在 CLion 和 VSC 中工作正常不炸毁不是“工作正常”。就我问 ChatGPT 而言,它建议 temp[length] = '\0';?ChatGPT 是一个 f!*&^#!@ 愚蠢的反刍机器,它可以吐出它塞满的任何垃圾。C 数组是从零开始的,所以如果数组的大小是越界的。而 ChatGPT 真的太愚蠢了,无法意识到这一点。ChatGPT 背后的“智能”为零。lengthtemptemp[length]
1赞 axiac 10/3/2023
ChatGPT 可以讲漂亮的睡前故事,但它不能调试你的程序。它不明白它说了什么,它只是匹配单词。

答:

0赞 Vlad from Moscow 10/4/2023 #1

对于使用可变长度数组的初学者

int length = strlen(phrase);
char temp[length];

是不安全和多余的。请注意,函数的返回类型是无符号类型,而不是有符号类型。strlensize_tint

此外,调用该函数也是多余的。您可以根据字符串以终止零字符终止的事实来扫描字符串。strlen'\0'

您定义了一个数组,该数组没有终止零字符的空间。因此,这项任务'\0'

temp[length] = '\0';

调用未定义的行为。

在检查字符串是否为等值图之前转换可变长度数组的所有字符

for(int i = 0; temp[i]; i++){
    temp[i] = tolower(temp[i]);
}

效率低下。

短语中的单词不仅可以用空格字符分隔,还可以用制表符分隔。此外,您应该排除标点符号。' ''\t'

我将按以下方式编写函数,如下面的演示程序所示。

#include <stdio.h>
#include <ctype.h>
#include <stdbool.h>

bool is_isogram( const char phrase[] )
{
    bool isogram = true;

    if (*phrase != '\0')
    {
        for (const char *current = phrase + 1; isogram && *current != '\0'; ++current)
        {
            if (!isblank( ( unsigned char )*current ) && !ispunct( ( unsigned char )*current ))
            {
                char c = tolower( ( unsigned char )*current );

                const char *prev = current;

                while (isogram && prev != phrase)
                {
                    --prev;
                    isogram = tolower( ( unsigned char )*prev ) != c;
                }
            }
        }
    }  

    return isogram;
}

int main( void )
{
    const char *phrase = "lumberjacks";

    printf( "The phrase \"%s\" is isogram: %s\n",
        phrase, is_isogram( phrase ) ? "true" : "false" );
}

程序输出为

The phrase "lumberjacks" is isogram: true
0赞 Chris 10/4/2023 #2

来自莫斯科的 Vlad 很好地指出了数组长度的问题,但正如他所指出的,您使用的数组是不必要的。O(n) 解决方案:这个问题可能涉及创建一个直方图:我们将使用一个 26 个元素的数组来计算每个字母,其中每个元素对应于英文字母表中的一个字母。然后,可以在一次迭代中检查此字母,以确保没有字母出现多次(或每个字母出现相同的次数)。

通过在一个函数中执行此操作,我们可能会更有效率,但这演示了如何将程序分解为更小的问题。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdbool.h>

int *alpha_histogram(const char *str) {
    int *hist = calloc(sizeof(int), 26);
    if (!hist) return NULL;

    for (const char *ch = str; *ch; ch++) {
         if (!isalpha(*ch)) continue;

         hist[tolower(*ch) - 'a']++;
    }

    return hist;
}

bool is_isogram(const char *str) {
    int *hist = alpha_histogram(str);
    if (!hist) exit(EXIT_FAILURE);

    for (size_t i = 0; i < 26; ++i) {
         if (hist[i] > 1) return false;
    }

    free(hist);

    return true;
}

int main(void) {
    const char *str = "lumberjacks";

    if (is_isogram(str)) {
        printf("\"%s\" is an isogram.\n", str);
    }
}