esb5415
esb5415

Reputation: 25

Assign data to char array

I am coding in C doing an assignment regarding block storage (for an OS class). My struct (which is typedef to block_store_t in another file) is

struct block_store{
    unsigned char data[BLOCK_SIZE_BYTES][BLOCK_STORE_NUM_BLOCKS];
    //block_size_bytes is 32, block_store_num_blocks is 512
};

I am putting all the data into a char array because chars are one byte long. To keep track of which blocks are in use, we use a bitmap which we have to store at block 127 of the block store data array. So, accessing it should be something like

bs->data[127]

or something similar to that. My code where I declare an initialize the block storage struct is like so:

block_store_t *block_store_create()
{
    bitmap_t *bm = bitmap_create(BITMAP_SIZE_BITS);

    block_store_t *bs = malloc(sizeof(block_store_t));
    if (bs == NULL) return NULL;

    for (int i = 0; i < (BITMAP_SIZE_BLOCKS); i++)
    {
        bitmap_set(bm, i + BITMAP_START_BLOCK);
    }

    //I've tried the ways two ways below, both give seg faults
    //bs->data[BITMAP_START_BLOCK] = (unsigned char*)*bm;
    //memcpy(bs.data[BITMAP_START_BLOCK], bm, BITMAP_SIZE_BYTES);
    return bs;
}

Since the bitmap is of the type bitmap_t, how do I get the bitmap to be in block 127 of the data array, and how would I access it again? What if the bitmap is larger than the size of one block (in my case, the bitmap will be the size of two blocks, because 512 bits / 8 / 32 bytes per block)?

tl;dr How do I assign data that is not a char to a char array that is inside of a struct? I'm using char because it is one byte long.

Upvotes: 0

Views: 388

Answers (2)

it depends of os .
you'll need to deal with alignment here and there's no standard on that.
for gcc and msvc it will be like that

#include <stdio.h>
#include <stddef.h>

int main(int argc , char *argv[])
{
    struct blockheader {
#pragma pack(1)
        unsigned char hashPrevBlock[30];
        unsigned short b;  //2 bytes
        unsigned char merk[72];
    };

    unsigned char c[104] = {0};
    struct blockheader *a = (struct blockheader *)c;
    a->b = 400 ;
    printf("and then %d  ,   %d\n" ,a->b ,offsetof(struct blockheader, b));
}

for bulk load part of the array you then can use offsetof for sizes and startpoints.

gcc native style on that is

    struct blockheader_on_gcc {
        unsigned char hashPrevBlock[30];
        unsigned short b;  //2 bytes
        unsigned char merk[72];
    }__attribute__((aligned(1)));

if it's np to have extra bytes at the end.
if it's a problem there's __attribute__((packed))

Upvotes: 0

John Bollinger
John Bollinger

Reputation: 181179

How do I assign data that is not a char to a char array that is inside of a struct? I'm using char because it is one byte long.

You cannot assign (which implies using the = operator) an object of structure type to an object of type unsigned char. If your bitmap_t were a numeric type then such an assignment would be allowed, but unless it was a character type, the result would be subject to losing data. You cannot assign to whole arrays at all (as opposed to assigning to array elements of non-array type).

You can, however, copy the bytes of an object's representation into a character array. To that end, your memcpy() approach is substantially right:

    //memcpy(bs.data[BITMAP_START_BLOCK], bm, BITMAP_SIZE_BYTES);

Note in particular that the value designated by bs.data[BITMAP_START_BLOCK] is an array (of unsigned char), and as such it is automatically converted to a pointer to the first element of that array, which will be at an offset of BITMAP_START_BLOCK * BLOCK_STORE_NUM_BLOCKS bytes from the beginning of bs.data.

But that expression for the offset makes me think that you have defined your block_store structure differently than you meant to do. This ...

    unsigned char data[BLOCK_SIZE_BYTES][BLOCK_STORE_NUM_BLOCKS];

... declares data as an array of BLOCK_SIZE_BYTES elements, each of them an array of BLOCK_STORE_NUM_BLOCKS elements of type unsigned char. I think you've reversed the dimensions. The declaration that would be consistent with the macro names would be:

    unsigned char data[BLOCK_STORE_NUM_BLOCKS][BLOCK_SIZE_BYTES];

The size in bytes of the data is the same either way, but the meaning of bs.data[BITMAP_START_BLOCK] differs from one to the other. With the latter declaration of data, the resulting offset in your memcpy would be BITMAP_START_BLOCK * BLOCK_SIZE_BYTES bytes from the start of the data, which I think is what you want.

how would I access it again?

If you intend to rely only on behavior defined by the language standard, then you would copy the bitmap back to an object of type bitmap_t:

bitmap_t bm2;
memcpy(&bm2, bs.data[BITMAP_START_BLOCK], BITMAP_SIZE_BYTES);

Be well aware, however, that the copies involved are shallow. If the objects being copied contain pointers, then it is the pointers that will be copied, not the data to which they point.

Non-conforming alternative

C's so-called "strict aliasing rule" forbids accessing your struct block_store, or its data member, as if part of it were a bitmap_t. Nevertheless, such accesses have historically been relatively common practice. That would take the form of casting block or byte pointers to type bitmap_t * and dereferencing the result. For example:

// requires a definition of bitmap_t in scope:
(bitmap_t *) bs.data[BITMAP_START_BLOCK] = *bm;

bitmap_set((bitmap_t *) bs.data[BITMAP_START_BLOCK], x);

The perceived advantages of that approach are simpler expressions and less copying, but your compiler is free to do any number of surprising things with such code. Before going that route, be sure you know how to prevent the compiler from surprising you in that regard. For example, with GCC, you would need to specify the -fno-strict-aliasing option to the compiler.

Upvotes: 1

Related Questions