我使用lfind
代码:
int StringPtrCompare(const void *ptr1, const void *ptr2)
{
return strcmp(*(char(*)[])ptr1, *(char**)ptr2);
}
void Unique()
{
char* array[1000] = {0};
char buffer[200] = {0};
size_t count = 0;
FILE* fp = fopen("text", "r");
if (fp == NULL)
{
error(1, 0, "Couldn't open text for reading");
}
while (count < 1000 && fscanf(fp, "%199s", buffer) == 1)
{
if (!lfind(buffer, array, &count, sizeof(char*), StringPtrCompare))
{
array[count++] = strdup(buffer);
}
}
fclose(fp);
for (int i = 0; i < count; i++)
{
printf("%s\n", array[i]);
free(array[i]);
}
}
这很有效,但最初我得到的是细分错误,相反,在StrPtrCompare
中,我用<{p}}替换了strcmp
中第一个参数的强制转换。
*(char**)ptr1
从概念上讲,我不明白为什么会这样。 不是
(char(*)[]) equivalent to (char**)?
另外,我发现如果我在while循环语句中用buffer
替换&buffer
,该函数仍然有效。
任何人都可以向我解释这两个奇怪的事情吗?
答案 0 :(得分:4)
&buffer
和buffer
将具有相同的值。数组的名称充当指向其第一个元素的指针,这就是为什么两者都相同。
现在,如果我们谈论您的第二个问题(char(*)[]) equivalent to (char**)?
(char(*)[])
不是函数指针。它是指向字符数组的指针。
数组和指针不相同.Array可能会衰减为指针。
请查看here
答案 1 :(得分:1)
char(*)[]
是指向数组的指针,而char**
是指向指针的指针。
不,这些不等同,例如,如果您有char (*arr)[10]
,此处arr
只能分配给相同大小和类型的数组。此外,如果希望指针指向数组,则必须传递不需要执行的数组的地址。
请考虑以下用法:
char arr[10];
char brr[10];
char (*crr)[10] = &arr; /* note the address-of operator, just passing 'arr' will incense the compiler */
crr = &brr; /* again reassigning but to an array of valid type */
将此与char**
:
char *twod_arr[5];
char *twod_brr[4];
char** drr = twod_arr; /* legal, doesn't care about size of array */
drr = twod_brr; /* different array size, still doesn't matter */
答案 2 :(得分:1)
ptr1
实际代表的是提供给lfind()
的值,它是数组buffer
的衰减指针值。
您投射ptr1
的内容是指向char
数组的指针。取消引用,该表达式生成一个char
数组。表达式中的数组衰减到等于其第一个元素的地址的值。这对您有用,因为&buffer
和buffer
具有相同的指针值(尽管它们具有不同的类型)。最后,strcmp()
的第一个参数的结果与传递给lfind()
的结果相同,即buffer
的衰减指针值。
当您将ptr1
转换为(char **)
时,这是指向char
的指针。引用会产生指向char
的指针。但是,由于ptr1()
实际上具有数组buffer
的衰减指针值,因此此取消引用的结果会导致strcmp()
将sizeof(char *)
字节buffer
视为指向char
的指针。
在任何一种情况下,您都取消引用了一个指针,该指针的类型与实际位于该地址的对象不兼容,从而导致未定义的行为。在一个案例中,它碰巧解决了,在另一个案例中,它崩溃了。
您可以将ptr1
视为实际类型,指向char
的指针:
int StringPtrCompare(const void *ptr1, const void *ptr2) {
return strcmp((const char *)ptr1, *(char**)ptr2);
}
答案 3 :(得分:1)
您的代码将错误类型的内容传递给lfind
。如果key
参数是指向某个类型T的指针,则base
参数必须是指向T数组的指针。当您将buffer
作为key
传递时,则buffer
是char
的数组,会自动转换为指向char
的指针。因此,T
是char
。因此base
必须是char
的数组。但是,您的base
是指向char
的指针数组,因此类型错误。
要解决此问题,请在定义Unique
后在buffer
中添加此代码:
char *BufferPointer = buffer;
然后,要拨打lfind
,请使用:
if (!lfind(&BufferPointer, &array, &count, sizeof(char*), StringPtrCompare))
通过此调用,第一个参数是指向char
指针的指针。因此,类型T是指向char
的指针。这匹配&array
,它是指向char
指针数组的指针。
然后,在StringPtrCompare
中,参数ptr1
和ptr2
将各自具有指向T的类型指针。由于T是指向char
的指针,{{1}的类型}和ptr1
是指向ptr2
指针的指针。 (我暂时忽略了char
限定符。)然后你可以这样调用const
:
strcmp
指向数组的指针(如int StringPtrCompare(const void *ptr1, const void *ptr2)
{
return strcmp(* (char **) ptr1, * (char **) ptr2);
}
)与指针指针(例如char (*)[]
)不同。如果char **
是指向数组的指针,则在p
指向的位置必须有一个元素数组:元素的数据必须位于该位置。
相反,如果p
是指向指针的指针,则在p
指向的位置必须有一个指针:该位置的字节必须包含指针的值
当p
的类型为buffer
时,它是char [200]
的数组。在表达式中使用数组时,它将转换为指向其第一个元素的指针,除非它是char
,sizeof
或_Alignof
的操作数,或者是使用的字符串文字初始化一个数组。当您将&
传递给buffer
时,这些例外都不适用,因此lfind
会转换为其第一个buffer
的地址。
传递char
时,此表达式是数组的地址。数组的地址与其第一个元素的地址具有相同的值,但具有不同的类型。但是,当您将&buffer
或buffer
传递给&buffer
时,它会转换为lfind
,因为void *
被声明为采用该类型的参数。因此,lfind
和buffer
的类型之间的差异将丢失,并且仅传递该值。由于它们具有相同的值,因此没有区别。