通过函数传递 void 指针,然后强制转换为所需的类型

Passing void pointer through function and casting later to the wanted type

提问人:JohnDoe 提问时间:9/14/2023 最后编辑:JohnDoe 更新时间:9/15/2023 访问量:126

问:

我正在练习 C,但使用指针仍然不是很安全。在那里我写了一个代码(没有编译它,就像伪代码一样) 假设我有这样的东西:

typedef struct
{
   uint16 A;
   uint16 B
   uint16 C;
   uint16 D;
   uint16 E
}myDataStruct;

typedef struct _data_info
{
    uint8t*           bufAddr;
    uint32t           bufSize;

}data_info;


int getInfo(void* pMsg, int32 size); //pMsg points to the buffer of the message and size is the size of the buffer. This function receives a message from a message queue and is defined in the OS. It is similar like an mq_receive() known from Posix


static void myFunction(void* pData);

static void OperateOnData(myDataStruct* pData)


main()
{
    data_info inf;   //Declare instance of data_info

    getInfo(&inf,sizeof(data_info))   //Function holds the information where data of interest is stored

    myFunction((void*)data_info.bufAddr);  //Here I want to pass the address of the data in order to operate on it
}

static void myFunction(void* pData)
{ 
  /*Furtheremore I want to pass the pointer to next function where the data will be used*/
  OperateOnData((myDataStruct*)pData)
}

static void OperateOnData(myDataStruct* pData)
{
    /*do something with pData*/

    uint8 x;
    uint8 y;

    x = pData->A
    Y = pData->B 

 /*....*/
 /*....*/
}

这里的问题是我是否正确地提供了来自 myFunction() 调用的数据 OperateOnData() 函数,这样我就可以使用保存在 getInfo() 返回的地址中的数据。是否可以将 myFunction 的参数设置为 *void,然后将其转换为所需的类型,例如 myDataStruct ?

C 指针 转换

评论

2赞 Support Ukraine 9/14/2023
“没有编译”为什么不呢?那会告诉你至少一个错误
1赞 JohnDoe 9/14/2023
@wohlstad:谢谢,我纠正了这一点。我将假设在调用函数时不知道类型。因此,我提供了 void 指针。
1赞 Support Ukraine 9/14/2023
是有什么特别的还是只是?VOIDvoid
1赞 Lundin 9/14/2023
讨论你的伪代码是否“正确”是毫无意义的——显然不是。这里重要的是你要解决什么潜在的问题 - 某种类型的泛型分配器?创建一个具有各种相关函数的“ADT”结构?别的?
1赞 Support Ukraine 9/14/2023
@JohnDoe 查看:第二个参数是指向 int32 的指针。它不是 int32。顺便说一句:退货int getInfo(VOID * pMsg, int32 *size);sizeofsize_t

答:

0赞 0___________ 9/14/2023 #1

一般来说,指针双关语是危险的,为了安全起见,你应该使用,你不会调用未定义的行为。memcpy

static void myFunction(void* pData)
{ 
    myDataStruct Data;
    memcpy(&Data, pData, sizeof(Data));
    OperateOnData(&Data);
}
0赞 chux - Reinstate Monica 9/14/2023 #2

我是否正确地提供了 down to function 调用的数据,以便我可以使用保存在 (?) 返回的地址中的数据myFunction()OperateOnData()getInfo()

不一定。

myFunction((void*)data_info.bufAddr);(更简单地编码为不需要强制转换)确实传递了指针,但缺少有关指向的数据中有多少是有效的信息。myFunction(data_info.bufAddr);void *myFunction()data_info.bufAddr


是否可以将参数设置为as,然后稍后将其转换为所需的类型,例如?myFunction*voidmyDataStruct

是的,没关系。请注意,不需要强制转换即可从 到 。void *myDataStruct *


在破坏类型检查时,还涉及其他注意事项。在不了解 OP 的更高层次目标的情况下,仍然不清楚如何为一个好的替代方案提供建议。

有时类型失败是好的。通常不是。

0赞 arfneto 9/15/2023 #3

