C misreading resolution of .bmp file - c

I have a project that involves reading a .bmp file into a C program, putting a mask on it, and printing the version with the mask back to a different file. The part I'm having an issue with seems to be the actual process of reading in the file. The first big red flag I'm seeing is that it keeps reading in the wrong resolution. I've searched quite a bit and seen a few scripts to read in a .bmp file as answers to various questions here but using the logic from those scripts hasn't helped.
The primary issue seems to be that rather than reading in the proper dimensions of 200 x 300 on the example image given by my professor, it reads in 13107200 x 65536. However, if I were to include the part of the code that prints to a different file, you would see that the output file has the appropriate resolution. This tells me that I am likely reading in the information properly but not storing it in the way that I think I am.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct HEADER {
unsigned short int Type; // Magic indentifier
unsigned int Size; // File size in bytes
unsigned short int Reserved1, Reserved2;
unsigned int Offset; // Offset to data (in B)
} Header; // -- 14 Bytes
struct INFOHEADER {
unsigned int Size; // Header size in bytes
int Width, Height; // Width / height of image
unsigned short int Planes; // Number of colour planes
unsigned short int Bits; // Bits per pixel
unsigned int Compression; // Compression type
unsigned int ImageSize; // Image size in bytes
int xResolution, yResolution; // Pixels per meter
unsigned int Colors; // Number of colors
unsigned int ImportantColors; // Important colors
} InfoHeader; // -- 40 Bytes
struct PIXEL {
unsigned char Red, Green, Blue; // Intensity of Red, Green, and Blue
}; // -- 3 Bytes
int getFileSize(FILE *input);
int getHexVal(FILE *input);
struct HEADER *getHeader(FILE *input);
struct INFOHEADER *getInfoHeader(FILE *input);
struct PIXEL *getPixel(FILE *input, struct PIXEL *loc);
struct HEADER *printHeader(FILE *output);
struct INFOHEADER *printInfoHeader(FILE *output);
struct PIXEL *printPixel(FILE *output, struct PIXEL *loc);
int main(int argc, char const *argv[]) {
if (argc == 3) {
if (!strcmp(argv[1], argv[2])) {
printf("The input and output file must be different. Please try again.\n");
return 1;
}
// char Matrix[3][3] =
// { { 0, -1, 0 },
// { -1, 4, -1 },
// { 0, -1, 0 }
// };
FILE *input = fopen(argv[1], "rb");
if (!input) return 1;
int i, j;
// getHeader(input);
fread(&Header, sizeof(struct HEADER), 1, input);
if (Header.Type != 0x4D42) {
printf("The specified input file was not a bitmap. Please try again.");
fclose(input);
return 1;
}
// getInfoHeader(input);
fread(&InfoHeader, sizeof(struct INFOHEADER), 1, input);
fseek(input, Header.Offset, SEEK_SET);
struct PIXEL arr[InfoHeader.Width][InfoHeader.Height];
printf("%d %d\n", InfoHeader.Width, InfoHeader.Height);
for (i = 0; i < InfoHeader.Width; i++) {
for (j = 0; j < InfoHeader.Height; j++) {
getPixel(input, arr[i] + j);
printf("%d %d %d\n", arr[i][j].Red, arr[i][j].Green, arr[i][j].Blue);
}
}
fclose(input);
}
}

I can see a lot of problems with your code:
1. Inconsistent sizes of data types
On different platforms, types like int and short can have different sizes. So, int might be one size on one platform, and another size on a different platform. You may need to use exact sized types like uint32_t.
2. Padding and alignment
The headers stored in the files are packed. Your structs are aligned. That means that the compiler inserts padding between members to ensure that members are always aligned for optimal memory access.
There are a variety of ways of dealing with this. You could declare your structs to be packed. That would get you so far, but see the next point.
3. Endianness
If you are reading a Windows bitmap on a big endian system, you have convert from little endian data in the file to big endian data for your system.
4. xResolution, yResolution are the wrong members
These are meant to indicate a physical size of the pixels. In practice they are seldom specified. You meant to read Width and Height.
5. The VLA (gah!)
You are using a variable length array: struct PIXEL arr[InfoHeader.xResolution][InfoHeader.yResolution]. That is liable to lead to stack overflows for large bitmaps. You really need to use dynamically allocated memory for the pixel array.
How would I deal with these issues?
Use exact sized types.
Declare packed structs.
Read the structs from file, and then perform endian correction if needed.
Allocate the pixel array with malloc.

