2

我使用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,我将第一个参数的转换替换strcmp

*(char**)ptr1 

从概念上讲,我不明白为什么会这样。不是吗

(char(*)[]) equivalent to (char**)? 

另外,我发现如果我在 while 循环语句中替换为,该函数仍然有效buffer&buffer

谁能给我解释一下这两个奇怪的地方?

4

4 回答 4

4

&buffer并且buffer将具有相同的值。数组的名称充当指向其第一个元素的指针,这就是为什么您得到两者相同的原因。

现在如果我们谈谈你的第二个问题(char(*)[]) equivalent to (char**)?

(char(*)[])不是函数指针。它是指向字符数组的指针。数组和指针不相同。数组可能会衰减为指针。

请看这里

于 2013-08-06T13:37:58.170 回答
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 */
于 2013-08-06T13:39:05.590 回答
1

ptr1实际上代表的是提供给 的值,lfind()它是数组的衰减指针值buffer

你所ptr1转换的是一个指向数组的指针char。取消引用后,该表达式会生成一个char. 表达式中的数组衰减到等于其第一个元素的地址的值。这对您有用,因为它恰好发生了&buffer并且buffer具有相同的指针值(尽管它们具有不同的类型)。最后,第一个参数的结果与strcmp()传递给的结果相同lfind(),即衰减的指针值buffer

当您改为ptr1as时(char **),这是一个指向char. Deferencing 产生一个指向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);
}
于 2013-08-06T13:54:10.003 回答
1

修复代码

您的代码将错误类型的内容传递给lfind. 如果key参数是指向某个类型 T 的指针,则base参数必须是指向 T 数组的指针。当您传递bufferaskey时,buffer作为 数组的char自动转换为指向 的指针char。因此,Tchar。所以base需要是一个数组char。但是,您base是指向 的指针数组char,因此它是错误的类型。

要解决此问题,请在定义Unique之后添加此代码:buffer

char *BufferPointer = buffer;

然后,调用lfind,使用:

if (!lfind(&BufferPointer, &array, &count, sizeof(char*), StringPtrCompare))

在这个调用中,第一个参数是一个指向的指针char。因此,类型 T 是指向 的指针char。这匹配&array,它是指向 的指针数组的指针char

然后,在 中StringPtrCompare,参数ptr1ptr2都将具有指向 T 的类型指针。由于 T 是指向 的指针,因此和char的类型是指向指向 的指针。(我现在忽略限定符。)然后你可以这样调用:ptr1ptr2charconststrcmp

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或者&是用于初始化数组的字符串文字。当您传递bufferlfind时,这些异常都不适用,因此buffer将转换为其第一个 的地址char

当你通过 时&buffer,这个表达式就是数组的地址。数组的地址与其第一个元素的地址具有相同的值,但具有不同的类型。但是,当您将buffer或传递&buffer给时lfind,它会转换为void *,因为lfind被声明为采用该类型的参数。buffer这样,和的类型之间的区别&buffer就丢失了,只传递了值。由于它们具有相同的值,因此没有区别。

于 2013-08-06T19:59:20.423 回答