简体   繁体   English

空指针算术和取消引用

[英]Void pointer arithmetic and dereference

I was looking at a quick sort implementation for generic purpose and one of the parameters of the quick sort function was a void pointer, and I saw the ff arithmetics on void pointers, so I was wondering what that actually does and if it's even possible?我正在研究一个用于通用目的的快速排序实现,快速排序 function 的参数之一是一个void指针,我看到了 void 指针上的 ff 算术,所以我想知道它实际上是做什么的,如果它甚至可能吗?

void _qsort(void *v, int size, int left, int right, int (*comp)(void *, void *));

void swap(void *v1, void *v2, int size) {
    char buffer[size];
    memcpy(buffer, v1, size);
    memcpy(v1, v2, size);
    memcpy(v2, buffer, size);
}

This part appears in the body of the quick sort defined above:这部分出现在上面定义的快速排序的主体中:

    void *vl = (char *)(v + (left * size));
    void *vr = (char *)(v + (mid * size));
    swap(vl, vr, size);

so from above code,所以从上面的代码,

  1. How is it possible to do arithmetic on void pointer v , like v + (left * size) ?如何对void指针v进行算术运算,例如v + (left * size)
  2. What does void *vl = (char *)(v + (left * size)); void *vl = (char *)(v + (left * size));是什么意思? part mean?部分是什么意思? isn't already casted to char pointer, if so why are we assigning it to a void pointer?尚未转换为char指针,如果是,我们为什么要将它分配给void指针?
  3. In the swap part, what exactly is happening, like are the vl and vr changing their memory location, value or something else?swap部分,到底发生了什么,比如vlvr是否改变了它们的 memory 位置、值或其他什么?

You assume right: arithmetics on void pointers is not allowed by the C Standard.您假设正确:C 标准不允许对void指针进行算术运算。

The program you are looking at uses a common compiler extension supported by gcc , clang , tcc and many others, that implements arithmetics on void pointers as if they were byte pointers.您正在查看的程序使用gccclangtcc和许多其他程序支持的通用编译器扩展,它在void指针上实现算术,就好像它们是字节指针一样。 With this extension, v + (left * size) behaves as有了这个扩展, v + (left * size)表现为

    (void *)((char *)v + (left * size))

So the declaration void *vl = (char *)(v + (left * size));所以声明void *vl = (char *)(v + (left * size)); is equivalent to:相当于:

    void *vl = (char *)((void *)((char *)v + (left * size)));

Note that the whole expression is cast implicitly to (void *) in C.请注意,整个表达式在 C 中被隐式转换为(void *)

This declaration can be simplified as:这个声明可以简化为:

    void *vl = (char *)v + left * size;

This is probably what the programmer meant to write and their mistake went unreported because the compiler allows void * arithmetics with exactly the same effect.这可能是程序员想要写的,他们的错误没有被报告,因为编译器允许void *算术具有完全相同的效果。

Regarding your third question, the swap function exchanges the contents of the memory blocks pointed to by v1 and v2 using a local variable length array buffer of size bytes.关于您的第三个问题, swap function 使用size字节的局部可变长度数组buffer交换v1v2指向的 memory 块的内容。 qsort is usually called with a rather small element size, so this approach is OK, but calling qsort with an array of very long elements (more than a few megabytes) is allowed and could cause a stack overflow . qsort通常以相当小的元素大小调用,因此这种方法是可以的,但是允许使用非常长的元素(超过几兆字节)的数组调用qsort ,这可能会导致堆栈溢出

Here is a safer implementation:这是一个更安全的实现:

void swap(void *v1, void *v2, size_t size) {
    unsigned char *p1 = v1;
    unsigned char *p2 = v2;
    while (size >= 8) {
        char buffer[8];
        memcpy(buffer, p1, sizeof buffer);
        memcpy(p1, p2, sizeof buffer);
        memcpy(p2, buffer, sizeof buffer);
        p1 += sizeof buffer;
        p2 += sizeof buffer;
        size -= sizeof buffer;
    }
    if (size > 0) {
        if (size >= 4) {
            char buffer[4];
            memcpy(buffer, p1, sizeof buffer);
            memcpy(p1, p2, sizeof buffer);
            memcpy(p2, buffer, sizeof buffer);
            p1 += sizeof buffer;
            p2 += sizeof buffer;
            size -= sizeof buffer;
        }
        while (size > 0) {
            unsigned char temp = *p1;
            *p1 = *p2;
            *p2 = temp;
            p1++;
            p2++;
            size--;
        }
    }
}

Also note these remarks:另请注意以下备注:

  • The standard library function qsort has a different prototype:标准库 function qsort有一个不同的原型:

     void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
  • Using int for sizes and index values is not recommended and left * size might overflow for a large array on 64-bit systems.不建议将int用于大小和索引值,并且对于 64 位系统上的大型数组, left * size可能会溢出。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM