解决方案"取消引用`void*'指的"警告在结构中的C?
-
08-07-2019 - |
题
我是试图建立一个伪超级结构打印系列结构。我基本的 结构如下。
/* Type 10 Count */
typedef struct _T10CNT
{
int _cnt[20];
} T10CNT;
...
/* Type 20 Count */
typedef struct _T20CNT
{
long _cnt[20];
} T20CNT;
...
我创建了以下的结构以打印的阵列的上述结构。我得到了取消引用无效的指针的错误,同时编写下代码段。
typedef struct _CMNCNT
{
long _cnt[3];
} CMNCNT;
static int printCommonStatistics(void *cmncntin, int cmncnt_nelem, int cmncnt_elmsize)
{
int ii;
for(ii=0; ii<cmncnt_nelem; ii++)
{
CMNCNT *cmncnt = (CMNCNT *)&cmncntin[ii*cmncnt_elmsize];
fprintf(stout,"STATISTICS_INP: %d\n",cmncnt->_cnt[0]);
fprintf(stout,"STATISTICS_OUT: %d\n",cmncnt->_cnt[1]);
fprintf(stout,"STATISTICS_ERR: %d\n",cmncnt->_cnt[2]);
}
return SUCCESS;
}
T10CNT struct_array[10];
...
printCommonStatistics(struct_array, NELEM(struct_array), sizeof(struct_array[0]);
...
我的意思是有一个共同的功能打印的所有阵列。请让我知道的正确方式使用它。
更好的帮助。
编辑:参名称变更为cmncntin从cmncnt.对不起,这是拼写错误的错误。
谢谢, 马修力聚研发的
解决方案
我认为你的设计是要失败,但我也不认为其他的答案我看到充分处理更深层次原因。
看来你正在尝试使用的C处理通用类型的东西,总是得到毛茸茸的。你可以做到这一点,如果你是认真的,但这是不容易的,并且在这种情况下,我怀疑,如果这将是值得的。
更深层次的原因:让我们假设,我们让过去的不仅仅是能语(或者勉强超过法)的问题。你的代码显示,T10CNT包含20 int
和T20CNT包含20 long
.在现代64位机,比其他的下Win64- sizeof(long) != sizeof(int)
.因此,代码里面打印功能,应区分取消引用 int
阵列和 long
阵列。在C++、有规则的你不应该试图把多态方式阵列,以及这样的事情是什么。该CMNCNT类型包含3 long
值;不同两个T10CNT和T20CNT结构在数量上,虽然基类型阵列匹配T20CNT.
风格的建议:我强烈建议避免导致强调上的名字。在一般情况下,名字开头强调,保留用于实施使用,并使用作为宏。宏没有尊重的范围;如果执行定义的宏_cnt它会破坏你的代码。有细微差别以什么名称的保留;我可不想进入这些细微的差别。这是很简单的认为名称开始强调保留',以及它会引导你清楚的麻烦。
风格的建议:你打印功能返回的成功无条件的。那是不明智的;你能回应该没什么,这样的呼叫者不必试验成功或失败的(因为它可以永远不会失败).一个精心编谁注意到的功能返回状态将始终试验返回状况和有错误的处理编码。这些代码将永远不会被执行,因此,它是死了,但这是困难的人(或compiler),以确定。
表面修复:暂时,我们可以假设,可以治疗 int
和 long
作为同义词;但你必须出去的习惯思维,他们是同义词,虽然。的 void *
论点是正确的方式说:"这个函数的一个指的不确定的类型"。然而,内部的功能,你需要换一个 void *
特定类型之前你所做的索引。
typedef struct _CMNCNT
{
long count[3];
} CMNCNT;
static void printCommonStatistics(const void *data, size_t nelem, size_t elemsize)
{
int i;
for (i = 0; i < nelem; i++)
{
const CMNCNT *cmncnt = (const CMNCNT *)((const char *)data + (i * elemsize));
fprintf(stdout,"STATISTICS_INP: %ld\n", cmncnt->count[0]);
fprintf(stdout,"STATISTICS_OUT: %ld\n", cmncnt->count[1]);
fprintf(stdout,"STATISTICS_ERR: %ld\n", cmncnt->count[2]);
}
}
(我喜欢这个想法的文件流叫 stout
太。 建议:使用切割'n'和粘贴在真正的源代码--它是安全的!我通常使用"sed 's/^/ /' file.c
"准备码切'n'和粘贴到一个这样的回答。)
什么投线做什么?我很高兴你问了...
- 第一个动作被转换的
const void *
进入一个const char *
;这可以让你做字节大小的操作上的地址。在该天之前的标准C,char *
用于在地方void *
作为普遍的解决机制。 - 下一个操作增加了正确的数字得到的开始
i
第元的阵列的物体的大小elemsize
. - 第二个铸然后告诉编译器"信任我-我知道我在做什么"和"对待这个地址作为地址CMNCNT结构"。
从那里,代码是容易的。注意,由于结构包含CMNCNT long
值,我用 %ld
说出真相来 fprintf()
.
因为你不是修改的数据,在这种功能,它不是一个坏主意使用 const
限定词为我没有。
请注意,如果你要忠诚 sizeof(long) != sizeof(int)
, 然后你需要两个单独的区块代码(我的建议单独的功能),以处理'系列 int
'和'阵列 long
'结构类型。
其他提示
故意将void类型保留为不完整。由此可见,您不能取消引用空指针,也不能使用它的sizeof。这意味着您不能像数组一样使用下标运算符。
向void指针分配内容时,指向该类型的原始指针的任何类型信息都会丢失,因此,只有先将其转换回原始指针类型,才能取消引用。
首先也是最重要的是,您将T10CNT*
传递给函数,但是您尝试将其类型转换(并取消引用)到函数中的CMNCNT*
。这是无效且未定义的行为。
每种类型的数组元素都需要一个函数printCommonStatistics。所以,有一个
printCommonStatisticsInt
,printCommonStatisticsLong
,printCommonStatisticsChar
都因其第一个参数而有所不同(一个采用int*
,另一个采用long*
,依此类推)。您可以使用宏来创建它们,以避免冗余代码。
传递结构本身不是一个好主意,因为从那时起,您必须为结构中包含的数组的每个不同大小定义一个新函数(因为它们都是不同的类型)。因此最好直接传递包含的数组(struct_array[0]._cnt
,为每个索引调用该函数)
将函数声明更改为char *,如下所示: 通用标签
void类型不假定任何特定大小,而char将假定为字节大小。
您不能这样做: 通用标签
如果cmnct是一个空指针。
您必须指定类型。您可能需要重新考虑实施。
功能 通用标签
为我工作。
问题在于,在注释为“ Ptr Line”的行上,代码将指针添加到整数。因为我们的指针是一个char *,所以我们向前移动内存sizeof(char)* ii * cmncnt_elemsize,这是我们想要的,因为char是一个字节。您的代码尝试做向前移动sizeof(void)* ii * cmncnt_elemsize的等效操作,但是void没有大小,因此编译器给了您错误。
我将T10CNT和T20CNT都更改为使用int或long而不是每个使用int或long。您取决于sizeof(int)== sizeof(long)
在这一行: 通用标签
您正试图声明一个名为cmncnt的新变量,但是具有该名称的变量已经作为该函数的参数存在。您可能需要使用其他变量名称来解决此问题。
此外,您可能希望将指向CMNCNT的指针传递给该函数而不是void指针,因为这样编译器将为您执行指针算术,而您不必强制转换它。当您将其全部转换为CMNCNT时,我看不到传递void指针的意义。(顺便说一下,哪个不是数据类型的描述性名称。)
您的表情 通用标签
尝试获取cmncntin [ii * cmncnt_elmsize]的地址,然后将该指针转换为类型(CMNCNT *)。因为cmncntin的类型为void *,所以无法获取cmncntin [ii * cmncnt_elmsize]的地址。
研究C的运算符优先级,并在必要时插入括号。
信息点:内部填充确实可以解决这个问题。
考虑struct {char c [6]; }; -它的sizeof()= 6。但是,如果有这些数组,则每个元素可能会被填充为8字节对齐!
某些组装操作不能优雅地处理未对齐的数据。 (例如,如果一个int跨越两个存储字。)(是的,我之前对此很咬。)
。
第二:过去,我使用大小可变的数组。 (当时我很蠢...)如果您不更改类型,它会起作用。 (或者,如果您具有类型的并集。)
例如: 通用标签
分配为 通用标签
(尽管填充/对齐仍然可以使您烦恼。)
。
第三:考虑: 通用标签
它解决了知道如何打印数据的问题。
。
第四:您可以将int / long数组传递给函数,而不是结构。例如: 通用标签
通过以下方式调用: 通用标签
或者: 通用标签
这比在结构中隐藏数据要好得多。当您将成员添加到您的结构之一时,布局可能会在您的结构之间更改,并且不再保持一致。 (相对于_T10CNT而不是_T20CNT而言,_cnt相对于结构开头的地址可能会发生变化。在此进行有趣的调试时间。具有联合的_cnt有效负载的单个结构可以避免这种情况。)
例如: 通用标签
。
第五: 如果必须使用结构,则C ++,iostream和模板将更易于实现。
例如: 通用标签
但这可能不是您想要的...
C不是我的杯子,但我认为您的问题是“ void * cmncnt”应为CMNCNT * cmncnt。
C程序员,请随时纠正我,并告诉我这就是Java程序员无法拥有美好事物的原因。
这条线很折磨,你不觉得吗? 通用标签
更像 通用标签
或者更好,如果cmncnt_elmsize= sizeof(CMNCNT) 通用标签
这也应该摆脱警告,因为您不再引用空*。
顺便说一句:我不确定您为什么要这样做,但是如果cmncnt_elmsize有时不是sizeof(CMNCNT),并且实际上因调用而异,我建议您重新考虑这种设计。我想可能有一个很好的理由,但是对我来说,它似乎真的很不稳定。我几乎可以保证有更好的方法来设计事物。