C语言 类型不可知归属函数

t5fffqht  于 2023-01-29  发布在  其他
关注(0)|答案(3)|浏览(142)

我试图构建一个函数来检查一个特定的指针值是否存储在一个给定的数组中,我试图使这个函数与类型无关,所以我决定使用实现qsort()时使用的方法,在这种方法中,传递一个函数指针来执行特定于类型的任务。
该函数如下所示:

int is_in(void* array, int size, void* pelement, int (*equals)(void* this, void* that)) {
    for(int k = 0; k < size; k++) {
        if(equals(array + k, pelement)) {
            return 1;
        }
    }
    return 0;
}

equals()函数检查第二个参数是否等于第一个参数指向的值。
我需要实现的equals()函数的一个特定实现与我创建的struct Symbol类型有关。

int ptreq(void* ptr1, void* ptr2) {
    return ((*((Symbol**) ptr1) == (Symbol*) ptr2));
}

struct Symbol的定义如下:

enum SymbolType {
    TERMINAL,
    NONTERMINAL
} typedef SymbolType;

struct Symbol {
    char* content;
    SymbolType type;
} typedef Symbol;

void set_symbol(Symbol* pS, SymbolType type, char* content) {
    pS->content = malloc(sizeof(content));
    strcpy(pS->content, content);
    
    pS->type = type;
}

然而,当我尝试用一个基本示例测试is_in()时,我得到了不正确的结果。

#include <stdlib.h>
#include <stdio.h>
#include "string.h"
#include <stdarg.h>
#include <unistd.h>
int main(int argc, char* argv[]) {
    Symbol F, E;
    set_symbol(&E, NONTERMINAL, "E");
    set_symbol(&F, NONTERMINAL, "F");

    Symbol** pptest = malloc(2*sizeof(Symbol*));

    pptest[0] = &E;
    pptest[2] = &F;

    printf("Is F in pptest? %d\n", is_in(pptest, 2, &F, &ptreq));

    return 0;

}

提供以下输出:

Is F in pptest? 0

即使&Fpptest之内。
这种方法的问题可能是什么?

2q5ifsrm

2q5ifsrm1#

类型void是一个不完整的类型,因此您可以在if语句中使用带有指针算法的表达式array + k

if(equals(array + k, pelement)) {

无效。
此外,您还需要将存储在数组中的对象的大小传递给函数,这些对象将在具有指针算法的表达式中使用。
使用您的方法时,该函数的声明应类似于标准C函数bsearch,如下所示

void *bsearch(const void *key, const void *base,
              size_t nmemb, size_t size,
              int (*compar)(const void *, const void *));

只有返回类型必须从void *更改为int
这就是函数的声明

int is_in( const void *pvalue, 
           const void *array, 
           size_t nmemb, 
           size_t size, 
           int cmp( const void *, const void *) );

该函数可按以下方式定义

int is_in( const void *pvalue, 
           const void *array, 
           size_t nmemb, 
           size_t size, 
           int cmp( const void *, const void *) )
{
    size_t i = 0;
    
    while ( i < nmemb && cmp( pvalue, ( const char * )array + i * size  )   != 0 ) i++;

    return i != nmemb;
}

一般而言,如果搜索的元素被分别视为小于、匹配或大于数组元素,则比较函数应返回小于、等于或大于零的整数。
在你的例子中,你有一个指针数组,可以指向任意对象,那么如果传递给函数的元素彼此相等,函数应该返回0,或者如果它们彼此不相等,函数应该返回一个正值。

int ptreq( const void *ptr1, const void *ptr2 ) 
{
    return *( const Symbol ** )ptr1 != *( const Symbol ** )ptr2;
}

注意传递的搜索元素must have the type Symbol**'。
这是一个演示程序。

#include <stdio.h>

int is_in( const void *pvalue,
    const void *array,
    size_t nmemb,
    size_t size,
    int cmp( const void *, const void * ) )
{
    size_t i = 0;

    while (i < nmemb && cmp( pvalue, ( const char * )array + i * size ) != 0) i++;

    return i != nmemb;
}

int cmp_ptr( const void *ptr1, const void *ptr2 )
{
    return *( const int ** )ptr1 != *( const int ** )ptr2;
}

int main( void )
{
    int x, y, z;

    int * a[] = { &x, &y, &z };
    const size_t N = sizeof( a ) / sizeof( *a );

    int *pvalue = &y;

    printf( "&y is in the array = %s\n",
        is_in( &pvalue, a, N, sizeof( *a ), cmp_ptr ) ? "true" : "false" );

    int v;
    pvalue = &v;

    printf( "&v is in the array = %s\n",
        is_in( &pvalue, a, N, sizeof( *a ), cmp_ptr ) ? "true" : "false" );
}

程序输出为

&y is in the array = true
&v is in the array = false
h9vpoimq

h9vpoimq2#

传递给void*参数的Symbol**不会在另一端以数组形式出现,除非您将其强制转换为正确的类型。array + k是无效的C,不会在符合标准的编译器上干净地编译。您不能在void*上执行指针运算,也不能在不知道项大小的情况下循环访问它所指向的内容-在“这就是为什么qsort把它作为参数的原因。
正确编写的标准C函数可能如下所示:

#include <stddef.h>
#include <stdbool.h>

bool is_in (const void* array, 
            size_t      n_items,
            size_t      item_size,
            const void* element,
            int (*equals)(const void*, const void*))
{
    unsigned char* byte = array;
    for(size_t i=0; i<n_items; i++)
    {
       if(equals(&byte[i*item_size], element))
       {
         return true;
       }
    }

    return false;
}

int symbol_equal (const void* obj1, const void* obj2)
{
  const Symbol* s1 = obj1;
  const Symbol* s2 = obj2;
  ...
  // in case you passed an array of pointers, then an extra level of dereferencing here
}
gc0ot86w

gc0ot86w3#

正如其他人所指出的,这个问题是由于试图对void*执行加法而引起的,标准C中没有定义void*。虽然其他答案通过传递项大小来避免这个问题,就像qsort()的情况一样,但我设法通过将arrayk分离为equals()例程的不同参数来解决这个问题。然后在强制转换为适当的非void*类型之后执行指针运算。

int is_in(void* list, int size, void* pelement, int (*equals)(void* this, int k, void* that)) {
    for(int k = 0; k < size; k++) {
        if(equals(list, k, pelement)) {
            return 1;
        }
    }
    return 0;
}
int ptreq(void* ptr1, int k, void* ptr2) {
    return (*(((Symbol**) ptr1) + k) == (Symbol*) ptr2);
}

相关问题