我在C中有一个简单的函数,它提供了一个指向数据数组的void*
指针。我知道这个内存块中每个单独数据点的大小(以字节为单位),并且需要保证我可以修改该块中的每个数据点,而不会意外地改变相邻的数据点。在这个例子中,我想将每个值减1。
所有数据点都是8位,16位或32位。
例如:
void myFunction(void* data, size_t arraySize, size_t widthPerDataPoint)
{
if(!data)
return -1;
size_t w = widthPerDataPoint;
int numPoints = arraySize / widthPerDataPoint;
int i;
for(i=0; i<numPoints; i++)
{
if(w==1) // 8 bit
(*((int8_t*)data + i))--;
else if(w==2) // 16 bit
(*((int16_t*)data + i))--;
else if(w==4) // 32 bit
(*((int32_t*)data + i))--;
}
}
不幸的是,根据C99规范,int8_t等数据类型仅保证其最小大小,而不是精确大小。有没有办法重新建立和修改数据,并保证我不会粉碎我的阵列或触摸相邻的数据点?此外,是否有一种等效技术可以用于其他数据宽度(即:24位,60位等)?
答案 0 :(得分:2)
根据定义,通过 n 递增T*
类型的指针会将其移位n * sizeof(T)
个字节。因此,编译器会保证一致性。不用担心。
答案 1 :(得分:2)
int8_t
保证恰好是8位,如果CHAR_BIT==1
,则恰好为1个字节。
引用最新C标准的N1570草案,第7.20.1.1节:
typedef名称 int N _t 指定有符号整数类型 宽度 N ,无填充位和二进制补码 表示。因此, int8_t 表示这种有符号整数类型 宽度恰好为8位。
虽然为了您的目的,使用uint8_t
,uint16_t
等可能更有意义。
如果实现不支持具有所需特征的类型,则不会定义它们;您可以通过检查来检测到这一点,例如:
#include <stdint.h>
#ifdef UINT8_MAX
/* uint8_t exists */
#else
/* uint8_t doesn't exist */
#endif
(如果CHAR_BIT != 8
,则不会定义int8_t
和uint8_t
。)
标准仅保证最小尺寸的[u]intleast_N_t
和[u]intfast_t
类型。
您必须保证阵列及其中的偏移量与您用于访问它的类型正确对齐。我认为你已经在照顾它了。
答案 2 :(得分:1)
代码似乎并非完全不合理。我个人可能会这样做:
switch(widthPerDataPoint)
{
case 1:
{
int8_t *dptr = data;
for(i = 0; i < numPoints; i++)
dptr[i]--;
}
break;
case 2:
{
int16_t *dptr = data;
for(i = 0; i < numPoints; i++)
dptr[i]--;
}
break;
case 4:
{
int32_t *dptr = data;
for(i = 0; i < numPoints; i++)
dptr[i]--;
}
break;
default:
fprintf(stderr, "Someone gave the wrong width - width=%d\n",
widthPerDatapoint);
break;
}
这里的优点是你不会在每个循环中得到一堆条件。编译器可能会对其进行排序,但我并不总是相信编译器能够解决这些问题 - 我认为它也有点清晰。
答案 3 :(得分:-1)
以下情况如何?
将值复制到任何大小的本地int
对局部变量执行递减
使用适当的位掩码将阵列中的位置清零 (例如,8位的~0xFF)
'和'将局部变量返回到数组中。