The types int and short and so on are only guaranteed to have certain minimum sizes. They can vary on different implementations. Even if we assume that an int and short is four and two octets respectively, you will still run into problems when reading and writing your structures.
For example:
struct HEADER {
unsigned short int Type;
unsigned int Size;
unsigned short int Reserved1, Reserved2;
unsigned int Offset;
} Header;
In order to make Size suitably aligned for the processor, the compiler will (typically) insert padding between Type and Size which places Size at offset +4 instead of +2 (assuming the sizes mentioned above).
The best way to read (and write) binary formats is to read the file into an unsigned char * buffer, and then extract the fields from there. Eg.
unsigned long Size = buffer[2] +
buffer[3] * 0x100UL +
buffer[4] * 0x10000UL +
buffer[5] * 0x1000000UL;
or similar.

I suspect you mixed up some of the fields.
After looking at http://en.wikipedia.org/wiki/BMP_file_format , I think instead of this
struct PIXEL arr[InfoHeader.xResolution][InfoHeader.yResolution];
You really meant this:
struct PIXEL arr[InfoHeader.Width][InfoHeader.Height];

Related

I can't reach the size of header of BMP file in c code

I want to copy a 1bit BMP image and I wrote this code. I don't know why the size of Header struct in my code is 56, although it need to be 54.
My code for struct HeaderBMP:
short _type; // file type
short _size;
int _reserved;
int _offset;
int _info_size;
int _width;
int _height;
short _planes;
short _bpp;
int _compression;
int _imagesize;
int _xresolution;
int _yresolution;
int _colours;
int _impcolours;
I can't reach to size 54, it is just 52 or 56 when I try to fix . I hope you can help me to fix this.
Use fixed size integers for this kind of structs. Those types are defined in the stdint.h header file. struct might require packing (which is implementation defined)
gcc example:
typedef struct { // Total: 54 bytes
uint16_t type; // Magic identifier: 0x4d42
uint32_t size; // File size in bytes
uint16_t reserved1; // Not used
uint16_t reserved2; // Not used
uint32_t offset; // Offset to image data in bytes from beginning of file (54 bytes)
uint32_t dib_header_size; // DIB Header size in bytes (40 bytes)
int32_t width_px; // Width of the image
int32_t height_px; // Height of image
uint16_t num_planes; // Number of color planes
uint16_t bits_per_pixel; // Bits per pixel
uint32_t compression; // Compression type
uint32_t image_size_bytes; // Image size in bytes
int32_t x_resolution_ppm; // Pixels per meter
int32_t y_resolution_ppm; // Pixels per meter
uint32_t num_colors; // Number of colors
uint32_t important_colors; // Important colors
} __attribute__((packed)) BMPHeader;
Thank you for your solution, however its is also 56 when I compile by passing argument through cmd on window 10 x64. It is 54 when it works on ubuntu and codeblock on window with passing argument in code.
My solution :
typedef struct { // Total: 54 bytes
char _type[2]; // Magic identifier: 0x4d42
char _size[4]; // File size in bytes
char _reserved1[2]; // Not used
char _reserved2[2]; // Not used
char _offset[4]; // Offset to image data in bytes from beginning of file (54 bytes)
char _dib_header_size[4]; // DIB Header size in bytes (40 bytes)
char _width[4]; // Width of the image
char _height[4]; // Height of image
char _planes[2]; // Number of color planes
char _bpp[2]; // Bits per pixel
char _compression[4]; // Compression type
char _image_size[4]; // Image size in bytes
char _x_resolution_ppm[4]; // Pixels per meter
char _y_resolution_ppm[4]; // Pixels per meter
char _num_colors[4]; // Number of colors
char _important_colors[4]; // Important colors
} header;
and when I need to take value , I will do :
*(int*) header._width
I test and it works with above three cases .

