Quantcast
Channel: Unsigned 32-bit integer to binary string function - Code Review Stack Exchange
Viewing all articles
Browse latest Browse all 5

Answer by Toby Speight for Unsigned 32-bit integer to binary string function

$
0
0

The interface is problematic. What output does this code produce?

#include <stdio.h>int main(void){    const char *s1 = uint32_to_binarystr(0);    const char *s2 = uint32_to_binarystr(1000000);    printf("%s\n%s\n", s1, s2);}

You might expect it to output 00000000000000000000000000000000 followed by 00000000000011110100001001000000, but that's not what we get. Do you see why?

The problem is

    static char binarystr[33];  ⋮    return binarystr;

s1 and s2 both point to the same data!

The two usual solutions to this problem are

  • return newly-allocated memory that the caller must free(), or
  • have the caller supply a buffer into which to write.

I tend to prefer the latter - it avoids overhead of heap allocation in many cases, and reduces the risk of memory leaks due to misuse.

I would write that as

#include <stdbool.h>#include <stdint.h>#include <string.h>bool uint32_to_binarystr(char *binarystr, size_t len, uint32_t n) {    if (len < 33) {        // 32 bits + 1 terminating NUL char        return false;    } ⋮    return true;}
int main(void){    char s1[33];    char s2[33];    assert(uint32_to_binarystr(s1, sizeof s1, 0));    assert(uint32_to_binarystr(s2, sizeof s2, 1000000));    printf("%s\n%s\n", s1, s2);}

Passing the length available might seem unnecessary (who would supply a buffer that's too short?) but it does turn out to be a good idea in practice.


For the algorithm, I would approach this with a "moving bit" (for simplicity, I'll demonstrate the uint8_t version). If we start with our number abcdefgh and an initial mask 10000000, then we can decide whether the first digit is 0 or 1 using a bitwise AND operation n & mask. Then move to the next digit by shifting mask rightwards by one. When the bit runs off the end, mask becomes zero, and we know we're done.

In code (and switching back to 32 bits), that looks like:

for (uint32_t mask = 0x80000000;  mask;  mask >>= 1) {    if (n & mask) {        /* we have a 1 bit */    } else {        /* we have a 0 bit */    }}

We can simplify the body of the loop, too, because C guarantees that '1' is one more than '0':

char digit = '0'+ ((n & mask) != 0);

If we write all the characters like this, then there's no need for memset() at the beginning.

Putting this all together, I have a replacement which is worth studying:

#include <stdbool.h>#include <stddef.h>#include <stdint.h>bool uint32_to_binarystr(char *buf, size_t len, uint32_t n) {    if (len < 33) {        // 32 bits + 1 terminating NUL char        return false;    }    for (uint32_t mask = 0x80000000;  mask;  mask >>= 1) {        bool bit_is_set = n & mask;        *buf = '0'+ bit_is_set;++buf;    }    *buf = '\0';          /* add the terminator */    return true;}

Viewing all articles
Browse latest Browse all 5

Trending Articles