通用C函数的字符串比较函数

时间:2016-01-30 08:08:24

标签: c generics

void *idealGenericLSearch(void *key, void *base, int numElem, int elemSize,
                          int (*cmpfn)(void *, void *)) {
    for (int i = 0; i < numElem; i++) {
        void *elemAddr = (char *)base + (i * elemSize);
        if (cmpfn(key, elemAddr) == 0)
            return elemAddr;
    }
    return NULL;
}

这是我正在使用的通用线性搜索功能。我正在搜索char *的数组。这是我的比较功能:

int myStrCmp(void *vp1, void *vp2) {
    char *s1 = *(char**)vp1;
    char *s2 = *(char**)vp2;
    return strcmp(s1, s2);
}

以下是我在main中调用它的方式:

char *notes[] = { "Ab", "F#", "B", "Gb", "D" };
char *keyNote = "Gb";
char **foundNote = idealGenericLSearch(&keyNote, notes, 5, sizeof(char *), myStrCmp);
if (foundNote) {
    printf("found the note: %s\n", *foundNote);
} else {
    printf("did not find note\n");
}

我无法弄清楚的是,如果我摆脱(char**)强制转换和取消引用,我的比较函数仍然可以正常工作。如果我像这样写cmpfn

int myStrCmp(void *vp1, void *vp2) {
    char *s1 = vp1;
    char *s2 = vp2;
    return strcmp(s1, s2);
}

它仍然有效。当lsearchelemAddr传递给此比较函数时,它应该是指向char*的指针,在这种情况下,我将通过比较函数strcmp {{1} }秒。有人可以解释这里发生了什么。

2 个答案:

答案 0 :(得分:2)

这就是为什么第二版myStrCmp“有效”。第一个版本是您想要的,它按预期比较字符串。另一方面,第二个版本是将包含字符串地址的指针视为字符串本身。因此,它逐字节地比较指针,就好像它们是字符串一样。如果指针不同,则它可能会比较为不相等(除非两个指针在它们不同之前包含零个字节)。但是如果指针是相同的,那么在遇到不同的字节之前,如果在指针中或在它们之后遇到零字节,它们就有可能比较相等。

那么为什么两个"Gb"指针是一样的呢?因为编译器识别它们是相同的字符串,并为两个引用分配单个字符串。

毋庸置疑,这是非常未定义的行为,因此分析它有时可行的原因纯粹是学术性的。

答案 1 :(得分:2)

你的问题很有趣!人们会期待一些未定义的行为,甚至是崩溃。它确实是偶然的,但原因如下:

您在测试中使用字符串文字:

char *notes[] = { "Ab", "F#", "B", "Gb", "D" };
char *keyNote = "Gb";

编译器很可能共享字符串文字,因此notes[3]中的指针与keyNote具有相同的值。

在您使用线性搜索时,您将执行strcmp,而不是字符串,而是在其地址上。地址可能包含空字节,如果您的体系结构是小端,则地址中的重要非空字节将首先出现。有了这些机会,对于所有其他条目,strcmp((char*)notes[3], (char*)&keyNote)确实为0而非0

你可以通过测试来验证这个理论:

char *notes[] = { "Ab", "F#", "B", "Gb", "D" };
char *keyNote = strdup("Gb");

keyNote中使用不同的指针时,通用搜索会失败。