Thread: Extract the last 4 bits in a pointer?

  1. #1
    Registered User
    Join Date
    Oct 2021
    Posts
    16

    Question Extract the last 4 bits in a pointer?

    Hi everyone! I'm new here

    I have an assignment to, among other things, write a pointer's value in hexadecimal.

    I realized I could do this easily by looking at the pointer's bit themselves. If I can isolate the last 4 bits, put them in, say, a char, and send it to my function which simply prints from 0 to f depending on value, I can just iterate 16 times, look at 16 packs of 4 bits, and print from left to right.

    I know how to use & and >>, so I was aiming at starting at (address >> 15 * 4) & 0x000000000000000f, and go down from 15 to 14 etc to 0 and then end.

    However, when I try to do this, since my function requires a char, it tells me

    invalid operands to binary >> (have ‘void *’ and ‘int’)

    If I try to cast the addr as char, it unsurprisingly tells me

    cast from pointer to integer of different size

    How could I store the last 4 bits of my address (shifted with >>) in a char without errors?

    Thanks!

  2. #2
    Registered User
    Join Date
    Dec 2017
    Posts
    1,626
    Code:
    #include <stdio.h>
    #include <stdint.h>
    #include <inttypes.h>  // this includes stdint.h, too
     
    int main()
    {
        void *p = &p; // holds address of itself
     
        // The usual way.
        printf("%p\n", p); // format p prints void* in hex
     
        // Integer version of pointer value (needs stdint.h).
        uintptr_t u = (uintptr_t)p;
     
        // Another way (needs special format spec from inttypes.h).
        printf("0x%" PRIxPTR "\n", u);
     
        // What you are trying to do.
        printf("0x");
        for (int i = sizeof u * 2; i-- > 0; )
            printf("%x", (unsigned)((u >> (i * 4)) & 0xf));
        putchar('\n');
     
        // C23 has UINTPTR_WIDTH which gives the bit width of uintptr_t,
        // so instead of sizeof u * 2 we could say UINTPTR_WIDTH / 4.
        // sizeof u * 2 assumes 8-bit bytes.
     
        // If you want to assume a 64-bit pointer, then you could do this:
        long long n = (long long)p;
        printf("0x%016llx\n", n);
     
        // Or this:
        printf("0x");
        for (int i = 16; i-- > 0; )
            printf("%x", (unsigned)((n >> (i * 4)) & 0xf));
        putchar('\n');
     
        return 0;
    }
    A little inaccuracy saves tons of explanation. - H.H. Munro

  3. #3
    Registered User
    Join Date
    Oct 2021
    Posts
    16
    My predicament is that I can't use printf. It'd be too easy otherwise. I can only use write.

    I started out casting the address as a long int and iterating to get the number of hexa digits it'd turn out to be (dividing by 16 over and over), and making some (too many) functions to decipher the decimal and write it as hexadecimal (with powers of 16).

    But then I realized heck, bundles of 4 bits can be used to get something between 0 and 15 that I can very easily convert into hexa and I can simplify my code tenfold.

    I understand things more clearly thanks to your explanatien now!

    What I'm gonna do is cast the void * as a long int, store it in a long int, and then send the last 4 bits of that long int cast as a char, into my function which is supposed to write my characters in hexa. That way I'm not passing a long int around when I only need 4 bits out of it.

    Doesn't make much difference for an assignment but I prefer doing it "right" if I can.

    Also thanks for pointing out 0x000000000000000f is just 0xf ^-^'' I feel dumb.

  4. #4
    Registered User
    Join Date
    Dec 2017
    Posts
    1,626
    Sounds good. Make sure it's an unsigned long, though. I should have used an unsigned long long in the code I posted. Note that, technically, long is not guaranteed to be 64 bits, although it almost certainly is on a 64-bit system. However, long long is guaranteed to be at least 64 bits on any system.

    EDIT: Here's one way of doing it with write and unsigned long (assuming 64-bit void* pointer and long) :
    Code:
    #include <unistd.h>
     
    int main()
    {
        void *p = &p; // holds address of itself
        unsigned long n = (unsigned long)p;
     
        for (int i = 16; i-- > 0; )
        {
            unsigned x = (unsigned)((n >> (i * 4)) & 0xf);
            write(1, "0123456789ABCDEF" + x, 1);
        }
     
        write(1, "\n", 1);
     
        return 0;
    }
    Last edited by john.c; 10-16-2021 at 02:55 PM.
    A little inaccuracy saves tons of explanation. - H.H. Munro

  5. #5
    Registered User
    Join Date
    Oct 2021
    Posts
    16
    What happens if it's just a long int? I did it that way, and it seems to be working as expected.

    Here's my output:

    Extract the last 4 bits in a pointer?-messages_0-png

    Here's my function that prints the address

    Code:
    void    ft_print_addr(void *addr)
    {
        long int    addri;
        int   i;
    
        addri = (long int)addr;
        i = 15;
        while (i >= 0)
        {
      ft_puthex((char)((addri >> i * 4) & 0xf));
      i--;
        }
        write(1, ": ", 2);
    }
    ft_puthex simply adds a '0' if < 10 or else - 10 + 'a' and writes it.

  6. #6
    Registered User
    Join Date
    Dec 2017
    Posts
    1,626
    Since pointers are obviously unsigned integers it seems logical to store it in an unsigned integer.

    At any rate, right-shifting a signed integer with a negative value (high bit set) is implementation-defined. See C11 draft standard (n1570), section 6.5.7 paragraph 5. Left-shifting such a value (or even a positive value whose true result is not representable in the result type) is undefined (paragraph 4). Due to these uncertainties, we tend to use unsigned integers for shifting operations, lest we get different results on different systems.
    Last edited by john.c; 10-16-2021 at 04:58 PM.
    A little inaccuracy saves tons of explanation. - H.H. Munro

  7. #7
    Registered User
    Join Date
    Oct 2021
    Posts
    16
    Oh, right.

    Is it when you right shift a signed integer with a high bit set at 1 and it fills new bits with 1?

    So that depends on your architecture? Some of them fill new bits with 0?

    I don't use any of these new bytes myself, and I set them at 0 anyway. I don't think it would be problematic for my program, right? But it's a good thing to know! Thanks

  8. #8
    Registered User
    Join Date
    Dec 2017
    Posts
    1,626
    Yes, the reasonable implementation-defined options for a right-shift would be whether it is "arithmetic" or "logical". I'm aware that it works in either case in your situation, but I don't understand what you have against unsigned integers. It's pretty much always better to use them with bitwise ops.
    A little inaccuracy saves tons of explanation. - H.H. Munro

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Changing particular bits and detecting toggled bits
    By Nordomus in forum C++ Programming
    Replies: 11
    Last Post: 04-13-2018, 12:07 PM
  2. Replies: 1
    Last Post: 02-29-2012, 09:38 AM
  3. How can I extract the most significant 4 bits?
    By mr_coffee in forum C Programming
    Replies: 6
    Last Post: 04-03-2009, 01:40 PM
  4. Extracting certain bits from sequence of bits
    By lucaspewkas in forum C Programming
    Replies: 5
    Last Post: 10-06-2007, 12:22 AM
  5. copy some bits into a 8 bits binary number
    By Unregistered in forum C Programming
    Replies: 6
    Last Post: 05-29-2002, 10:54 AM

Tags for this Thread