发布的代码有很多很多问题。至少考虑:

  • main返回一个int
  • 使用for函数将对您不利,因为您不能简单地编写/并使用十几个小程序对其进行测试,而只需...如果你真的需要这个,你可以添加另一个时间。staticstuff.hstuff.cmain
  • 所有类型都是错误的。尝试而不是作为例子stdintuint8_tuint8t
  • 永远避免使用以下划线开头的类型。它们保留供库中使用
  • 使用以大写字母命名事物的约定typedef
  • 避免骆驼案例的东西:)这是 Java 的东西
  • 让匿名者,如果没有内部自我引用struct
typedef struct _data_info
{
    uint8t*           addr;
    uint32t           size;
}data_info;

可能是

typedef struct
{
    uint8_t*          addr;
    uint32_t          size;
}  Info;

在你传递一个指向事物的指针,而不是名称,所以myFunctiontypedef

myFunction(
        (void*)data_info.bufAddr);

应该是

        myFunction((void*)inf);

或类似的东西。而且可能是错的: 您声明

int getInfo(void* pMsg, int32_t size);

但顾名思义,您正在接收某些东西,因此通常该函数将分配该事物的缓冲区并返回它。由于您正在将值传递给您,因此不会取回该值。 也应该是一个指针。infogetInfosizesize

即使这样也不好。通常的C

        Info* get_info();

这样你就可以---你猜怎么着?---指向某些信息的指针。

  • 如果您知道事物可能具有的类型,请不要使用。 不是朋友voidvoid
  • DO NOT RETURN :返回状态代码,或指向编译器可以为您检查的命名事物的指针。void
  • 在很多时候,强制转换与编译器无关,但它是一个警告,是给代码读者的一段文档,在第二天给你。
    例如,如果你在一个函数中将 4 种类型的缓冲区转换为你期望的类型,那么它可以为你省去很多麻烦,可能是六次:一个可以称之为浪费打字,另一个可以称之为保险。反转一对这些指针的错误可能会花费你一天的时间,一份工作,谁知道呢?只有在学生代码中,参数才像 .在生产代码中,很多时候 是复杂表达式甚至函数调用的结果,如 .仅通过查看调用本身,您可能无法看到反转某些分配的错误......
    mallocvoid*mallocn * sizeof(t)sizemalloc(f())

您的代码几乎可以编译

这是我们在这里讨论的基础

#include <stdint.h>
#include <stdio.h>

typedef struct
{
  uint16_t            A;
  uint16_t            B;
  uint16_t            C;
  uint16_t            D;
  uint16_t            E;
} myDataStruct;

typedef struct _data_info
{
  uint8_t*  bufAddr;
  uint32_t bufSize;

} data_info;

int getInfo(
  void* pMsg,
  int32_t size);  // pMsg points to the buffer of the
                // message and size is the size of the
                // buffer. This function receives a
                // message from a message queue and is
                // defined in the OS. It is similar like
                // an mq_receive() known from Posix

static void myFunction(void* pData);

static void OperateOnData(myDataStruct* pData);

int main(void)
{
  data_info inf;  // Declare instance of data_info

  getInfo(&inf.bufAddr,inf.bufSize);  // Function holds the
                           // information
                       // where data of interest is stored

      myFunction(
          &inf);  // Here I want to pass the
                          // address of the data in order
                          // to operate on it
}

static void myFunction(void* pData)
{
      /*Furtheremore I want to pass the pointer to next
       * function where the data will be used*/
      OperateOnData((myDataStruct*)pData);
};

static void OperateOnData(myDataStruct* pData)
{
      /*do something with pData*/

      uint8_t x;
      uint8_t y;

      // x = pData->A Y = pData->B

      /*....*/
      /*....*/
};

一个例子

我将添加有关处理此类消息的常用方法的示例代码,其中包含完整的注释代码

数据

typedef struct
{
    uint32_t size;
    uint8_t* addr;
} Info;

Info* free_info(Info*);
Info* get_info();
int   my_fn1(Info* data);
int   my_fn2(Info* data);
int   operate_on(Info*[], int (*fn)(Info*));
int   show_info(Info* data, const char* msg);

最后,所有这些可能的实现都在最后

Info是数据包。 这是一个工厂函数,它返回一条 10 到 50 个字节的随机短消息,其中 90% 的字节最多为 20 个字节,因此我们可以轻松测试。它就在里面,你会打电话给.get_info()get_infomq_receive()

my_fn1my_fn2

此功能只需用“f1”、“F1”、“f2”或“F2”标记消息,以便于测试。

show_info

