1

I'm trying to code the function :

void memdump(void *p, size_t nmemb, size_t size);

Whose purpose is to display in the correct order the bits contained in the variable pointed to by p. Not checking whether p is NULL is deliberate. Using write() also.

The problem is that I want the display to be correct, whatever the endianess of the machine.

Here's my code:

#include <unistd.h>
#include <stdint.h>

void memdump(void *p, size_t nmemb, size_t size)
{
    size_t total_bits = nmemb * size * 8;
    unsigned char *ptr = (unsigned char *)p;

    for (size_t i = 0; i < total_bits; i++) {
        unsigned char bit = (ptr[i / 8] >> (7 - (i % 8))) & 0x01;
        char c = bit ? '1' : '0';
        write(STDOUT_FILENO, &c, sizeof(char));
        if ((i + 1) % 8 == 0)
            write(STDOUT_FILENO, " ", 1);
    }
    write(STDOUT_FILENO, "\n", sizeof(char));
}

int main(void)
{
    int i = 0;
    int j[2] = {1, 0};
    char c = 'a';
    char *s = "za";
    uint64_t size = 213213;

    memdump(&i, 1, sizeof(int));
    memdump(j, 2, sizeof(int));
    memdump(&c, 1, sizeof(char));
    memdump(s, 2, sizeof(char));
    memdump(&size, 1, sizeof(size_t));
    return 0;
}

I therefore expect the following result :

00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001 00000000 00000000 00000000 00000000
01100001
01111010 01100001
00000000 00000000 00000000 00000000 00000000 00000011 01000000 11011101

But I get this :

00000000 00000000 00000000 00000000 
00000001 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
01100001 
01111010 01100001 
11011101 01000000 00000011 00000000 00000000 00000000 00000000 00000000 

Can you help me?

Here's a link to a reproducible example : https://onlinegdb.com/4e5Ei2OUS

Thanks in advance

[UPDATE]

I don't know how to take endianess into account, that's my question. My memdump function is supposed to have the expected behavior specified above.

The reason I don't check whether the pointer is NULL or not is simply that this function is part of a library based on the standard C library and its principles (i.e. developers are responsible for their code, so pass a NULL string to strlen() and you won't get any warnings).

My function is supposed to display the bytes in the order in which they are inserted into the variable example :

int test[2] = {0 , 1};

The expected result is :

00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001

It doesn't matter how endianless the machine is.

