我正在尝试在 C 中的令牌循环中调用一个函数。我一直在段错误?

I'm trying to call a function inside of a token loop in C. I keep segfaulting?

提问人:Tanya Kumari 提问时间:9/24/2023 最后编辑:chqrlieTanya Kumari 更新时间:9/24/2023 访问量:71

问:

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

void pigLatinConsonant(char input[]) {
    char end[] = "ay";
    int length = strlen(input);
    int first_vowel = -1;
   
    for (int i = 0; i < length; i++) {
        if (strchr("aeiou", input[i])) {
            first_vowel = i;
            break;
        }
    }

    if (first_vowel != -1) {
        char consonant_cluster[length + 1];
        strncpy(consonant_cluster, input, first_vowel);
        consonant_cluster[first_vowel] = '\0';

        strcpy(input, input + first_vowel);
        strcat(input, consonant_cluster);
    }

    strcat(input, end);
    printf("Translated word: %s\n", input);
}

int main() {
    char sentence[100];
    printf("Enter a sentence: ");

    fgets(sentence, sizeof(sentence), stdin);

    char sentence_copy[sizeof(sentence)];
    strcpy(sentence_copy, sentence);

    char *token = strtok(sentence_copy, " ");

    while (token != NULL) {
        pigLatinConsonant(token);
        token = strtok(NULL, " ");
    }

    return 0;
}

在这里,我尝试从循环中调用此方法,但我认为它与我的函数的构建方式有关。用户输入一个句子,它应该移动前两个字符,并将它们放在单词的末尾并添加到其中。ay

C-字符串 strtok 函数定义 strcpy

评论

1赞 Some programmer dude 9/24/2023
您是否尝试过使用调试器来捕获崩溃,并在代码中查看崩溃发生的时间和位置?
0赞 Tanya Kumari 9/24/2023
它被困在方法中。至少这是我从中得到的?我发现当它出现段错误时,它只是重复结束字符串?这怎么可能?

答:

2赞 Vlad from Moscow 9/24/2023 #1

对于初学者来说,您的代码与赋值没有任何共同之处

用户输入一个句子,它应该移动前两个字符, 并把它们放在单词的末尾,并在其上加上“ay”。

本声明

strcpy(input, input + first_vowel);

已调用未定义的行为。

当范围重叠时,您不能使用。strcpy

相反,您应该使用 .memmove

还有这句话

strcat(input, end);

可以覆盖原始阵列之外的内存。

您应该在函数中分配一个新的字符数组,该数组的长度比原始字符串的长度大 3 并构建在其中 一个新字符串。

如果使用可变长度数组来存储转换后的字符串,则该函数可以如下所示,如下面的演示程序所示

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

void pigLatinConsonant( const char *s ) 
{
    const char end[] = "ay";

    size_t length = strlen( s );

    char pig_latin[length + sizeof( end )];

    size_t i = 0;

    while (i != length && strchr( "aeiou", s[i] ) == NULL ) ++i;

    if (i == length)
    {
        strcpy( pig_latin, s );
    }
    else
    {
        strcpy( pig_latin, s + i );
        strncat( pig_latin, s, i );
        pig_latin[length] = '\0';
    }

    strcat( pig_latin, end );

    printf( "Translated word: %s\n", pig_latin );
}

int main( void )
{
    pigLatinConsonant( "pig" );
}

程序输出为

Translated word: igpay

评论

0赞 Tanya Kumari 9/24/2023
我们还没有了解memmove,所以我认为我们不能这样做。但是我发现了错误,它与strncat有关,它会一直说ay。我该如何解决这个问题?
0赞 Vlad from Moscow 9/24/2023
@TanyaKumari 我看不出你在哪里使用 strncat。
0赞 Tanya Kumari 9/24/2023
对不起,这是一个错别字。我的意思是说strcat(input,end)是问题所在
0赞 B. Pantalone 9/24/2023
此解决方案适用于单个单词,而不是整个句子。OP 的代码还有其他问题(如您提到的),但眼前的问题是解析输入字符串中的单个单词。
0赞 Vlad from Moscow 9/24/2023
@B.Pantalone 她的函数被调用给每个单词:pigLatinConsonant(token);。她不会试图将整个句子传递给函数。
1赞 B. Pantalone 9/24/2023 #2

问题在于,它是指向原始输入字符串的指针,并且每次调用 时都会修改该字符串。该函数希望此字符串不会更改。tokenpigLatinConsonant(token)strtok()

而不是将 import 复制到 中,而是复制到一个临时字符串中,然后将其传递到 中。sentencesentence_copytokenwordpigLatinConsonant(word)

0赞 Fe2O3 9/24/2023 #3

从手册页中可以看出:“
注意事项**:字符串 src 和 dst 不能重叠。
因此,使用左移字符串的右侧会导致 UB。
(在某些实现中,使用 开始复制,因为这样可以更快地执行。
让自己非常熟悉手册页是值得的......
strcpy()strcpy()strcpy()'\0'


下面显示了如何使用经过验证的标准库函数搜索第一个元音,以及如何输出字符串区域(通过仔细研究 的手册页收集了更多信息。此版本包括一个连字符,以突出显示预期的音节化。printf()

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

void piggish( char *cp ) {
    size_t vowel1 = strcspn( cp, "aeiou" ); // lowercase only
    if( vowel1 == 0 )
        printf( "%s-ay ", cp );
    else
        printf( "%s-%.*say ", cp + vowel1, vowel1, cp );
}

int main( void ) {
    char str[] = "when in rome do as the romans do\n";

    /* Notice the '\n' that fgets() leaves in the returned buffer
     * Notice the delimiters for strtok() eats that '\n'
     */

    puts( str );
    for( char *cp = str; ( cp = strtok( cp, " \n" ) ) != NULL; cp = NULL )
        piggish( cp );
    putchar( '\n' );

    return 0;
}

结果:

when in rome do as the romans do
en-whay in-ay ome-ray o-day as-ay e-thay omans-ray o-day

由于输出接收到的字的段,因此不需要 提供 之外的缓冲区。printf()

全面的测试和提高鲁棒性是 OP 的一项练习。例如,处理用逗号分隔的单词。


** “Caveat”是一个拉丁词。也许扩展此代码以使用常用拉丁单词的词典,以避免将这些单词猪化