交换未知类型的值是否安全

时间:2017-10-17 19:40:49

标签: c c99

假设我们有以下结构:

typedef struct array
{
    size_t  sz_of  // sizeof an object;
    size_t  count;
    void*   objects;
}Array;

好吧,一个可以包含一组任意类型的数组。以下功能旨在应用于此结构:

Array* ArrayNew(size_t len, size_t sz_of){
    Array* a = malloc(sizeof(Array));
    if(a == NULL)
        return NULL;

    a->sz_of = sz_of;
    a->count = len;
    a->objects = malloc(len * sz_of);
    if(a->objects == NULL){
        free(a);
        return NULL;
    }

    return a;
}

void ArrayDestroy(Array* a){
    free(a->objects);
    free(a);
}

void* ArrayGet(Array* a, size_t i){
    if(i > a->count)
        return NULL;

    return (a->objects + a->sz_of * i);
}

void ArraySet(Array* a, void* v, size_t i){
    if(i > a->count)
        return;

    memcpy(a->objects + a->sz_of * i, v, a->sz_of);
}

我们有一个功能:

  • 充当构造函数;
  • 充当析构函数;
  • 充当吸气剂;
  • 充当制定者。

我添加了一个交换数组的两个元素的函数,但我不确定我是否正确执行。由于char长度为1个字节,因此我暂时将一个元素存储在char数组中sz_of长:

void ArraySwap(Array* a, size_t x, size_t y){
    if(x < a->count && y < a->count){
        char t[a->sz_of];
        memcpy(t, a->objects + a->sz_of * x, a->sz_of);
        memcpy(a->objects + a->sz_of * x, a->objects + a->sz_of * y, a->sz_of);
        memcpy(a->objects + a->sz_of * y, t, a->sz_of);
    }
}

xy是必须交换的对象的索引。

它在我的配置上运行良好,但它是不安全还是非标准的继续进行方式?在全球范围内,如果你看到一些奇怪的,不安全的或非标准的东西,你能指出我吗?

感谢您的回答。

2 个答案:

答案 0 :(得分:3)

代码依赖char*void*的指针数学一样。有些编译器将void*指针数学视为char*,但最好不要假设非标准行为。

a->objects + a->sz_of * x // not potable.

而是使用unsigned char *char *并考虑固定大小的缓冲区@supercat。这避免了使用可变长度数组(VLA),这是一个无限制的,只有可选支持C11。

void ArraySwap(Array* a, size_t x, size_t y){
  if(x < a->count && y < a->count){
    unsigned char *xp = a->objects;
    xp += x*a->sz_of;
    unsigned char *yp = a->objects;
    yp += y*a->sz_of;
    unsigned char buf[64];
    for (size_t i=0; i<a->sz_of; i += sizeof buf) {
      size_t sz = a->sz_of - i;
      if (sz > sizeof buf) {
        sz = sizeof buf;
      }
      memcpy(t, xp, sz);
      memcpy(xp, yp, sz);
      memcpy(yp, t, sz);
      xp += sz;
      yp += sz;
    }
  }
}

答案 1 :(得分:2)

您不必担心临时阵列的大小。在C99中,it is always true sizeof(char) == 1

这意味着堆栈大小上的t数组将为a->sz_of个字节。

可能会出现另一个问题。在堆栈上分配t时,您使用名为VLA (variable-length array)的C99功能。这可能在两种情况下存在问题:

  1. 您想支持C89编译器(我假设您不支持)。
  2. 您的对象大小可能太大,堆栈上没有足够的空间,导致未定义的行为。如果您担心这种情况,并且想要正确处理它,最好在堆上分配内存(使用malloc),因为这样可以检查内存分配中的错误(将结果与NULL进行比较)。
  3. 最后,这可能不是问题,但在ArrayGet的实现中使用ArraySwap函数似乎更好,而不是手动计算元素`地址。