0

I was reading over some of the source code behind pngquant (here)

I got confused when I saw plus-equals seemingly assigning a new value to an array of structs (base += r in the code snippet below):

static void hist_item_sort_range(hist_item base[], unsigned int len, unsigned int sort_start)
{
    for(;;) {
        const unsigned int l = qsort_partition(base, len), r = l+1;

        if (l > 0 && sort_start < l) {
            len = l;
        }
        else if (r < len && sort_start > r) {
            base += r; len -= r; sort_start -= r;
        }
        else break;
    }
}

The hist_item definition is given as:

typedef struct {
    f_pixel acolor;
    float adjusted_weight,   // perceptual weight changed to tweak how mediancut selects colors
          perceptual_weight; // number of pixels weighted by importance of different areas of the picture

    float color_weight;      // these two change every time histogram subset is sorted
    union {
        unsigned int sort_value;
        unsigned char likely_colormap_index;
    } tmp;
} hist_item;

I apologize ahead of time, because I'm sure to those in the know this must be a really dumb question, but how is plus-equals operating on base, which appears to be an array of structs, and some integer r? It seems to me that this operation should be undefined for the combination of those two types.

I haven't had to write C for almost ten years, and I'm admittedly pretty rusty; however, searching for about thirty minutes only turned up answers to the wrong questions, and any help is appreciated. Thanks!

Mackie Messer
  • 584
  • 3
  • 14
  • 1
    `hist_item base[]` is actually decayed to `hist_item* base`: https://stackoverflow.com/questions/1461432/what-is-array-decaying - so `base += r` is just pointer arithmetic – UnholySheep Dec 22 '18 at 15:39
  • @UnholySheep thanks! That's exactly what I needed to know. – Mackie Messer Dec 22 '18 at 15:48
  • 1
    I misunderstood the question. As @UnholySheep already says, in C you cannot pass arrays as parameter, they are **always passed by reference**. This is obtained by their automatic decay to pointers to same object forming the array. So they're de-facto pointers, and the `+=` operation follows the pointers arithmetic. In the specific case after `base+=r;`, `base` points to `r` elements ahead. – Frankie_C Dec 22 '18 at 15:51
  • @UnholySheep 1) if you respond as an answer, I'll accept it ASAP. 2) In your answer, could you also clarify one aspect of the pointer arithmetic here; namely, if the array pointer is incremented and then the array's value is referenced, &base[0] for example, is the indexing offset by the incrementation? Put more simply, is &base[0] AFTER base +=1 the same as &base[1] before incrementation? – Mackie Messer Dec 22 '18 at 22:50

1 Answers1

1

As explained in What is array decaying?

static void hist_item_sort_range(hist_item base[], unsigned int len, unsigned int sort_start)

becomes

static void hist_item_sort_range(hist_item* base, unsigned int len, unsigned int sort_start)

Where base is a pointer to the first element of the array. Therefore base += r; performs simple pointer arithmetic, i.e.: modifies the pointer to point to an offset of r elements from the start of the array.
Due to the += the original pointer is modified, so any access happens with offset from the now pointed to element.

To use the example from the comment:
After base += 1; accessing the "first" element via &base[0]; yields a pointer to the same element as &base[1]; before the increment

UnholySheep
  • 3,125
  • 4
  • 20
  • 23