0

I have an array that represents an image of RGB pixels:

struct pix{
  unsigned char red;
  unsigned char green;
  unsigned char blue
}

struct pix *pixels = malloc(800*600*sizeof(struct pix));

The array pixels represents an 800x600 image, with values stored as R,G,B triplets. Is there any way to treat this 1-dimensional array as if it were a 2-dimensional array with width 800 and height 600, so that e.g. pixels[0][0].red=255 is a valid operation?

8
  • 6
    If it really is 800x600, why not struct pix (*pixels)[800] = malloc(600 * sizeof *pixels) ? Commented Aug 19, 2021 at 22:35
  • Can we assume that, in general, the dimensions are not known at compile time (800x600 beeing just an example)? Commented Aug 19, 2021 at 22:46
  • Assuming that your compiler supports variable length arrays (VLA), you can just replace the numbers with variables: struct pix (*pixels)[cols] = malloc(rows * sizeof *pixels); Commented Aug 19, 2021 at 22:51
  • Does c-faq.com/aryptr help you solve the problem, especially subsection 13? Commented Aug 19, 2021 at 23:01
  • 2
    @2lean4 , no, WhozCraig means sizeof *pizels, which is the size of 800 structs, since in that comment pixels is a pointer to arrays of 800 structs. Commented Aug 19, 2021 at 23:08

2 Answers 2

2

You can use this code:

struct pix (*pixels)[800] = malloc(600 * sizeof *pixels);

This declares pixels to be a pointer to an array of 800 struct pix and allocates 600 of them. Then you can access the element in row i and column j as pixels[i][j], and its members with pixels[i][j].red. (Do not forget to check the return value of malloc to see if it succeeded or failed.)

In many C implementations, 800 can be replaced by a variable instead of a constant, and it will still work.

When allocating, preferably use sizeof *MyPointer as shown here, rather than sizeof (MyType). Then, if you ever need to change the type of MyPointer, there is one place fewer you need to edit.

Sign up to request clarification or add additional context in comments.

3 Comments

that's exactly what I was looking for, thanks. Is there any way to swap around i and j while keeping the underlying data structure the same? so like i can do pixels[x][y], essentially? not exactly necessary, but it'd be more intuitive.
@2lean4: What do you mean by swapping i and j? There is no x or y in your question or my answer, so I do not know what pixels[x][y] is supposed to mean.
er sorry, i meant in terms of x-coordinate,y-coordinate, rather than the other way around
0

This uses a similar pointer to what Eric used, but I've added an array descriptor struct that can be passed around easily.

That is, from main, how do you pass the array to subfunctions? Especially, if you have to have multiple arrays that have differing dimensions?

The syntax of (e.g.):

struct pix (*pixels)[width] = malloc(sizeof(*pixels) * height);

can be cumbersome.

It can be simplified with a small bit of macro trickery.

Here's some code that illustrates the benefits of a struct and the convenience of being able to do true 2D indexing:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

typedef uint8_t u8;
typedef uint32_t u32;

#define sysfault(_fmt...) \
    do { \
        printf(_fmt); \
        exit(1); \
    } while (0)

// pixel control
typedef struct pix {
    u8 red;
    u8 green;
    u8 blue;
} pix_t;

// pixel array control
typedef struct arr {
    size_t arr_width;
    size_t arr_height;
    pix_t *arr_pixels;
} arr_t;

#define ARR2DPTR(_arr) \
    pix_t (*_arr##ptr)[_arr->arr_width] = \
        (__typeof__(_arr##ptr)) _arr->arr_pixels

arr_t *
arrnew(size_t width,size_t height)
{
    arr_t *arr = malloc(sizeof(*arr));

    arr->arr_width = width;
    arr->arr_height = height;
    arr->arr_pixels = malloc(sizeof(*arr->arr_pixels) * width * height);

    return arr;
}

void
pixpack(pix_t *pix,u32 off)
{

    pix->red = (off >> 16);
    pix->green = (off >> 8);
    pix->blue = (off >> 0);
}

u32
pixunpack(const pix_t *pix)
{
    u32 off = 0;

    off |= ((u32) pix->red) << 16;
    off |= ((u32) pix->green) << 8;
    off |= ((u32) pix->blue) << 0;

    return off;
}

void
arrfill(arr_t *arr)
{
    pix_t *arrptr = arr->arr_pixels;
    u32 off = 0;

    for (size_t y = 0;  y < arr->arr_height;  ++y) {
        for (size_t x = 0;  x < arr->arr_width;  ++x, ++off)
            pixpack(&arrptr[(y * arr->arr_width) + x],off);
    }
}

void
chk2ptr(arr_t *arr)
{
    ARR2DPTR(arr);
    u32 offexp = 0;
    u32 offact;

    for (size_t y = 0;  y < arr->arr_height;  ++y) {
        for (size_t x = 0;  x < arr->arr_width;  ++x, ++offexp) {
            offact = pixunpack(&arrptr[y][x]);
            if (offact != offexp)
                sysfault("chk2ptr: y=%zu x=%zu offexp=%u offact=%u\n",
                    y,x,offexp,offact);
        }
    }
}

void
chk1ptr(arr_t *arr)
{
    pix_t *arrptr = arr->arr_pixels;
    u32 offexp = 0;
    u32 offact;

    for (size_t y = 0;  y < arr->arr_height;  ++y) {
        for (size_t x = 0;  x < arr->arr_width;  ++x, ++offexp) {
            offact = pixunpack(&arrptr[(y * arr->arr_width) + x]);
            if (offact != offexp)
                sysfault("chk1ptr: y=%zu x=%zu offexp=%u offact=%u\n",
                    y,x,offexp,offact);
        }
    }
}

int
main(void)
{

    arr_t *arr = arrnew(20,10);
    arrfill(arr);

    chk1ptr(arr);
    chk2ptr(arr);

    return 0;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.