2

I'm working on a project which must be coded in standard C. (Not C++.) A while ago, I wrote the below program, writes the contents of different structs into a binary file:

#include <stdio.h>
#include <stdlib.h>
    
typedef struct structA{
        int a, b, c;
}AAA;
typedef struct structB{
        int a, b, c, d, e;
}BBB;
    
int main( int argc, char **argv ){

        // Create and open the output file
        FILE *fd = fopen("test.txt", "w");

        AAA* a1 = (AAA*)malloc( sizeof(AAA) );
        AAA* a2 = (AAA*)malloc( sizeof(AAA) );
        BBB* b1 = (BBB*)malloc( sizeof(BBB) );

        a1->a = 1;   a1->b = 2;   a1->c = 3;
        a2->a = 4;   a2->b = 5;   a2->c = 6;
        b1->a = 10;  b1->b = 20;  b1->c = 30;  b1->d = 40;  b1->e = 50;

        // Write all these structs to the file:
        fwrite((char*)&a1, sizeof(AAA), 1, fd);
        fwrite((char*)&a2, sizeof(AAA), 1, fd);
        fwrite((char*)&b1, sizeof(BBB), 1, fd);

        // Close the file
        fclose( fd );

        free( a1 );
        free( a2 );
        free( b1 );

        printf("END OF PROGRAM.\n");
        return 0;
}

The above works perfectly… even though I can’t tell by looking at the output:

me@ubuntu:/home/me# more test.txt
▒$▒&V
me@ubuntu:/home/me#

I have another program which can read this file and extract all the information from the structs. So I know the above code gives me exactly what I want.

But now, I need to write those structs into a block of allocated memory, not into a file. I thought it would be easy:

#include <stdio.h>
#include <stdlib.h>
    
typedef struct structA{
        int a, b, c;
}AAA;
typedef struct structB{
        int a, b, c, d, e;
}BBB;
    
int main( int argc, char **argv ){

        u_char* BlockOfMemory = (u_char*) malloc( sizeof(u_char) * 100 );

        AAA* a1 = (AAA*)malloc( sizeof(AAA) );
        AAA* a2 = (AAA*)malloc( sizeof(AAA) );
        BBB* b1 = (BBB*)malloc( sizeof(BBB) );

        a1->a = 1;   a1->b = 2;   a1->c = 3;
        a2->a = 4;   a2->b = 5;   a2->c = 6;
        b1->a = 10;  b1->b = 20;  b1->c = 30;  b1->d = 40;  b1->e = 50;

        // Write all these structs into BlockOfMemory:
        memcpy ( BlockOfMemory, &a1, sizeof( AAA ) );
        memcpy ( (BlockOfMemory+sizeof(AAA)), &a2, sizeof( AAA ) );
        memcpy ( (BlockOfMemory+sizeof(AAA)+sizeof(AAA)), &b1, sizeof( BBB ) );

        printf("==>  %hhn\n", BlockOfMemory);

        free( a1 );
        free( a2 );
        free( b1 );
        free( BlockOfMemory );

        printf("END OF PROGRAM.\n");
        return 0;
}

Did it work? I have no idea:

me@ubuntu:/home/me# gcc -Wall writeBlock.c
me@ubuntu:/home/me# ./a.out
==>
END OF PROGRAM.
me@ubuntu:/home/me#

The objective here is that the block of memory must contain the exact same information as the binary file did. I’m in the weird situation where my code compiles and runs, but given the tools I have (VI and GCC), I have no way to verify if my code is correct or way off the mark.

Can anyone advise? Also, would memcpy() be the function to use here? Thank you.

EDIT: Fixed the first program when I mistakenly added a second "free( b1 );" because of a cut-n-paste error.