这个只是在屏幕上显示一条带有可选文本的消息。Info

operate_on就像一个javascriptforEach

在网络中,通常以指针流的形式接收消息,并用标记结束。这就是它的作用:它接受一个指针数组并通过提供的函数进行处理。这就像 javascript 中对 Array 执行的操作,或者在NULLoperate_onforEachstd:for_eachC++

int operate_on(Info* data[], int (*fn)(Info*))
{
    if (data == NULL) return -1;
    size_t i = 0;
    Info**  p = (Info**)data;
    while (*p != NULL) fn(*p++);
    return 0;
}

而且并不多。为了测试这一点,我编写了 mininum 2 函数。下面是这样使用的,如示例所示:operate_on

    Info*     block[9] = {0};
    const int tst_size =
        sizeof(block) / sizeof(block[0]) - 1;
    int res = 0;
    printf("\t%d messages in this test\n", tst_size);
    for (int i = 0; i < tst_size; i += 1)
        block[i] = get_info(), show_info(block[i], "[new] ");
    block[tst_size] = NULL;

    res = operate_on(block, my_fn1);
    res = operate_on(block, my_fn2);

    for (int i = 0; i < tst_size; i += 1)
        show_info(block[i], NULL);

您只需传递 2 个指针:一个用于消息数组,另一个用于函数地址

C 就是为这种任务而写的。

main用于测试

{
    srand(230913);
    Info*     block[9] = {0};
    const int tst_size =
        sizeof(block) / sizeof(block[0]) - 1;
    int res = 0;
    printf("\t%d messages in this test\n", tst_size);
    for (int i = 0; i < tst_size; i += 1)
        block[i] = get_info(),
        show_info(block[i], "[new] ");
    block[tst_size] = NULL;

    // call f1 f2
    printf("\tCall my_fn1() for all messages\n");
    res = operate_on(block, my_fn1);
    printf("\tCall my_fn2() for all messages\n");
    res = operate_on(block, my_fn2);
    printf("\tShow all %d messages\n", tst_size);
    for (int i = 0; i < tst_size; i += 1)
        show_info(block[i], NULL);

    // again
    printf("\tCall my_fn1() for all messages\n");
    res = operate_on(block, my_fn1);
    printf("\tCall my_fn2() for all messages\n");
    res = operate_on(block, my_fn2);
    printf("\tShow all %d messages\n", tst_size);
    for (int i = 0; i < tst_size; i += 1)
        show_info(block[i], NULL);

    // free all
    for (int i = 0; i < tst_size; i += 1) free(block[i]);
    return 0;
};

代码

  • 通过调用Infoget_info
  • 在屏幕上显示消息
  • 用于通过 和 传递所有消息operate_onmy_fn1my_fn2
  • 显示处理后的结果
  • 清除一切

输出

        8 messages in this test
[new] ====> [35 bytes]  "Lorem ipsum dolor sit amet, consec"
[new] ====> [16 bytes]  "Lorem ipsum dol"
[new] ====> [33 bytes]  "Lorem ipsum dolor sit amet, cons"
[new] ====> [12 bytes]  "Lorem ipsum"
[new] ====> [16 bytes]  "Lorem ipsum dol"
[new] ====> [36 bytes]  "Lorem ipsum dolor sit amet, consect"
[new] ====> [16 bytes]  "Lorem ipsum dol"
[new] ====> [17 bytes]  "Lorem ipsum dolo"
        Call my_fn1() for all messages
        Call my_fn2() for all messages
        Show all 8 messages
====> [35 bytes]        "F1F2m ipsum dolor sit amet, consec"
====> [16 bytes]        "F1F2m ipsum dol"
====> [33 bytes]        "F1F2m ipsum dolor sit amet, cons"
====> [12 bytes]        "F1F2m ipsum"
====> [16 bytes]        "F1F2m ipsum dol"
====> [36 bytes]        "F1F2m ipsum dolor sit amet, consect"
====> [16 bytes]        "F1F2m ipsum dol"
====> [17 bytes]        "F1F2m ipsum dolo"
        Call my_fn1() for all messages
        Call my_fn2() for all messages
        Show all 8 messages
