提问人:JohnDoe 提问时间:9/14/2023 最后编辑:JohnDoe 更新时间:9/15/2023 访问量:126
通过函数传递 void 指针,然后强制转换为所需的类型
Passing void pointer through function and casting later to the wanted type
问:
我正在练习 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 ?
答:
一般来说,指针双关语是危险的,为了安全起见,你应该使用,你不会调用未定义的行为。memcpy
static void myFunction(void* pData)
{
myDataStruct Data;
memcpy(&Data, pData, sizeof(Data));
OperateOnData(&Data);
}
我是否正确地提供了 down to function 调用的数据,以便我可以使用保存在 (?) 返回的地址中的数据
myFunction()
OperateOnData()
getInfo()
不一定。
myFunction((void*)data_info.bufAddr);
(更简单地编码为不需要强制转换)确实传递了指针,但缺少有关指向的数据中有多少是有效的信息。myFunction(data_info.bufAddr);
void *
myFunction()
data_info.bufAddr
是否可以将参数设置为as,
然后稍后将其转换为所需的类型,例如?myFunction
*void
myDataStruct
是的,没关系。请注意,不需要强制转换即可从 到 。void *
myDataStruct *
在破坏类型检查时,还涉及其他注意事项。在不了解 OP 的更高层次目标的情况下,仍然不清楚如何为一个好的替代方案提供建议。
有时类型失败是好的。通常不是。
发布的代码有很多很多问题。至少考虑:
main
返回一个int
- 使用for函数将对您不利,因为您不能简单地编写/并使用十几个小程序对其进行测试,而只需...如果你真的需要这个,你可以添加另一个时间。
static
stuff.h
stuff.c
main
- 所有类型都是错误的。尝试而不是作为例子
stdint
uint8_t
uint8t
- 永远避免使用以下划线开头的类型。它们保留供库中使用
- 使用以大写字母命名事物的约定
typedef
- 避免骆驼案例的东西:)这是 Java 的东西
- 让匿名者,如果没有内部自我引用
struct
typedef struct _data_info
{
uint8t* addr;
uint32t size;
}data_info;
可能是
typedef struct
{
uint8_t* addr;
uint32_t size;
} Info;
在你传递一个指向事物的指针,而不是名称,所以myFunction
typedef
myFunction(
(void*)data_info.bufAddr);
应该是
myFunction((void*)inf);
或类似的东西。而且可能是错的: 您声明
int getInfo(void* pMsg, int32_t size);
但顾名思义,您正在接收某些东西,因此通常该函数将分配该事物的缓冲区并返回它。由于您正在将值传递给您,因此不会取回该值。 也应该是一个指针。info
getInfo
size
size
即使这样也不好。通常的C
Info* get_info();
这样你就可以---你猜怎么着?---指向某些信息的指针。
- 如果您知道事物可能具有的类型,请不要使用。 不是朋友。
void
void
- DO NOT RETURN :返回状态代码,或指向编译器可以为您检查的命名事物的指针。
void
- 在很多时候,强制转换与编译器无关,但它是一个警告,是给代码读者的一段文档,在第二天给你。
例如,如果你在一个函数中将 4 种类型的缓冲区转换为你期望的类型,那么它可以为你省去很多麻烦,可能是六次:一个可以称之为浪费打字,另一个可以称之为保险。反转一对这些指针的错误可能会花费你一天的时间,一份工作,谁知道呢?只有在学生代码中,参数才像 .在生产代码中,很多时候 是复杂表达式甚至函数调用的结果,如 .仅通过查看调用本身,您可能无法看到反转某些分配的错误......malloc
void*
malloc
n * sizeof(t)
size
malloc(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_info
mq_receive()
my_fn1
和my_fn2
此功能只需用“f1”、“F1”、“f2”或“F2”标记消息,以便于测试。
show_info
这个只是在屏幕上显示一条带有可选文本的消息。Info
operate_on
就像一个javascriptforEach
在网络中,通常以指针流的形式接收消息,并用标记结束。这就是它的作用:它接受一个指针数组并通过提供的函数进行处理。这就像 javascript 中对 Array 执行的操作,或者在NULL
operate_on
forEach
std:for_each
C++
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;
};
代码
- 通过调用
Info
get_info
- 在屏幕上显示消息
- 用于通过 和 传递所有消息
operate_on
my_fn1
my_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;
}
评论
VOID
void
int getInfo(VOID * pMsg, int32 *size);
sizeof
size_t