-2

I am currently working on a task where I need to print the address of a variable. It would be easy to use printf %p but I am only allowed to use write from unistd.

I tried casting the pointer in to an unsigned integer and uintptr_t and then converting it into a hexadecimal number. With uintptr_t it works but with an unsigned integer it only prints half of the address. Maybe someone can explain me why this is the case?

I also saw some solutions using ">>" and "<<" but I didn't get why that works. It would be nice if someone can explain a solution using "<<" and ">>" step by step, because I am not sure if I am allowed to use uintptr_t.

this is the code I use to cast it into a unsigned int / unitptr_t / unsigned long long (I know that ft_rec_hex is missing leading 0's):

void ft_rec_hex(unsigned long long nbr)
{
    char tmp;

    if (nbr != 0)
    {
        ft_rec_hex(nbr / 16);
        if (nbr % 16 < 10)
            tmp = nbr % 16 + '0';
        else
            tmp = (nbr % 16) - 10 + 'a';
        write(1, &tmp, 1);
    }
}

int     main(void)
{
    char c = 'd';
    unsigned  long long ui = (unsigned long long)&c;
    ft_rec_hex(ui);
}
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
jsiller
  • 78
  • 7

2 Answers2

2

It looks like only half of the address is printed because the "unsigned integer" you used has only half size of uintptr_t. (note that uintptr_t is an unsigned integer type)

You can use an array of unsigned char to store data in a pointer variable and print that to print full pointer withput uintptr_t.

Using character types to read objects with other type is allowed according to strict aliasing rule.

#include <stdio.h>
#include <unistd.h>

void printOne(unsigned char v) {
    const char* chars = "0123456789ABCDEF";
    char data[2];
    data[0] = chars[(v >> 4) & 0xf];
    data[1] = chars[v & 0xf];
    write(1, data, 2);
}

int main(void) {
    int a;
    int* p = &a;
    /* to make sure the value is correct */
    printf("p = %p\n", (void*)p);
    fflush(stdout);

    unsigned char ptrData[sizeof(int*)];
    for(size_t i = 0; i < sizeof(int*); i++) {
        ptrData[i] = ((unsigned char*)&p)[i];
    }
    /* print in reversed order, assuming little endian */
    for (size_t i = sizeof(int*); i > 0; i--) {
        printOne(ptrData[i - 1]);
    }

    return 0;
}

Or read data in a pointer variable as unsigned char array without copying:

#include <stdio.h>
#include <unistd.h>

void printOne(unsigned char v) {
    const char* chars = "0123456789ABCDEF";
    char data[2];
    data[0] = chars[(v >> 4) & 0xf];
    data[1] = chars[v & 0xf];
    write(1, data, 2);
}

int main(void) {
    int a;
    int* p = &a;
    /* to make sure the value is correct */
    printf("p = %p\n", (void*)p);
    fflush(stdout);

    /* print in reversed order, assuming little endian */
    for (size_t i = sizeof(int*); i > 0; i--) {
        printOne(((unsigned char*)&p)[i - 1]);
    }

    return 0;
}
MikeCAT
  • 73,922
  • 11
  • 45
  • 70
  • Ok I tried understanding this and got confused a lot :D. In the second solution you are casting a pointer to the the pointer p into an unsigned char*. (like this: "unsigned char *d = (unsigned char*)&p;"). What exactly does that? what would be the value of f.e. d[0] or d[10] ? – jsiller Apr 14 '21 at 12:33
  • It is making `d` to point at the memory that is assigned to `p`, enabling to view the data corresponding to `p` as an array of `unsigned char`. `d[0]` will be the least byte of the pointer (if it is little endian), and `d[10]` will be out-of-range (if the size of a pointer is less than 11 bytes). – MikeCAT Apr 14 '21 at 12:53
-1

It would be easy to use printf %p but I am only allowed to use write from unistd.

Then form a string and print that.

int n = snprintf(NULL, 0, "%p", (void *) p);
char buf[n+1];
snprintf(buf, sizeof buf, "%p", (void *) p);
write(1, buf, n);

Using a pointer converted to an integer marginally reduces portability and does not certainly form the best textual representation of the pointer - something implementation dependent.


With uintptr_t it works but with an unsigned integer it only prints half of the address.

unsigned is not specified to be wide enough to contain all the information in a pointer.

uintptr_t, when available (very common), can preserve most of that information for void pointers. Good enough to round-trip to an equivalent pointer, even if in another form.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • I am only allowed to use write no other function is allowed. – jsiller Apr 14 '21 at 12:27
  • @jsiller Hard to write a program without `main()`. Good luck. – chux - Reinstate Monica Apr 14 '21 at 12:32
  • I am actually not supposed to write a main. I only use the main to test it but the task only asks for a function which prints a memory address only using write. I can write other functions myself and use them but writing snprintf wouldnt be that easy to write. – jsiller Apr 14 '21 at 12:37