Thread: pointers, structures, and malloc

  1. #1
    Registered User
    Join Date
    Oct 2008
    Posts
    7

    pointers, structures, and malloc

    Hi,

    My job is to read some image data, and add some appropriate hex data. I stripped this code to this form because much of the code is irrelevant to my problem. I have multiple structures, and behind each one is a variable length string. Also, the output may contain two or three of these structures. I malloc the area where this data is constructed. Then I try to assign the first structure to point to this area. Then I add the variable length data via memcpy. Then I want to make the next structure point to the adjacent byte to the previous, add its fixed length part, then variable, then third structure if applicable. I need to know the total length (always less than 256) so it can be written.

    Here is the "reduced version" where I have attempted to illustrate my problem while deleting lots of nasty details. This is very convoluted because the data will be sent to another platform. The structures were created because I had a difficult time defining all the hex constants.

    I do realize this code will not compile. I just need to know how to address my data.

    Code:
       #include <stdio.h>
       #include <string.h>   
       
       main(argc, argv)
       int argc;
       char *argv[];
    
       {    
       
         struct TLE_hdr {          /* hdr  */
             char x5A;
             char len1;
             char len2;
             char xD3;
             char xA0;
             char x90;
             char h10;
             char h20;
             char h30;       
         };
       
         struct TLE_search {         /* variable name */
             char slen;       
             char styp1;
             char styp2;
             char h40;
             char sname[10];
         };
         
         struct TLE_data {         /* data name */
             char dlen;       
             char dtyp;
             char h50;  
             char h60; 
             char dname[10];
         };  
    
           struct TLE_hdr *ptrh1, hdr1 =
               { 0x5A,0x00,0x00,0xD3,0xA0,0x90,0x00,0x00,0x00 };
    
           struct TLE_search *ptrs1, search1 =
               { 0x08,0x02,0x0B,0x00, "NAME" };        
    
           struct TLE_search *ptrs2, search2 =
               { 0x0B,0x02,0x0B,0x00, "ACCTNUM" };
    
           struct TLE_data *ptrc1, custdata =
               { 0x00,0x36,0x00,0x00 };  /* no initial account number or name*/
    
           struct TLE_data  *ptra1, acctdata =
               { 0x12,0x36,0x00,0x00 };  /* 14 byte acctno + 4 = 18 */
    
           char *cname[10];
           char *cacct[10];            
    
           char aname[5] = "NAME"; 
           char ename[5];
           char aacct[8] = "ACCTNUM";
           char eacct[8];
    
           char xlen;      
      
           FILE *fpout;       
          
           int tot_len = 0;
           int rc = 0;            
    
           char *buffer[255];
           
           char david[] = "David";
           char goliath[] = "Goliath";       
          
           printf ("Demo Version 1.0\n");
           printf ("%s Parm 0\n", argv[0]);
           printf ("%s Parm 1\n", argv[1]);
           
           if ((fpout = fopen(argv[1], "wb")) == NULL)
               {
               printf("Can not open output file!\n");
               return 4;
           }
    
           /* -------------------------------------------------*/
              
           *buffer =  malloc(256);
           
           ptrh1 = (struct TLE_hdr *) buffer;     
    
           memcpy(ptrh1, &hdr1, 9);               
           
           ptrs1 = ptrh1 + sizeof(hdr1);
           memcpy(ptrs1, &search1, sizeof(search1));
    
           ptrc1 = ptrs1 + sizeof(search1);               
           memcpy(ptrc1, &custdata, sizeof(custdata));
          
           cname = ptrc1 + sizeof(custdata);                
           memcpy(cname, &david, strlen(david) - 1);
                   
           memcpy((ptrc1).dlen, &xlen, 1);  /* length */             
    
           ptra1 = ptrc1 + strlen(david) - 1;       
    
           tot_len = cacct + 13 - buffer;
                                                             
           memcpy((ptra1).name, &goliath, strlen(goliath) - 1);  /* length */
    
           tot_len = ptra1 + strlen(goliath) - 1 - buffer;
    
           /* ----------------------------------------------------- */
                
           printf("Total length is: %d \n", tot_len);
                                           
           fwrite(*buffer, tot_len + 1, 1, fpout);
    
           free(buffer);
    
           close(fpout);
    
           return 0;
      )
    I know part of my problem is knowing when to use *var versus var, and using cast to make the compiler know I understand the bytes.

  2. #2
    Banned master5001's Avatar
    Join Date
    Aug 2001
    Location
    Visalia, CA, USA
    Posts
    3,685
    Its like parsing an AVI.... damn chunks...

    Here are a few things to note:

    Example 1:
    Code:
    struct sample1
    {
      size_t hsize, rate, height, width, whatever;
      char extra[0];
    };
    
    // when parsing
    void parse(FILE *stream)
    {
      struct sample1 r, *actual;
    
      fread(&r, sizeof(r), 1, stream);
      actual = malloc(r.hsize);
    
      if(!actual)
      {
        fseek(stream, -sizeof(r), SEEK_POS);
        return;
      }
    
      memcpy(actual, &r, sizeof(r));
      fread(actual+1, actual->hsize - sizeof(*actual), 1, stream);
    }
    If the variable length string is in the middle of two pieces of information (as they can be in JPEG, AVI, MPEG, MP3, etc. headers).

    Example 2:
    Code:
    // The names aren't pretty, but I usually do actually name them in this convention for clarity
    struct header_chunk_1
    {
      size_t csize;
      char empty[0];
    }; // a comment or something.....
    
    struct header_chunk_2
    {
      size_t asize;
      char empty[0];
    }; // author data
    
    struct header
    {
      header_chunk_1 *comment;
      header_chunk_2 *author;
    }
    Then parse similarly to Example 1. There aren't a lot of shortcuts. I remember posting a lot of stuff about this when I was writing an AVI parser... that was wayyyyyyyy back in the day. I am sure those threads have been purged by now

  3. #3
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Code:
       main(argc, argv)
       int argc;
       char *argv[];
    
       {    
    Do you have that in your real code, as well?

  4. #4
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    If you're confused about *var versus var, then there's not a lot of hope for the rest of it.

    var is a pointer. You can not use .fieldname with a pointer, you must use ->fieldname instead.
    *var is what it points to.

    Is there a good reason to not use the character arrays in TLEdata, printing out different strings afterwards? Or are you trying to write david and goliath where the character arrays would be?

  5. #5
    Registered User
    Join Date
    Oct 2008
    Posts
    7

    Clarification

    The output should look like:
    *fixed length**var length**fixed length2**var length2**fixed length3**varlength3*
    There may be two or three sections of this fixed length header, variable length data part. The fixed length data is hex (mostly, with some EBCDIC characters). The variable length data is all strings that were read in and translated to EBCDIC. So the \00 at the end of the strings was not copied to the output. The destination system knows how to read this stuff.

    Code:
     struct TLE_hdr1 *ptrh1, hdr1;
     struct TLE_search1 *ptrs1, search1;
     struct TLE_search2 *ptrs2, search2;
    
     ptrh1 = malloc(256);
    
     memcpy(ptrh1, hdr1, 9);  /* copies header to malloc'd buffer */
    
     ptrs1 = ptrh1 + 9;     /* Attempt to put in next section */
    Maybe this pseudo code illustrates my problem better. I don't know how to index my pointers and keep the compiler happy with mixing my structures and character arrays.

    Thanks

  6. #6
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    ptrh1 + 9 doesn't move 9 bytes, it moves 9 TLE_hdr1 structures (and I doubt that you have ten of them in a row to move over).

    If you want to be able to specify bytes, you'll need a char* pointing at your memory address.

  7. #7
    Banned master5001's Avatar
    Join Date
    Aug 2001
    Location
    Visalia, CA, USA
    Posts
    3,685
    Heh.. This is where I end up with that nasty looking code that I used sometime earlier in the week with double pointer casts.

    Look, my above code should do the trick just fine. However if its an issue of null terminated strings then just buffer your input. There is no general answer I can give to this question. So feel free to use the attach code button and put your code as an attachment.

  8. #8
    Registered User
    Join Date
    Oct 2008
    Posts
    7

    Getting closer

    Thanks Tabstop. It was a revelation. Assembler is so much simpler. Too bad I don't know Intel asm.

    Master5001, I looked ar your code again (Example 1) and it is very clear. What it lacks is handling multiple structures. I can easily handle 1. There are at most 3 structures (plus the header without any additional data) which have variable length data at their end. Thus my problem is properly setting up the address of structures 2 and 3. The reason that this is so important is that those fixed length headers have a single byte length field where the length of the following string is placed. After all this is done, then the very first header (9 bytes long) has a length field for the entire data area. Fortunately, this length is always < 256 so the length will fit in one byte. Otherwise I'd have to deal with the "big endian" versus "little endian" thing of system z versus Intel.

    Does this make sense now?

  9. #9
    Banned master5001's Avatar
    Join Date
    Aug 2001
    Location
    Visalia, CA, USA
    Posts
    3,685
    It makes sense. If it were me I would write this out in C++ just to keep my code looking cleaner. But I am a C guy, so no harm doing it all in C

    Here is how I typically handle this stuff, lugnut, and see if this is of more help:

    Example:
    Code:
    char *pool = malloc(4096), *p; // Lets just pretend like the 4k is allocated no problem.
    struct myheader *h1;
    struct myotherheader *h2;
    
    // after peforming some sort of reading function that fills up pool's 4k of data.
    
    p = pool;
    
    // Lets say the first piece if information in your file is the offset of the first header..
    
    p += *(size_t *)p;
    h1 = (void *)p;
    p = (h1 + 1) + h1->comment_length;
    
    h2 = (void *)p;
    I find it easier to have one pointer that processes the entire data chunk in terms of bytes (or sometimes dwords) then have it scout out where the headers are supposed to exist.

  10. #10
    Registered User
    Join Date
    Oct 2008
    Posts
    7

    Thanks again

    I think this code handles my situation. To be honest, it's a little (well, maybe not so little) over my head. But I've been known to study this kind of thing until I understood it!

    It just seems there ought to be a simpler way to do pointer arithmetic. Most of this is just trying to beat the compiler.

  11. #11
    Banned master5001's Avatar
    Join Date
    Aug 2001
    Location
    Visalia, CA, USA
    Posts
    3,685
    Quote Originally Posted by lugnut View Post
    Most of this is just trying to beat the compiler.
    You may be new, but its very good that you are capable of making that distinction. The main thing my code does is tries to dupe the compiler. But that is only one way of thinking about it. The other way of thinking about it is that you are just letting the compiler see specifically what you are trying to accomplish. Though to be honest, this is one area of programming where the compiler is a bigger foe than friend. Assembler is much nicer about these sorts of things... Actually assembler is almost the most appriate language for these sorts of things.

  12. #12
    Registered User
    Join Date
    Oct 2008
    Posts
    7

    Next try!

    I thought I understood this, but apparently I was wrong (just this once). Specifically, I really do not understand the code...
    Code:
     p += *(size_t *)p;
    h1 = (void *)p;
    p = (h1 + 1) + h1->comment_length;
    I made a big mistake in putting all that code out in my first post since it has a lot of irrelevant stuff in it. i decided to write another program. This program malloc's an area and then attempts to put a structure (fixed length) in it, followed by another structure which has a name (variable length in it). I put two of the latter. I also try to change the name in the second area since I have to update this stuff in place. Then I try to print this area. When I can get it to print, I get mostly garbage. I am using Watcom and its debugger. I traced the code and it was putting trash in the area. I modified it several times since and it won't compile now. All of this proves that I still can't navigate my way around a dynamic area.

    In the assembler world, I would get the address of this dynamic area, and use a "dsect" to describe it. I could then "move" anything I wanted to this area by the dsect name. I was thinking that's what the "*ptr" did for a structure. When I try to use this, I get compile errors. I have a trial copy of VS 2008 PRO, but I can't figure out how to set up a "project" and compile. MS has made this difficult, and their helps are useless. This is the usual case for me.

    I would write the mainframe asm that would do this but that wouldn't help... this is no system z. It's very frustrating to understand bytes, addressability, and all that stuff, and not be able to get this to work.

    If I can get this code to work, then I'll know how to make the real code work. It also has several diagnostic printf's. It took me a while to get the debugger going. Incidentally, I got all kinds of errors trying to initialize hdr1 and hdr2 with
    Code:
     hdr1 = {"a","b","c"};
    . I got this straight out of a book.

  13. #13
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by lugnut View Post

    If I can get this code to work, then I'll know how to make the real code work. It also has several diagnostic printf's. It took me a while to get the debugger going. Incidentally, I got all kinds of errors trying to initialize hdr1 and hdr2 with
    Code:
     hdr1 = {"a","b","c"};
    . I got this straight out of a book.
    I haven't carefully read the address part yet, so I won't speak to that yet -- maybe later. But what your book did say was
    Code:
    hdr1 = {'a', 'b', 'c'};
    which is not quite what you tried.

    Edit: Well, I suppose I should say that what works is using 'characters' and not "strings"; without looking at your book maybe it's a misprint.

  14. #14
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Looking at your code, I got rather worried; gcc shares my concerns:
    Code:
    master.c: In function ‘main’:
    master.c:40: warning: initialization makes integer from pointer without a cast
    master.c:46: warning: format ‘&#37;x’ expects type ‘unsigned int’, but argument 2 has type ‘char *’
    master.c:48: warning: assignment makes integer from pointer without a cast
    master.c:49: warning: assignment makes integer from pointer without a cast
    master.c:50: warning: assignment makes integer from pointer without a cast
    master.c:52: warning: assignment makes integer from pointer without a cast
    master.c:53: warning: assignment makes integer from pointer without a cast
    master.c:54: warning: assignment makes integer from pointer without a cast
    master.c:55: warning: assignment makes integer from pointer without a cast
    master.c:58: warning: assignment makes integer from pointer without a cast
    master.c:59: warning: assignment makes integer from pointer without a cast
    master.c:60: warning: assignment makes integer from pointer without a cast
    master.c:61: warning: assignment makes integer from pointer without a cast
    master.c:64: warning: target of assignment not really an lvalue; this will be a hard error in the future
    master.c:66: warning: format ‘%x’ expects type ‘unsigned int’, but argument 2 has type ‘struct data_1 *’
    master.c:74: warning: assignment from incompatible pointer type
    master.c:76: warning: format ‘%x’ expects type ‘unsigned int’, but argument 2 has type ‘struct data_2 *’
    master.c:80: error: conversion to non-scalar type requested
    master.c:80: error: incompatible type for argument 1 of ‘memcpy’
    master.c:90: error: conversion to non-scalar type requested
    master.c:92: warning: format ‘%x’ expects type ‘unsigned int’, but argument 2 has type ‘struct data_2 *’
    master.c:94: error: conversion to non-scalar type requested
    master.c:94: error: incompatible type for argument 1 of ‘memcpy’
    master.c:100: warning: passing argument 1 of ‘memcpy’ makes pointer from integer without a cast
    master.c:108: warning: format ‘%x’ expects type ‘unsigned int’, but argument 2 has type ‘char *’
    master.c:114: warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘int’
    master.c:39: warning: unused variable ‘aname’
    Line 40 is the end00 line; since that's a char it should just be char end00 = '\0', I believe.
    All the stuff starting at line 46 is what I said in the last post -- chars are ' ', not " ". I think you have managed to not get any characters inside your struct at all. Line 64 says that casting the left side of an assignment is just plain silly. Line 80 is just all messed up; that's a memcpy line:
    Code:
    memcpy(*ptrh2, (struct data_2) &hdr2,  4 + strlen(tommy));
    *ptrh2 isn't a pointer, its a struct header, so that doesn't work. You just want ptrh2 there, I reckon. The cast on the second argument is Very Bad for two reasons -- one, you try to cast an address into an actual object, which just ain't never gonna work, and two, memcpy takes void * anyway.

    You get the idea. I didn't try to figure out whether all the numbers were right, since it wasn't at all clear to me what you were trying to accomplish in several places.

  15. #15
    Registered User
    Join Date
    Oct 2008
    Posts
    7
    The difference between " and ' was ... surprising.

    I guess I'm too old to play this game. The mainframe world uses all caps. VBA (I write a lot of Excel macros) capitalizes a lot of directives (If), but Lua goes ape when I capitalize. VBA and Lua use if...then, C doesn't.

    All this is a poor excuse, but it's all I have. Maybe tomorrow will be better.

    Thanks for the assistance.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. malloc, structures, arrays and sudoku!!
    By AmbliKai in forum C Programming
    Replies: 13
    Last Post: 10-15-2008, 03:05 AM
  2. Structures, arrays, pointers, malloc.
    By omnificient in forum C Programming
    Replies: 9
    Last Post: 02-29-2008, 12:05 PM
  3. pointers and structures
    By coo_pal in forum C Programming
    Replies: 1
    Last Post: 07-23-2003, 04:45 AM
  4. returning an array of pointers to structures
    By dharh in forum C Programming
    Replies: 9
    Last Post: 02-06-2003, 03:26 PM
  5. Freeing pointers in structures
    By jim50498 in forum C Programming
    Replies: 4
    Last Post: 03-08-2002, 12:53 PM