====> [35 bytes]        "f1f2m ipsum dolor sit amet, consec"
====> [16 bytes]        "f1f2m ipsum dol"
====> [33 bytes]        "f1f2m ipsum dolor sit amet, cons"
====> [12 bytes]        "f1f2m ipsum"
====> [16 bytes]        "f1f2m ipsum dol"
====> [36 bytes]        "f1f2m ipsum dolor sit amet, consect"
====> [16 bytes]        "f1f2m ipsum dol"
====> [17 bytes]        "f1f2m ipsum dolo"

完整代码

#include <memory.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct
{
    uint32_t size;
    uint8_t* addr;
} Info;

Info* free_info(Info*);
Info* get_info();
int   my_fn1(Info* data);
int   my_fn2(Info* data);
int   operate_on(Info*[], int (*fn)(Info*));
int   show_info(Info* data, const char* msg);

int main(void)
{
    srand(230913);
    Info*     block[9] = {0};
    const int tst_size =
        sizeof(block) / sizeof(block[0]) - 1;
    int res = 0;
    printf("\t%d messages in this test\n", tst_size);
    for (int i = 0; i < tst_size; i += 1)
        block[i] = get_info(),
        show_info(block[i], "[new] ");
    block[tst_size] = NULL;

    // call f1 f2
    printf("\tCall my_fn1() for all messages\n");
    res = operate_on(block, my_fn1);
    printf("\tCall my_fn2() for all messages\n");
    res = operate_on(block, my_fn2);
    printf("\tShow all %d messages\n", tst_size);
    for (int i = 0; i < tst_size; i += 1)
        show_info(block[i], NULL);

    // again
    printf("\tCall my_fn1() for all messages\n");
    res = operate_on(block, my_fn1);
    printf("\tCall my_fn2() for all messages\n");
    res = operate_on(block, my_fn2);
    printf("\tShow all %d messages\n", tst_size);
    for (int i = 0; i < tst_size; i += 1)
        show_info(block[i], NULL);

    // free all
    for (int i = 0; i < tst_size; i += 1) free(block[i]);
    return 0;
};

Info* free_info(Info* info)
{
    if (info == NULL) return NULL;
    free(info->addr);
    free(info);
    return NULL;  // to invalidate the pointer
}

Info* get_info()
{
    // if it is the case, here is the place to call
    // mq_receive() and use the received data to fill
    // our `Info` buffer

    // by courtesy of lipsum.com
    const char* lorem =
        "Lorem ipsum dolor sit amet, consectetur "
        "adipiscing elit. Proin ornare, felis et tristique "
        "varius, libero eros tincidunt est, eleifend "
        "accumsan turpis magna eu elit. Donec quis "
        "vehicula quam. Phasellus sit amet convallis leo. "
        "Nullam sagittis egestas leo, et sagittis purus "
        "lobortis ut. Mauris a neque vitae diam elementum "
        "elementum quis";  // 336 bytes
    // now around 90% of the messages will have 10 to 20
    // bytes in size and the rest from 30 to 50 bytes
    Info* info = malloc(sizeof(Info));
    if (rand() % 10 < 9)
        info->size = rand() % 11 + 10;
    else
        info->size = rand() % 21 + 30;
    info->addr = malloc(info->size);
    memcpy(info->addr, lorem, info->size);
    *(info->addr + info->size - 1) = 0;  // we want a string
    return info;
}

int my_fn1(Info* data)
{
    if (data == NULL) return -1;
    switch (data->addr[0])
    {
        case 'F':
            data->addr[0] = 'f';
            data->addr[1] = '1';
            break;
        default:
            data->addr[0] = 'F';
            data->addr[1] = '1';
            break;
    }
    return 0;
}

int my_fn2(Info* data)
{
    if (data == NULL) return -1;
    switch (data->addr[2])
    {
        case 'F':
            data->addr[2] = 'f';
            data->addr[3] = '2';
            break;
        default:
            data->addr[2] = 'F';
            data->addr[3] = '2';
            break;
    }
    return 0;
}

int show_info(Info* data, const char* msg)
{
    if (data == NULL) return -1;
    if (msg != NULL) printf("%s", msg);
    printf(
        "====> [%u bytes]\t\"%s\"\n", data->size,
        data->addr);
    return 0;
}

int operate_on(Info* data[], int (*fn)(Info*))
{
    if (data == NULL) return -1;
    size_t i = 0;
    Info** p = (Info**)data;
    while (*p != NULL) fn(*p++);
    return 0;
}