所以我正在学习C语言,为了练习,我做了一些"LeetCode"挑战.对于回文判断器,我try 了两种方法,一种使用索引,另一种使用指针.

  • 这种巨大的表现差异来自哪里?

作为一个后续问题:

  • 通常首选的方法是什么?原因是什么?

enter image description here enter image description here

以下是代码块以供参考.

Pointers:

bool isPalindrome(char *s) {

    size_t length = strlen(s);
    char *left = s;
    char *right = s + length - 1;

    while (left < right) {
        
        while (left < right && !isalnum(*left))
            ++left;
        
        if (left == right)
            return true;

        while (right > left && !isalnum(*right))
            --right;

        if (right == left)
            return true;
        
        if (tolower(*left) != tolower(*right))
            return false;

        ++left;
        --right;
    }

    return true;
}

Indices:

bool isPalindrome(char *s) {

    size_t length = strlen(s);
    size_t left = 0;
    size_t right = length - 1;

    while (left < right) {
        
        while (left < right && !isalnum(s[left]))
            ++left;
        
        if (s[left] == '\0')
            return true;

        while (right > left && !isalnum(s[right]))
            --right;

        if (right == 0)
            return true;
        
        if (tolower(s[left]) != tolower(s[right]))
            return false;

        ++left;
        --right;
    }

    return true;
}

推荐答案

这种巨大的表现差异来自哪里?

造成性能差异的潜在原因有很多,但了解如何编译代码、如何测量性能以及使用什么输入会很有帮助.以下是一些提示:

  • 发布的两个函数没有实现相同的语义:在指针版本中,退出循环if (left == right),而在索引版本中的等价测试是if (s[left] == '\0'),这在大多数情况下不是真的,其中if (left == right)为真.如果这些函数的表现不同,它们的表现也就会不同,这并不奇怪.

  • 您启用优化了吗?在没有优化的情况下,s[left]可能会生成比*left更多的代码,但在启用优化的情况下,大多数编译器将生成具有类似性能的代码,要么将索引法转换为指针法,要么使用使用多个寄存器的寻址模式,无论有没有zoom ,都不会有任何损失.

通常首选的方法是什么?原因是什么?

如果实现得当,这两种方法是等价的,并且用现代编译器编译到相同的性能.

以下是使用指针的原因:

  • 如果当地规则鼓励使用指针,使用指针可能会更合适
  • 指针是特定于C(和C++)的,它使60多岁的顽固C程序员更加熟悉这些代码

使用类型为size_t的索引变量的原因:

  • 使用索引更通用,适用于大多数语言,在C++语言中可能更常用(for.
  • 对于来自不同背景的程序员来说,代码更具可读性
  • 代码更容易移植到其他语言.

在你把注意力放在性能上之前,先把注意力放在正确性上!

  • 这两个函数在空字符串上都有未定义的行为,空字符串是一个普通的回文.
  • 您有多个多余的测试.
  • 来自<ctype.h>的宏isalnum()tolower()仅针对int个值的受限范围定义:类型unsigned char的所有值和特殊负值EOF扩展为.在默认情况下对类型char进行签名的平台上,传递负char值具有未定义的行为.您应该使用到(unsigned char)的强制转换.

以下是修改后的版本:

bool isPalindrome_idx(const char *s) {
    size_t len = strlen(s);
    if (len > 1) {
        size_t left = 0;
        size_t right = len - 1;
        while (left < right) {
            if (!isalnum((unsigned char)s[left])) {
                left++;
            } else
            if (!isalnum((unsigned char)s[right])) {
                right--;
            } else
            if (tolower((unsigned char)s[left]) != tolower((unsigned char)s[right])) {
                return false;
            } else {
                left++;
                right--;
            }
        }
    }
    return true;
}

bool isPalindrome_ptr(const char *s) {
    size_t len = strlen(s);
    if (len > 1) {
        const char *left = s;
        const char *right = s + len - 1;
        while (left < right) {
            if (!isalnum((unsigned char)*left)) {
                left++;
            } else
            if (!isalnum((unsigned char)*right)) {
                right--;
            } else
            if (tolower((unsigned char)*left) != tolower((unsigned char)*right)) {
                return false;
            } else {
                left++;
                right--;
            }
        }
    }
    return true;
}

C++相关问答推荐

初始化char数组-用于初始化数组的字符串是否除了存储数组的位置之外单独存储在内存中

Mbed TLS:OAEP的就地en—/decryption似乎不起作用'

Ebpf内核代码:permission denied:invalid access to map value

如何判断宏参数是否为C语言中的整型文字

如何解决C中的严格别名?

为什么删除CAP_DAC_OVERRIDE后创建文件失败?

在C语言中,在数学运算过程中,为什么浮点数在变量中的行为不同

fwrite无法写入满(非常大)缓冲区

为什么该函数不将参数值保存到数据 struct 中?

S的这种管道实施有什么问题吗?

将字符串数组传递给C中的函数:`str[dim1][str_size]`vs`*str[dim1]`

当内存来自Malloc时,将char*转换为另一个指针类型是否违反了严格的别名规则?

不使用任何预定义的C函数进行逐位运算

如何逐位读取二进制文件?

未为同一文件中的函数执行DirectFunctionCall

x86-64平台上的int_fast8_t大小与int_fast16_t大小

If语句默认为true

添加/删除链表中的第一个元素

10 个字节对于这个 C 程序返回后跳行的能力有什么意义

使用邻接表创建图