'Converting an uint8_t[] to an IP address string [duplicate]

I am currently learning C. My ecosystem consists of an Espressif ESP-32 micro controller and Eclipse CDT IDE.

I am trying to convert an uint8_t[] to a human readable IPv4 address string. I came up with the following code so far:

void app_main() {
    uint8_t[] ip = {192, 168, 0, 99};
    dump_ip(ip);
}

void dump_ip(const uint8_t *in) {
    // ip addresses consist of three dots + terminator '\0'
    int size = 4;
    // count amount of chars needed for specific ip address
    for(int i=0; i<4; i++) {
        if(in[i]==0) {
            size ++;
        } else {
            size += ((int)log10(in[i]))+1;
        }
    }
    // allocate memory on heap for an ip address of length 'size'
    char *ip_str = (char*)malloc(size*sizeof(char));
    // copy ip address parts to char array
    int pos = 0;
    for(int i=0; i<4; i++) {
        if(in[i]==0) {
            ip_str[pos] = '0';
            pos++;
        } else {
            char b[4];
            itoa(in[i], b, 10);
            for(int j=0; j<3; j++) {
                if(b[j]!='0') {
                    ip_str[pos] = b[j];
                    pos++;
                }
            }
        }
        // add dot between ip address parts
        if(i<3) {
            ip_str[pos] = '.';
            pos++;
        }
    }
    // add terminator at end of string
    ip_str[pos] = '\0';
    // print to uart interface
    uart_send(ip_str);
    // release allocated heap memory
    free(ip_str);
}

I get the following results when testing with different IP arrays:

uint8_t[] ip = {192, 168, 0, 99};   =>   "192.168.0.99"   => OK
uint8_t[] ip = {192, 168, 1, 99};   =>   "192.168.1"      => FAILED
uint8_t[] ip = {192, 168, 10, 99};  =>   "192.168.1"      => FAILED
uint8_t[] ip = {10, 10, 10, 10};    =>   "1"              => FAILED
// etc. etc. etc. 

What am I doing wrong? Is there some more elegant way to accomplish this?



Solution 1:[1]

Just snprintf it.

void app_main() {
    uint8_t ip[] = {192, 168, 0, 99};
    char buf[3 * 4 + 3 * 1 + 1];
    snprintf(buf, sizeof(buf), "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
    uart_send(buf);
}

uint8_t[] ip is not valid C, the [] comes after the variable name.

  1. The sprintf and snprintf will print the formatted string into the memory pointed to by the first arg.
  2. The snprintf takes additional maximum size argument as the second arg.
  3. The uint8_t variables are auto-casted to int when passing in variable arguments list - thus you can print them using %d printf modifier.
  4. Then you have human readable, null terminated string.

Solution 2:[2]

The best answer is simply to use snprintf as suggested by Kamil Cuk.

However if you're still wondering why your code didn't work, the answer is on this line:

if(b[j]!='0') {

you are only copying characters that aren't zero, which includes null chars. So when a segment of the IP contains less than 3 characters you will be copying a null into the string. The fix is very simple:

if(b[j]!='\0') {

which makes me think it may well have just been a typo!

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 ach
Solution 2 lxop