按位左移一个值到由 void* 指向的动态分配的内存中

Bit-wise left shifting a value into a dynamically allocated memory pointed by void*

我正在尝试为 Libvirt API 函数创建一个 CPU-Map。这个想法是根据物理CPUs的数量,我分配1、2或4字节的内存(最多支持32 CPUs)。比如一个平台有16个CPUs,我分配2个字节。

分配内存后,我需要设置 CPU 位。例如:如果我平台有16个CPU,我想给第16个CPUpin一个VirtualCPU,我需要在分配的内存中设置MSB。请参阅下面的代码片段。

void *cpuMap;

switch (pCpus/8) {
case 0:
case 1:
    size = sizeof(unsigned char);
    cpuMap = (unsigned char*) malloc(size);
    break;
case 2:
    size = sizeof(unsigned short int);
    cpuMap = (unsigned short int*) malloc(size);
    break;
case 3:
case 4:
default:
    size = sizeof(unsigned int);
    cpuMap = (unsigned int*) malloc(size);
    break;
}

*cpuMap = 0x1 << cpu_number; //error: invalid use of void expression

我在编译过程中遇到以下错误

vcpu_scheduler.c:317:3: warning: dereferencing ‘void *’ pointer
  317 |   *cpuMap = 0x1 << cpu_number;
      |   ^~~~~~~
vcpu_scheduler.c:317:11: error: invalid use of void expression
  317 |   *cpuMap = 0x1 << cpu_number;
      |           ^
make: *** [Makefile:4: compile] Error 1

感谢任何帮助。

您不能引用 void*,因为它是一个不完整的类型,无法通过设计完成。必须将其转换为适当的类型并分配正确的值。

例如:

case 2:
    size = sizeof(unsigned short int);
    cpuMap = malloc(size);
    *(unsigned short int*)cpuMap = 0x1 << cpu_number;
    break;

为其他情况添加类似代码。

请注意,设置后 cpuMap 只能由用于初始化它的类型访问(+ 少数例外)以避免违反严格的别名规则。

您最好总是使用 unsigned char(或 uint8_t),并在需要多个字节时分配多个字节(和索引):

unsigned char *cpuMap;

cpuMap = malloc((pCpus + 7U)/8U);  // round up to a mulitple of 8
memset(cpuMap, 0, (pCpus + 7U)/8U);

cpuMap[cpu_number / 8U] |= 1U << (cpu_number % 8U);

分配 1 或 2 个字节没有意义。始终分配 4 个字节。分配更少的内存不会为您节省任何内存,反而会使代码无缘无故地复杂化。

对 4 字节内存使用动态内存分配也没有(或非常非常小)意义。您存储其引用的指针在 64 位机器上的大小将是两倍 :)

要确定类型有多少位,请使用来自标准

的固定大小的整数
uint32_t cpumap = 0;

/* ... */

cpumap |= 0x1UL << cpu_number;

使用void *会破坏类型安全。最好使用单一类型(例如)uint8_t——根据 CPU 数量使用 uint8_t/uint16_t/uint32_t 在速度方面几乎没有优势,并使代码更复杂。

我们必须对 CPU 数进行四舍五入以获得字节数。

没有真正需要使用 malloc。只需使用基于最大可能 CPU 的固定大小(例如 32、64、128、256)。对于 32 个 CPU 的[最大]用例,始终使用四个字节并不是向量大小的真正问题。


当我做位向量时,我更喜欢使用macros/functions来隔离操作。

以下是我过去使用过的一些宏:

// btv.h -- bit vector primitives

// NOTE: by using 0x80, we are compatible with perl's vec function

typedef unsigned char btv_t;

// number of bytes for vector
#define BTVSIZE(_bit) \
    (((_bit) + 7) >> 3)

// index/offset into bit vector
#define BTVOFF(_bit) \
    ((_bit) >> 3)

// bit vector bit mask
#define BTVMSK(_bit) \
    (0x80 >> ((_bit) >> 3))

// bit vector byte pointer
#define BTVPTR(_sym,_bit) \
    (((btv_t *) _sym) + BTVOFF(_bit))

// set bit in vector
#define BTVSET(_sym,_bit) \
    *BTVPTR(_sym,_bit) |= BTVMSK(_bit)

// clear bit in vector
#define BTVCLR(_sym,_bit) \
    *BTVPTR(_sym,_bit) &= ~BTVMSK(_bit)

// test bit in vector
#define BTVTST(_sym,_bit) \
    (*BTVPTR(_sym,_bit) & BTVMSK(_bit))

// set/clear bit in vector
#define BTVFLG(_sym,_bit,_set) \
    do { \
        btv_t *__btvptr__ = BTVPTR(_sym,_bit); \
        btv_t __btvmsk__ = BTVMSK(_bit); \
        if (_set) \
            *__btvptr__ |= __btvmsk__; \
        else \
            *__btvptr__ &= ~__btvmsk__; \
    } while (0)

这是用于 cpus 的函数包装器:

// cpumap.c -- CPU mask primitives

#include "btv.h"

// pick whatever maximum number of CPUs you want ...
#define CPUMAX          256

typedef btv_t cpumap_t[BTVSIZE(CPUMAX)];

void
cpumapset(cpumap_t map,unsigned int cpuno)
{

    BTVSET(map,cpuno);
}

void
cpumapclr(cpumap_t map,unsigned int cpuno)
{

    BTVCLR(map,cpuno);
}

btv_t
cpumaptst(cpumap_t map,unsigned int cpuno)
{

    return BTVTST(map,cpuno);
}

libvirt API 提供了旨在与固定 API 一起使用的宏,以简化此操作:

unsigned char *cpuMap = malloc(VIR_CPU_MAPLEN(pCpus));
VIR_USE_CPU(cpuMap, cpu_number);