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);
}
它仍然有效。当lsearch
将elemAddr
传递给此比较函数时,它应该是指向char*
的指针,在这种情况下,我将通过比较函数strcmp
{{1} }秒。有人可以解释这里发生了什么。
答案 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
中使用不同的指针时,通用搜索会失败。