passign a struct pointer to a function and struct padding in c programming

In build_uart_frame() , I call calcFCS() which calculates an XOR of all the bytes in the struct members(len, cmd0, cmd1 and data).
I do not think the struct is padded therefore will calling calcFCS() be an issue? Could somebody explain what is the issue in relation to struct padding as I don't understand its role here and secondly how can I do this operation correctly?
Thank you
typedef struct uart_frame {
uint8_t sof; /* 1 byte */
uint8_t len; /* 1 bytes */
uint8_t cmd0; /* 1 byte */
uint8_t cmd1;
char data[11]; /* 0 -250 byte */
unsigned char fcs; /* 1 byte */
} uart_frame_t;
//-------------------------------------------------------------------------
// Global uart frame
uart_frame_t rdata;
//-------------------------------------------------------------------------
unsigned char calcFCS(unsigned char *pMsg, unsigned char len) {
unsigned char result = 0;
while(len--) {
result ^= *pMsg++;
}
return(result);
}
//-------------------------------------------------------------------------
// Worker code to populate the frame
int build_uart_frame() {
uart_frame_t *rd = &rdata; //pointer variable 'rd' of type uart_frame
// common header codes
rd->sof = 0xFE;
rd->len = 11;
rd->cmd0 = 0x22;
rd->cmd0 = 0x05;
snprintf(rd->data, sizeof(rd->data), "%s", "Hello World");
rd->fcs = calcFCS((unsigned char *)rd, sizeof(uart_frame_t) - 1); //issue with struct padding
return 0;
}
Given your very specific example, it is unlikely that padding will be an issue, since all data types are bytes. Padding is mostly an issue when you use larger data types, because those should typically not be allocated at misaligned addresses.
Yet that is no guarantee: the compiler could in theory decide to replace a char with an int if it thinks that will get faster code. It is free to insert any amount of padding anywhere in a struct, except at the very top.
This is why structs are unsuitable to describe memory maps or data protocols. You will have to ensure that no padding is present and preferably do so portably. The best way to ensure this is a standard C compile-time assert:
_Static_assert(sizeof(uart_frame_t) == offsetof(uart_frame_t, fcs)+sizeof(unsigned char),
"Padding detected");
Here the size of the whole struct is checked against the byte position of the last struct member + the size of that member. If they are the same, there was no padding.
Now of course this only prevents your code from compiling and misbehaving, it doesn't solve the actual problem. Unfortunately there is no portable way to block padding. #pragma pack(1) is common but non-standard. __attribute__((packed)) is another compiler-specific command for this.
Ensuring that no packing is present on the given system where the code is compiled is usually enough.
Also, some of the more exotic systems (MIPS, SPARC etc) don't even support misaligned reads, meaning that misaligned access will not just mean slower code, but a run-time bus error crash.
The only way to safely ensure maximum portability of code using structs, is to write serialize/de-serialize routines that manually copies every member to/from a raw byte array:
void uart_serialize (const uart_frame_t* frame, uint8_t* raw)
{
raw[0] = frame->sof;
raw[1] = frame->len;
...
memcpy(&raw[4], frame->data, 11);
...
}
The downside of such methods is that they obviously adds some execution time, so I would only use them for code that I know needs to be ported to all kinds of different systems.

How to read a structure with a size that isn't a multiple of 4 [duplicate]

