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.
bitmap_createdoes, what it returns or how it fails? How do we know whatbitmap_setdoes? How do we know thatBITMAP_SIZE_BLOCKSis longer than 144 (data + bitmap)? how do we know whatbitmap_teven is?bitmap_createallocates memory, you probably can't put the returned bitmap within theblock_store_t... you might want to create the bitmap some other way. Also, are you suebitmap_createtakes the bitmap size rather than theBITMAP_START_BLOCKlength? or maybe yourforloop should beBITMAP_SIZE_BITSlong instead? Are they the same value? And whyi + BITMAP_START_BLOCK?bitmap_createdoes allocate memory. I wonder what my professor wants us to do in that case, since we have to store that bitmap at block 127, but use his functions for the bitmap.