Pete
  • 1,511
  • 2
  • 26
  • 49
  • 4
    [dont cast malloc](https://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc) – Barmar Aug 24 '20 at 15:18
  • 1
    You're just printing the value of the pointer, not the contents of the memory. `printf()` isn't used to print binary data. – Barmar Aug 24 '20 at 15:20
  • 3
    You are passing ptr-to-ptr in `memcpy` with `&a1` et al. – 001 Aug 24 '20 at 15:21
  • 1
    If the tools you have available to you don't include a debugger, you need to improve your toolset. – Barmar Aug 24 '20 at 15:21
  • 1
    In the first example, besides the problem of attempting to save the *pointers* to the structure (and not the structures they point to), you also expect raw binary data to be readable as text. Open the file in *binary* mode if you want to save raw data. – Some programmer dude Aug 24 '20 at 15:23
  • 2
    You won't be able to se anything if you use `%hhn` in `printf`. The `n` specifier is used for writing the number of characters written so far to a variable, it won't print anything out. – Marco Bonelli Aug 24 '20 at 15:24
  • 2
    In the second example `%hhn` doesn't make any sense at all. The `%n` format writes the number of characters printed thus far into the location where the argument is pointing. – Some programmer dude Aug 24 '20 at 15:24
  • 1
    With `a1` being a pointer, I have some doubts about the first version working perfectly: `fwrite((char*)&a1, sizeof(AAA), 1, fd);` This will write the address to the file, not the content. I wonder what the program you are using to read the files, really does... – Gerhardh Aug 24 '20 at 15:49
  • @Barmar - Wow, a wealth of knowledge! Thank you. I hadn't realized that I was writing the pointers into the file, which is important. And you're correct, I need a proper debugger. I will review your solution momentarily, thank you! – Pete Aug 24 '20 at 18:30
  • @Someprogrammerdude Thanks dude. You're correct, %hhn was a stab in the dark. GCC suggested it, and I just popped it in. Thanks for writing – Pete Aug 24 '20 at 18:32
  • 1
    @Gerhardh The first one is correct. The argument to `fwrite()` is a pointer to the data to write. – Barmar Aug 24 '20 at 18:32
  • @Gerhardh So I posted toy code here at SO to illustrate my problem. In the "real" code, my program assembles a network packet, header-by-header. The "test.txt" file is actually a PCAP file, which I could open and read with Wireshark. So I'm pretty sure the code works. Maybe I got lucky despite my ignorance...? – Pete Aug 24 '20 at 18:33
  • 1
    @Barmar I don't agree. `a1` already holds the address of the data to write. `&a1` is the address of a pointer. From that address you can read `sizeof(AAA*)` bytes and write them into the file, i.e. 4 or 8. You cannot read `sizeof AAA` bytes. – Gerhardh Aug 24 '20 at 18:35
  • 2
    Oh, you're right. – Barmar Aug 24 '20 at 18:36
  • 1
    @Pete I would assume that you introduced that bug while creating the toy code and that toy code simply does not produce the correct result while your real application does. – Gerhardh Aug 24 '20 at 18:36
  • @Gerhardh - Ah. Very possible. You are wise, sensei. – Pete Aug 24 '20 at 18:38
  • @Gerhardh _ Oh, crud, your correct - that's a cut-n-paste error. I'll correct... – Pete Aug 24 '20 at 18:41

1 Answers1

2

As already pointed out you are passing the address of pointer a1 to memcpy, same for the a2 and b1. It should be

memcpy ( BlockOfMemory, a1, sizeof( AAA ) );
memcpy ( (BlockOfMemory+sizeof(AAA)), a2, sizeof( AAA ) );
memcpy ( (BlockOfMemory+sizeof(AAA)+sizeof(AAA)), b1, sizeof( BBB ) );

If you want to print the content of BlockOfMemory you need to cast back into AAA* and BBB* and move with pointer arithmetic, like so

unsigned char* tmp = BlockOfMemory;
AAA* x = (AAA*)tmp;
printf("==>  %d %d %d\n", x->a, x->b, x->c);

tmp += sizeof(AAA);
x = (AAA*)tmp;
printf("==>  %d %d %d\n", x->a, x->b, x->c);

tmp += sizeof(AAA);
BBB* y = (BBB*)tmp;
printf("==>  %d %d %d %d %d\n", y->a, y->b, y->c, y->d, y->e);
sebastian
  • 412
  • 1
  • 4
  • 14