Virgil G.
  • 75
  • 6
  • *"Not checking whether p is NULL is deliberate."* you cannot dereference null pointers. So if you don't check for that, then you must make sure by other means, that you're not attempting to dereference a null pointer. Be advised, that the C programming language makes no concessions about the internal values of pointers as long as they adhere to pointer arithmetic. On architectures with a dedicated trap value for pointers (that might be considered NIL), when accessed using C, they must be comparatively equal to a 0 integer literal. i.e. regardless of architecture it's always `NULL == (void*)0`. – datenwolf Jul 21 '23 at 13:46
  • 5
    Where do you attempt to account for the "endianess of the machine"? – jarmod Jul 21 '23 at 13:46
  • Re “The problem is that I want the display to be correct, whatever the endianess of the machine”: There is no universal meaning for the word “correct”. If one person wants the bits of each byte displayed in the order they appear in memory in the object, then displaying the bytes in the order they appear in memory is correct. If another person wants the bits of an object displayed in order from most significant to least significant, then displaying the bits in the order from most significant to least significant is correct. What is your `memdump` routine suppose to do? – Eric Postpischil Jul 21 '23 at 13:52
  • What does it mean for the display to be correct "whatever the endianess of the machine"? In that regard, note in particular that there is no defined relative order of the bits in a `char`, as these are not individually addressable. – John Bollinger Jul 21 '23 at 13:52
  • @JohnBollinger: C does associate order with the bits in `char` and integer types, via the left-shift and right-shift operators. – Eric Postpischil Jul 21 '23 at 13:55
  • 4
    @datenwolf: The comment about not checking for `NULL` being deliberate was intended to inform you a choice had been made that `memdump` would not perform such a check, that ensuring a null pointer was not passed was a responsibility of the caller, and that this issue was not part of the question and did not need to be discussed or commented upon. – Eric Postpischil Jul 21 '23 at 13:56
  • Re “whatever the endianess of the machine”: Do you want to support mixed-endian C implementations, or only big-endian and little-endian? (Also note endianness in a C implementation is technically a property of the C implementation, not the underlying machine.) – Eric Postpischil Jul 21 '23 at 13:58
  • You probably want to check the endianness of the machine (read this https://stackoverflow.com/questions/1001307/detecting-endianness-programmatically-in-a-c-program), and act accordingly. – Jabberwocky Jul 21 '23 at 13:58
  • 1
    As you well know, @EricPostpischil, left- and right- shift are defined with respect to semantic values, not representation in storage. Else their effects would depend on endianness. – John Bollinger Jul 21 '23 at 13:59
  • 1
    @JohnBollinger: And what is the point of your allegation there is no defined relative order of the bits in a `char`? They are commonly represented in text with the most significant bit on the left. The shift operators associate them with the most significant bit on the left. The OP’s sample desired output shows them with the most significant bit on the left. So you bring up this issue that is apparently not an issue. Why? How does lack of individual addressability in any way relate to whether we associate left with more significant bits? – Eric Postpischil Jul 21 '23 at 14:03
  • @EricPostpischil, did I not make that remark in the context of the question "What does it mean for the display to be correct 'whatever the endianess of the machine'"? Is it so hard to imagine that I might be interested in confirming that the OP really wants to present the bits of each byte from most-significant to least even when (if) the bytes themselves are presented from least- to most-significant? – John Bollinger Jul 21 '23 at 14:19
  • @EricPostpischil I answered you in the post update – Virgil G. Jul 21 '23 at 14:32
  • 3
    Per your updates, you want output that is independent of the machine's representation of integers (endianness), but this is hard to reconcile with the generic signature of your function. Is it to assume that all inputs will be arrays of `nmemb` integers, each of the size given by `size`? Note well that that would not support structure or union or pointer or floating-point types directly. If you want output that reflects the semantic values of objects then you probably need code that takes data types into account more explicitly. – John Bollinger Jul 21 '23 at 14:38
  • 3
    Simply put, your expectation is wrong because your belief about "the order which bytes appear in the variable" is wrong. – Ben Voigt Jul 21 '23 at 14:52
  • Simply adjust the order in which you print the bytes based on the endianness of the machine. You can see how [here](https://stackoverflow.com/q/2100331/589924). – ikegami Jul 21 '23 at 16:10
  • The notion of endianness is only meaningful when talking about *numeric* types which are larger that one byte in size. Are you planning to always pass arrays of numeric types to your function? Note, there exist architectures with other endiannesses than big and little, although they are rare. – n. m. could be an AI Jul 21 '23 at 16:15

1 Answers1

1

The expected result is :

1
00000000 00000000 00000000 00000001

The most significant bit is to the left. To the left are bytes with the lowest address. Which means the most (i.e. "big") significant bit has the lowest address. So this is big endian. Read https://en.wikipedia.org/wiki/Endianness .

how to take endianess into account

Apply dynamic programming. Two steps:


nmemb * size * 8;

Consider using CHAR_BITS from limits.h

memdump(&i, 1, sizeof(int));

Consider using sizeof(i) and sizeof(*j) in case you change variable types.

char *s = "za";

Note that endianess has no effect on the order of z and a. z always will be first (have lowest address), then a, then zero byte. You might consider optionally applying endianess conversion.

And, also, note that with newest glibc on linux and in C23 we have printf("%b", 123); to print stuff binary! Yay!

KamilCuk
  • 120,984
  • 8
  • 59
  • 111