Thread: Am I doing this pointer magic the right way?

  1. #1
    Registered User
    Join Date
    Jun 2013
    Posts
    5

    Am I doing this pointer magic the right way?

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    
    int main(int argc, char ** argv) {
    
    
        char * s = malloc(6 * sizeof(char));
        s[0] = 'h';
        s[1] = 'e';
        s[2] = 'l';
        s[3] = 'l';
        s[4] = 'o';
        s[5] = '\0';
    
    
        double * buf = malloc(3 * sizeof(double));
        buf[0] = 1.23;
        buf[1] = 4.56;
        buf[2] = 7.89;
    
    
        printf("%s %p %zu %u\n", s, s, strlen(s), sizeof(char *));
    
    
        printf("0 %p %f\n", &(*(buf + 0)), *(buf + 0));
        printf("1 %p %f\n", &(*(buf + 1)), *(buf + 1));
        printf("2 %p %f\n", &(*(buf + 2)), *(buf + 2));
    
    
        memcpy(&(*(buf + 1)), &s, sizeof(char *));
    
    
        printf("%p\n", &buf[1]);
        printf("%s\n", buf[1]);
        /*printf("%s\n", (char *) buf[1]);*/
        printf("%s\n", *((char **) &buf[1]));
    
    
        free(buf);
        free(s);
    
    
        return 0;
    
    
    }
    I am toying with putting pointers to strings inside a double array. It's very hackish of course, but I want to better understand what's going on the compiler level.

    In the sample code above I am allocating and populating a double array, and on row 32 I write a pointer to a string to an array item instead of a double value (on my system a char * pointer and a double has the same size, 8).

    That writing part seems to go fine, but I get some warnings and errors when I try to deference the array item.

    1)

    I would expect row 37 to be equivalent to row 36.


    But they are not equivalent, row 37 generates a compiler error and warning:


    test.c:37:5: error: cannot convert to a pointer type
    test.c:37:5: warning: reading through null pointer (argument 2)


    What does this mean?


    2)


    For row 37, I am casting the item in the double buffer to char *, which is the correct type for a string is it not?


    And row 36 generates this warning:


    test.c:36:5: warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘double’


    So why can't I cast the item value correctly like I do on row 37?


    3)


    When the array item is referenced on row 38 however, the code compiles without warnings.

    Is that the correct way of dereferencing the array item?
    Last edited by itsinthecompute; 06-13-2013 at 12:40 AM. Reason: Fixing row numbers

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by itsinthecompute
    I am toying with putting pointers to strings inside a double array.
    What do you mean by that?

    Frankly, this:
    Code:
    printf("0 %p %f\n", &(*(buf + 0)), *(buf + 0));
    printf("1 %p %f\n", &(*(buf + 1)), *(buf + 1));
    printf("2 %p %f\n", &(*(buf + 2)), *(buf + 2));
    
    memcpy(&(*(buf + 1)), &s, sizeof(char *));
    looks like a complicated way of writing:
    Code:
    printf("0 %p %f\n", (void*)&buf[0], buf[0]);
    printf("1 %p %f\n", (void*)&buf[1], buf[1]);
    printf("2 %p %f\n", (void*)&buf[2], buf[2]);
    
    memcpy(&buf[1], &s, sizeof(char *));
    or equivalently:
    Code:
    printf("0 %p %f\n", (void*)(buf + 0), buf[0]);
    printf("1 %p %f\n", (void*)(buf + 1), buf[1]);
    printf("2 %p %f\n", (void*)(buf + 2), buf[2]);
    
    memcpy(buf + 1, &s, sizeof(char *));
    But what on earth are you trying to do with that memcpy?

    Quote Originally Posted by itsinthecompute
    1)

    I would expect row 37 to be equivalent to row 36.


    But they are not equivalent, row 37 generates a compiler error and warning:


    test.c:37:5: error: cannot convert to a pointer type
    test.c:37:5: warning: reading through null pointer (argument 2)


    What does this mean?
    What is the type of buf[1]?
    Last edited by laserlight; 06-13-2013 at 01:03 AM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    Registered User
    Join Date
    Jun 2013
    Posts
    5
    First, you are right. The memcpy line is long winded, I just wrote it that way as practice, it's definitely not in the most brief form.

    Second, I don't want the string itself in the double array, just a pointer to the string that resides somewhere else on the heap. Is this not what my code does?

    I am first to admit that I am not an expert in pointers, so I'd gladly take in some pointers to learn more. Pun intended

  4. #4
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by itsinthecompute
    Second, I don't want the string itself in the double array, just a pointer to the string that resides somewhere else on the heap. Is this not what my code does?
    Oh, I see. Basically, you want to store a pointer to a char in the storage of an element of an array of double, then print the string whose first character is pointed to by that pointer through the element of the array of double. However, you are faced with a compile error telling you that it could not convert the double to a pointer to char.

    Consequently, it sounds like you cannot do what you want to do. You could create another pointer to char and then memcpy the contents of the storage of the double array element to it, but that would be rather pointless. Then again, this whole exercise sounds rather pointless
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  5. #5
    Registered User
    Join Date
    Jun 2013
    Posts
    5
    And btw, the example above doesn't make too much sense, especially from a type perspective. In reality the array is table based with specific columns being in string format (or rather containing a pointer to a string rather than a double value).

  6. #6
    Registered User
    Join Date
    Jun 2013
    Posts
    5
    I can read and write to an array item with any errors and warnings. It's just that I have to read the long winded way on row 38. The error comes when I try to do it a shorter way like on row 37...

  7. #7
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by itsinthecompute
    It's just that I have to read the long winded way on row 38.
    Oh yeah, I missed that. That works because you simply re-interpret the underlying array of double objects as an array (or rather pointer to the first element thereof) of pointers to char (which is what you were mentally doing in the first place, at least for that particular element), thus avoiding the problematic attempt to convert a double to a char*.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  8. #8
    Registered User migf1's Avatar
    Join Date
    May 2013
    Location
    Athens, Greece
    Posts
    385
    @itisinthecompute:

    If I get this right, the problem is that addresses are unsigned integers but you are forcefully store one in a double. If you define buf as an array of size_t instead of double (and adjust your code accordingly) both your printf() on lines 37 and 38 will work (and the one on line 36 will also need a cast to char *).

  9. #9
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    You can do this:

    Code:
    double arr[] = {1.2, 3.4, 5.6, 7.8, 9.0};
    unsigned char *p = (unsigned char *)&arr[0];
    /* use p */

  10. #10
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by whiteflags View Post
    You can do this:

    Code:
    double arr[] = {1.2, 3.4, 5.6, 7.8, 9.0};
    unsigned char *p = (unsigned char *)&arr[0];
    /* use p */
    That's not really relevant here though. I expect that migf1's suggestion will work since the conversion from size_t to char* should not be problematic, though it too might not be relevant because itsinthecompute is "toying with putting pointers to strings inside a double array".
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  11. #11
    Registered User migf1's Avatar
    Join Date
    May 2013
    Location
    Athens, Greece
    Posts
    385
    Quote Originally Posted by laserlight View Post
    ...itsinthecompute is "toying with putting pointers to strings inside a double array".
    Which is kinda bizarre, I mean I can't think right now why such a thing may be useful.

    In any case, I think memcpy() is tricky anyway if the size of a buf element differs from the size of a char pointer.

  12. #12
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    Quote Originally Posted by laserlight View Post
    That's not really relevant here though. I expect that migf1's suggestion will work since the conversion from size_t to char* should not be problematic, though it too might not be relevant because itsinthecompute is "toying with putting pointers to strings inside a double array".
    Er, well, since that makes no sense, I decided to mention something that might make sense. I mean, by itself, punning any object into a string of bytes allows you to do pretty hacky things.


    I'd like to point out that at the machine level you might want to learn assembly language instead since C can specifically compile to that level and show you what instructions it's going to use. It's as bare metal as it gets.

    I haven't actually done this and from what I can tell most manufacturers don't care about assembly programmers anymore because the code is not as readable as it used to be. So YMMV.

  13. #13
    Registered User
    Join Date
    Jun 2013
    Posts
    5
    Ok so I fired up GDB to debug and take a look at the memory in runtime. And I confirmed the value of buf[1] is indeed the address of the first item of s. I then assume that the compile error on row 37 is due to the fact that it's not guaranteed that the value of buf[1] can fit into a char *. Accordingly, the only way to dereference buf[1] is to take a step back and cast the address of buf[1] instead and cast that value as a pointer to a pointer to a char, and then dereference that instead.

    Lastly, I added these lines to verify the logic:

    Code:
        char * new;
        memcpy(&new, &buf[1], sizeof(char *));
    
    
        printf("&new=%p\n", &new);
        printf("new=%p\n", new);
        printf("new=%s\n", new);
    which outputted

    Code:
    sizeof(s)=8
    s=0x100f00950
    &s=0x7fff60abfa00
    s[0]=h
    s[1]=e
    s[2]=l
    s[3]=l
    s[4]=o
    &s[0]=0x100f00950
    &s[1]=0x100f00951
    &s[2]=0x100f00952
    &s[3]=0x100f00953
    &s[4]=0x100f00954
    
    
    sizeof(buf)=8
    buf=0x100f00960
    &buf=0x7fff60abf9f8
    buf[0]=1.230000
    buf[1]=4.560000
    buf[2]=7.890000
    &buf[0]=0x100f00960
    &buf[1]=0x100f00968
    &buf[2]=0x100f00970
    
    
    &buf[1]=0x100f00968
    buf[1]=hello
    
    
    &new=0x7fff60abf9f0
    new=0x100f00950
    new=hello
    which shows that the pointer value of new is the same as the address of the first item of the string.

    So it seems the logic works at least. I would be very interested in comments on the compile error on row 37 though, is my attempt at an explanation correct?
    Last edited by itsinthecompute; 06-13-2013 at 09:09 AM.

  14. #14
    Registered User
    Join Date
    Apr 2013
    Posts
    1,658
    Quote Originally Posted by itsinthecompute View Post
    I would be very interested in comments on the compile error on row 37 though, is my attempt at an explanation correct?
    In C or C++, a cast will invoke a conversion if appropriate. For example, "(int)(float)1.5" will convert the double 1.5 to a float, then convert float 1.5 to an integer 1. The error on line 37 occurred because your compiler was "smart" enough to know that a "%s" format specifier needs an pointer, and it can't convert a double to a pointer. Note that on a 32 bit machine, a pointer takes 4 bytes, while a double takes 8 bytes, so *(char **)(&buf[1]), is only going to use 4 of the 8 bytes of the double, and which 4 bytes are used depends if your machine is little endian or big endian. On a little endian machine (PC), ((char **)(&buf[1]))[0] will be the least significant 4 bytes of buf[1] and ((char **)(&buf[1]))[1] will be the most significant 4 bytes of buf[1].

    Is there a reason to be doing these type of conversions?
    Last edited by rcgldr; 06-13-2013 at 04:58 PM.

  15. #15
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Is there a reason to be doing these type of conversions?
    O_o

    I can't speak for him of course, but it seems to me like he is trying to wrap his head around variable type by intentionally breaking them while watching what happens.

    Soma

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Magic Numbers
    By strokebow in forum Game Programming
    Replies: 12
    Last Post: 11-19-2008, 12:57 AM
  2. Magic Box
    By dantu1985 in forum C Programming
    Replies: 5
    Last Post: 08-18-2007, 12:04 PM
  3. help with magic square
    By dragon_heart in forum C++ Programming
    Replies: 3
    Last Post: 11-26-2004, 05:56 PM
  4. the magic of MAGIC numbers
    By borko_b in forum A Brief History of Cprogramming.com
    Replies: 8
    Last Post: 06-27-2002, 02:36 AM
  5. A little STL magic please?
    By QuestionC in forum C++ Programming
    Replies: 2
    Last Post: 02-12-2002, 05:56 PM

Tags for this Thread