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

通用C函数的字符串比较函数,c,generics,C,Generics,我主要是这样称呼它的: int myStrCmp(void *vp1, void *vp2) { char *s1 = *(char**)vp1; char *s2 = *(char**)vp2; return strcmp(s1, s2); } 我不明白的是,如果我去掉了字符**强制转换和取消引用,为什么我的比较函数仍然可以工作。如果我这样写cmpfn: char *notes[] = { "Ab", "F#", "B", "Gb", "D" }; char *key

我主要是这样称呼它的:

int myStrCmp(void *vp1, void *vp2) {
    char *s1 = *(char**)vp1;
    char *s2 = *(char**)vp2;
    return strcmp(s1, s2);
}
我不明白的是,如果我去掉了字符**强制转换和取消引用,为什么我的比较函数仍然可以工作。如果我这样写cmpfn:

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");
}

它仍然有效。当lsearch将elemAddr传递给这个比较函数时,它应该是指向char*的指针,在这种情况下,在比较函数中,我将传递strcmp char**s。有人能解释一下这里发生了什么事吗

下面是myStrCmp的第二个版本工作的原因。第一个版本是您想要的,它会按预期比较字符串。另一方面,第二个版本将保存字符串地址的指针视为字符串本身。因此,它将指针逐字节进行比较,就像它们是字符串一样。如果指针不同,则很可能比较为不相等,除非两个指针在不同之前都包含零字节。但是如果指针是相同的,那么在遇到不同的字节之前,如果在指针中或指针之后遇到零字节,它们就有可能比较相等

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


不用说,这是一种非常不明确的行为,因此分析它有时起作用的原因纯粹是学术性的。

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

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

int myStrCmp(void *vp1, void *vp2) {
    char *s1 = vp1;
    char *s2 = vp2;
    return strcmp(s1, s2);
}
编译器很有可能共享字符串文字,因此notes[3]中的指针与keyNote具有相同的值

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

您可以通过测试以下内容来验证此理论:

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

如果keyNote中有不同的指针,则常规搜索将失败。

非常感谢。我刚刚测试了一下,你说得对!我在想,如果不使用char**解除对vp1的引用也是一样的,因为它是一个空**,但编译器不允许这样做。所以我删除了*并运行了它,它成功了。这就是我变得困惑的原因。多亏了阿加纳斯·汤姆,他在类似的回答中也指出,这都是未定义的行为,有时会像预期的那样起作用。但是有一个合理的解释是令人欣慰的,它可能是准确的,或者足够接近。谢谢。看起来你基本上和chqrlie说了同样的话。我真的很感激。
char *notes[] = { "Ab", "F#", "B", "Gb", "D" };
char *keyNote = "Gb";
char *notes[] = { "Ab", "F#", "B", "Gb", "D" };
char *keyNote = strdup("Gb");