This question already has answers here:
Why isn't sizeof for a struct equal to the sum of sizeof of each member?
(13 answers)
Closed 9 years ago.
I'm reading structure in file *stl, but the structure is:
typedef struct
{
float x;
float y;
float z;
} point;
typedef struct
{
point normal_vector; //12 bytes
point p1; //12 bytes
point p2; //12 bytes
point p3; //12 bytes
short int notuse; //2 bytes
} triangle;
sizeof(triangle) is 52—12+12+12+12+2+...2 (I don't know where the last 2 comes from?) The size of each unit in file *stl is 50 (not multiple of 4).
How can I reduce the size of structure to read file (from 52 to 50)?
Thank you.
A way to be preferred over reading the struct - whose memory layout can vary, as you see - as it is and reducing its size could be the way to go.
That said, you can read the file in large blocks and cut the data in the parts you need. Then you read out field for field and put the data into your target array. Something like
float read_float(void ** data) {
float ** fp = data;
float ret = **fp; (*fp)++;
return ret;
}
point read_point(void ** data) {
point ret;
ret.x = read_float(data);
ret.y = read_float(data);
ret.z = read_float(data);
return ret;
}
int16_t read16(void ** data) {
int16_t ** i16p = data;
int16_t ret = **i16p; (*i16p)++;
return ret;
}
point read_triangle(void ** data) {
triangle ret;
ret.normal_vector = read_point(data);
ret.p1 = read_point(data);
ret.p2 = read_point(data);
ret.p3 = read_point(data);
ret.notuse = read_int16(data); // using short int is not portable as well as its size could vary...
return ret;
}
void * scursor = source_array; // which would be a char array
while (scursor < source_array + sizeof(source_array)) {
// make sure that there are enough data present...
target[tcursor++] = read_triangle(&scursor); // the scursor will be advanced by the called function.
}
This way could as well - with certain enhancements - be used to keep e. g. the endianness of your numbers the same - which would be preferrably big endian on files intended to be interchanged between platforms. The changes to read16 would be small, the changes to read_float a bit bigger, but still doable.
Extra two bytes are coming due to padding. Padding is to align the structure with 4 bytes boundary (your word size may be 32 bits, it can vary for 64-bits).
In file, you have stored 50 bytes per structure. So, you can read those 50 bytes and assign the value to each member one by from 50 bytes. Code will look like
Char readbuf[50];
//Read the 50 bytes into the buffer readbuf.
triangle t;
t.normal_vector.x = (float *)readbuf;
t.normal_vector.y = (float *)(readbuf + sizeof(float));
t.normal_vector.z = (float *)(readbuf + 2*sizeof(float));
t.p1.x = (float *)(readbuf + 3*sizeof(float));
//and so on for other members.
Please note that this has byte alignment issue and same programme may not work on big endian machine. So, be wary of storing binary data directly without any rule or encoding.
With GCC/G++ you could do this to pack your structure:
typedef struct
{
point normal_vector; //12 bites
point p1; //12 bites
point p2; //12 bites
point p3; //12 bites
short int notuse; //2 bites
} __attribute__((packed)) triangle;

Continuous memory allocation with different data type in C?

I'm trying to compose a string (char array exactly) containing a fixed 14 starting characters and ending with varying content. The varying bit contains 2 floats and 1 32-bit integer that's to be individually treated as 4 1-byte characters in the array separated by commas. It can be illustrated by the following piece of code, which doesn't compile for some obvious reasons (*char can't assign to *float). So, what can I do to get around it?
char *const comStr = "AT+UCAST:0000=0760,0020,0001\r"; // command string
float *pressure;
float *temperature;
uint32_t *timeStamp;
pressure = comStr + 14; // pressure in the address following the '=' in command string
temperature = comStr + 18; // temperature in the address following the 1st ',' in command string
timeStamp = comStr + 22; // time stamp in the address following the 2nd ',' in command string
I have an unclear memory about something like struct and union in the C language which reserves strictly the memory allocation order in which the variables are defined within the "structure". Maybe something like this:
typedef struct
{
char[14] command;
float *pressure;
char comma1;
float *temperature;
char comma2;
uint32_t *time_stamp;
char CR;
}comStr;
Does this structure guarantee that comStr-> command[15] gives me the first/last byte (depends on the endian) of *pressure? Or is there any other special structure do the trick hiding from me?
(Note: comStr-> command[15] isn't going to be evaluated in future code, so exceeding index boundary is not a concern here. The only important thing here is just whether the memory is allocated continuously so that a hardware fetch lasting for 29 bytes starting from the memory address (comStr-> command) gives me exactly the string I want).
p.s. As I am writing this, I came up with an idea. Can I possibly just use memcpy() for the purpose ;) memcpy has parameters of void* type, hopefully it works! I am going to try it now! All hail stackOverflow anyway!
EDIT: I should have made myself clearer, sorry for any misleading and misunderstanding! The character array I want to construct is to be sent through UART byte by byte. To do this, a DMA system is to be used to transfer the array to the transmit buffer byte by byte automatically if the character array's starting memory address and length are given to the DMA system. So the character array must to be stored continuously in the memory. I hope this makes the question clearer.
This proposed structure:
typedef struct
{
char[14] command;
float *pressure;
char comma;
float *temperature;
char comma;
uint32_t *time_stamp;
char CR;
}comStr;
Is not going to help you with your requirement:
The only important thing here is just whether the memory is allocated continuously so that a hardware fetch lasting for 29 bytes starting from the memory address (comStr->command) gives me exactly the string I want.
Note you can't have two members with the same name; you'd need to use comma1 and comma2 for example. Also, the array dimension is in the wrong place.
One problem is that there will be padding bytes within the structure.
Another problem is that the pointers will be holding addresses of something outside the structure (since there is nothing valid inside the structure for them to point at).
It is not clear what you're after. Only a very limited range of floating point values can be represented by 4 bytes in a string. If you're after binary data I/O, then you can drop the pointers and the commas:
typedef struct
{
char command[14];
float pressure;
float temperature;
uint32_t time_stamp;
}comStr;
If you want the commas present, then you're going to have to work harder:
typedef struct
{
char command[14];
char pressure[4];
char comma1;
char temperature[4];
char comma2;
char time_stamp[4];
char CR;
} comStr;
You will have to load the data carefully:
struct comStr com;
float pressure = ...;
float temperature = ...;
uint32_t time_stamp = ...;
assert(sizeof(float) == 4);
...
memmove(&com.pressure, &pressure, sizeof(pressure));
memmove(&com.temperature, &temperature, sizeof(temperature));
memmove(&com.time_stamp, &time_stamp, sizeof(time_stamp));
You have to unpack with a similar set of memory copies. Note that you won't be able to use simple string manipulation on the structure; there could be zero bytes in any or all of the pressure, temperature and time_stamp sections of the structure.
Structure padding
#include <stddef.h>
#include <stdio.h>
#include <stdint.h>
typedef struct
{
char command[14];
float *pressure;
char comma1;
float *temperature;
char comma2;
uint32_t *time_stamp;
char CR;
} comStr;
int main(void)
{
static const struct
{
char *name;
size_t offset;
} offsets[] =
{
{ "command", offsetof(comStr, command) },
{ "pressure", offsetof(comStr, pressure) },
{ "comma1", offsetof(comStr, comma1) },
{ "temperature", offsetof(comStr, temperature) },
{ "comma2", offsetof(comStr, comma2) },
{ "time_stamp", offsetof(comStr, time_stamp) },
{ "CR", offsetof(comStr, CR) },
};
enum { NUM_OFFSETS = sizeof(offsets)/sizeof(offsets[0]) };
printf("Size of comStr = %zu\n", sizeof(comStr));
for (int i = 0; i < NUM_OFFSETS; i++)
printf("%-12s %2zu\n", offsets[i].name, offsets[i].offset);
return 0;
}
Output on Mac OS X:
Size of comStr = 64
command 0
pressure 16
comma1 24
temperature 32
comma2 40
time_stamp 48
CR 56
Note how large the structure is on a 64-bit machine. Pointers are 8-bytes each and are 8-byte aligned.
Various issues to be a covered in your question. I'll take a shot at some of those issues.
The order of members in a structure is guaranteed to be the same as order you have declared them. But there is a different issue here - padding.
Check this -http://c-faq.com/struct/padding.html and follow other links/questions there
Next thing is that you are mistaken in thinking that something like "125" is an integer or something like "1.25" is a float - it's not - it's a string. i.e.
char * p = "125";
p[0] will not contain 0. It will contain '0' - if the encoding is ASCII, then this will be 48. i.e. p[0] will contain 48 & not 0. p[1] will contain 49 & p[2] will contain 52. It will be something similar for float.
The opposite will also happen.
i.e. if you have at an address and you treat it as a char array - the char array will not contain the float you think it will.
Try this program to see this
#include <stdio.h>
struct A
{
char c[4];
float * p;
int i;
};
int main()
{
float x = 1.25;
struct A a;
a.p = &x;
a.i = 0; // to make sure the 'presumed' string starting at p gets null terminate after the float
printf("%s\n", &a.c[4]);
}
For me, it prints "╪·↓". And this has nothing to do with endianness.
Another thing you need to remember, while assigning values to your structure object - you need to remember that comStr.pressure & comStr.temperature are pointers. You cannot assign values to them directly. You need to either give them the address of an existing float or allocate memory dynamically to which they can point to.
Also are you trying to create the char array or to parse the char array which already exists. If you are trying to create it, a better way to do this will be to use snprintf to do what you want. snprintf uses format specifiers similar to printf but prints to a char array. You can create your char array that way. A bigger question remains - what do you plan to do with this char array you create - that will determine if endianness is relevant for you.
If you are trying to read from the char array you have been given and trying to split into floats and commas and whatever, then one way to do this will be sscanf but may be difficult for your particular string format.
At last, I found an easy way round but I don't know if there is any drawback for this method. I did:
char commandStr[27];
char *commandHeader = "AT+UCAST:0000=";
float pressure = 760.0;
float temperature = 20.0;
uint32_t timeStamp = 0;
memcpy(commandStr, commandHeader, 14);
commandStr[26] = '\r';
memcpy((void*)(comStr+14), (void*)(&pressure), 4);
memcpy((void*)(comStr+18), (void*)(&temperature), 4);
memcpy((void*)(comStr+22), (void*)(&timeStamp), 4);
Does this code have any security issues or performance issues or whatever?

