只要看到两个不同的字符,您就可以检测到不匹配 - 但直到您到达字符串末尾并且字符仍然相同时才能检测到匹配。
至少在我看来,这方面的大多数尝试都将基本想法倒退,试图立即比较字符(一个或两个字符串为空的一些特殊情况)。相反,通常最好从跳过相等的非零字节开始。然后,您要么位于(至少一个)字符串的末尾,要么您发现两个字符串中的字节不匹配。无论哪种方式,此时您都可以理清发生了什么,并返回正确的值。
int cmp_str(char const *a, char const *b) {
while (*a && *a == *b) {
++a;
++b;
}
if (*b < *a)
return 1;
if (*b > *a)
return -1;
return 0;
}
这使循环非常简单,条件很少,因此可以快速执行。所有更复杂的比较来确定实际排序发生在循环之外,它们只发生一次,并且对速度几乎没有影响。
我可能应该添加一个警告:这并没有尝试正确处理国际字符。为此,您只需添加定义字符相对顺序的整理表,因为(至少在许多代码页中)字符的值与字符的排序顺序不对应。
值得一提的是,这里有一个快速测试,比较了 Andreascompare
和strcmp
标准库中的结果和速度:
int cmp_str(char const *a, char const *b) {
while (*a && *a == *b) {
++a;
++b;
}
if (*b < *a)
return 1;
if (*b > *a)
return -1;
return 0;
}
inline int compare(char const* const a, char const* const b)
{
/* Return -1 less than, 0 equal, 1 greater than */
if (!a[0] && b[0])
return -1;
else if (a[0] && !b[0])
return 1;
register int i = 0;
for (; a[i] && b[i]; i++) {
if (a[i] < b[i])
return -1;
if (a[i] > b[i])
return 1;
}
#if 1 /* this addition makes this code work like std::strcmp */
if (!a[i] && b[i])
return -1;
else if (a[i] && !b[i])
return 1;
#endif
return 0;
}
#ifdef TEST
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
int main(){
char *s1 [] = { "", "a", "one", "two", "three", "one", "final" };
char *s2 [] = { "x", "b", "uno", "deux", "three", "oneone", "" };
for (int i = 0; i < 7; i++) {
printf("%d\t", cmp_str(s1[i], s2[i]));
printf("%d\t", compare(s1[i], s2[i]));
printf("%d\n", strcmp(s1[i], s2[i]));
}
// Test a long string:
static const int size = 5 * 1024 * 1024;
static char s3[size];
for (int i = 0; i < size - 1; i++)
s3[i] = (rand() % 254) + 1;
s3[size - 1] = '\0';
static char s4[size];
strcpy(s4, s3);
s3[size - 5] = (s3[size - 5] + 4) % 255;
clock_t start = clock();
int val1 = cmp_str(s3, s4);
clock_t t1 = clock() - start;
start = clock();
int val2 = compare(s3, s4);
clock_t t2 = clock() - start;
start = clock();
int val3 = strcmp(s3, s4);
clock_t t3 = clock() - start;
double v1 = (double) t1 / CLOCKS_PER_SEC;
double v2 = (double) t2 / CLOCKS_PER_SEC;
double v3 = (double) t3 / CLOCKS_PER_SEC;
printf("Jerry: %d, %f\nAndreas: %d, %f\nstdlib: %d, %f\n", val1, v1, val2, v2, val3, v3);
}
#endif
结果:
-1 -1 -1
-1 -1 -1
-1 -1 -1
1 1 1
0 0 0
-1 -1 -1
1 1 1
Jerry: 1, 0.007000
Andreas: 1, 0.010000
stdlib: 1, 0.007000
由于 Andreas 已经更正了他的代码,所有三个测试都产生了相同的结果,但是这个版本和标准库比 Andreas 的版本快了大约 30%。不过,这确实与编译器有所不同。使用VC++,我的代码几乎和标准库中的代码匹配(但如果我使用一个巨大的字符串,比如200兆字节,标准库中的版本明显更好。使用g++,标准库中的代码似乎有点比 VC++ 标准库中的代码慢,但它为 Andreas 的代码或我的代码生成的结果比 VC++ 为它们生成的结果要差很多。在 200 兆字节的字符串上,我使用 VC++ 得到以下结果:
Jerry: 1, 0.288000
Andreas: 1, 0.463000
stdlib: 1, 0.256000
...但是使用 g++ (MinGW),我得到如下结果:
Jerry: 1, 0.419000
Andreas: 1, 0.523000
stdlib: 1, 0.268000
尽管排名保持不变,但标准库和我的代码之间的速度差异使用 g++ 比使用 VC++ 大得多。