Read binary data (from file) into a struct

I'm reading binary data from a file, specifically from a zip file. (To know more about the zip format structure see http://en.wikipedia.org/wiki/ZIP_%28file_format%29)
I've created a struct that stores the data:
typedef struct {
/*Start Size Description */
int signatute; /* 0 4 Local file header signature = 0x04034b50 */
short int version; /*  4 2 Version needed to extract (minimum) */
short int bit_flag; /*  6 2 General purpose bit flag */
short int compression_method; /*  8 2 Compression method */
short int time; /* 10 2 File last modification time */
short int date; /* 12 2 File last modification date */
int crc; /* 14 4 CRC-32 */
int compressed_size; /* 18 4 Compressed size */
int uncompressed_size; /* 22 4 Uncompressed size */
short int name_length; /* 26 2 File name length (n) */
short int extra_field_length; /* 28 2 Extra field length (m) */
char *name; /* 30 n File name */
char *extra_field; /*30+n m Extra field */
} ZIP_local_file_header;
The size returned by sizeof(ZIP_local_file_header) is 40, but if the sum of each field is calculated with sizeof operator the total size is 38.
If we have the next struct:
typedef struct {
short int x;
int y;
} FOO;
sizeof(FOO) returns 8 because the memory is allocated with 4 bytes every time. So, to allocate x are reserved 4 bytes (but the real size is 2 bytes). If we need another short int it will fill the resting 2 bytes of the previous allocation. But as we have an int it will be allocated plus 4 bytes and the empty 2 bytes are wasted.
To read data from file, we can use the function fread:
ZIP_local_file_header p;
fread(&p,sizeof(ZIP_local_file_header),1,file);
But as there're empty bytes in the middle, it isn't read correctly.
What can I do to sequentially and efficiently store data with ZIP_local_file_header wasting no bytes?
In order to meet the alignment requirements of the underlying platform, structs may have "padding" bytes between members so that each member starts at a properly aligned address.
There are several ways around this: one is to read each element of the header separately using the appropriately-sized member:
fread(&p.signature, sizeof p.signature, 1, file);
fread(&p.version, sizeof p.version, 1, file);
...
Another is to use bit fields in your struct definition; these are not subject to padding restrictions. The downside is that bit fields must be unsigned int or int or, as of C99, _Bool; you may have to cast the raw data to the target type to interpret it correctly:
typedef struct {
unsigned int signature : 32;
unsigned int version : 16;
unsigned int bit_flag; : 16;
unsigned int compression_method : 16;
unsigned int time : 16;
unsigned int date : 16;
unsigned int crc : 32;
unsigned int compressed_size : 32;
unsigned int uncompressed_size : 32;
unsigned int name_length : 16;
unsigned int extra_field_length : 16;
} ZIP_local_file_header;
You may also have to do some byte-swapping in each member if the file was written in big-endian but your system is little-endian.
Note that name and extra field aren't part of the struct definition; when you read from the file, you're not going to be reading pointer values for the name and extra field, you're going to be reading the actual contents of the name and extra field. Since you don't know the sizes of those fields until you read the rest of the header, you should defer reading them until after you've read the structure above. Something like
ZIP_local_file_header p;
char *name = NULL;
char *extra = NULL;
...
fread(&p, sizeof p, 1, file);
if (name = malloc(p.name_length + 1))
{
fread(name, p.name_length, 1, file);
name[p.name_length] = 0;
}
if (extra = malloc(p.extra_field_length + 1))
{
fread(extra, p.extra_field_length, 1, file);
extra[p.extra_field_length] = 0;
}
C structs are just about grouping related pieces of data together, they do not specify a particular layout in memory. (Just as the width of an int isn't defined either.) Little-endian/Big-endian is also not defined, and depends on the processor.
Different compilers, the same compiler on different architectures or operating systems, etc., will all layout structs differently.
As the file format you want to read is defined in terms of which bytes go where, a struct, although it looks very convenient and tempting, isn't the right solution. You need to treat the file as a char[] and pull out the bytes you need and shift them in order to make numbers composed of multiple bytes, etc.
The solution is compiler-specific, but for instance in GCC, you can force it to pack the structure more tightly by appending __attribute__((packed)) to the definition. See http://gcc.gnu.org/onlinedocs/gcc-3.2.3/gcc/Type-Attributes.html.
It's been a while since I worked with zip-compressed files, but I do remember the practice of adding my own padding to hit the 4-byte alignment rules of PowerPC arch.
At best you simply need to define each element of your struct to the size of the piece of data you want to read in. Don't just use 'int' as that may be platform/compiler defined to various sizes.
Do something like this in a header:
typedef unsigned long unsigned32;
typedef unsigned short unsigned16;
typedef unsigned char unsigned8;
typedef unsigned char byte;
Then instead of just int use an unsigned32 where you have a known 4-byte vaule. And unsigned16 for any known 2-byte values.
This will help you see where you can add padding bytes to hit 4-byte alignment, or where you can group 2, 2-byte elements to make up a 4-byte alignment.
Ideally you can use a minimum of padding bytes (which can be used to add additional data later as your expand the program) or none at all if you can align everything to 4-byte boundaries with variable-length data at the end.
Also, the name and extra_field will not contain any meaningful data, most likely. At least not between runs of the program, since these are pointers.

Resources