Bad behavior with printf and multiple strings

Go To StackoverFlow.com

2

When I use printf/printf_s with two strings, I get the same output for both %s vars.

IN_ADDR oldIP;
oldIP.S_un.S_addr = iptable[j]->ipAddress;
IN_ADDR newIP;
newIP.S_un.S_addr = adapterTbl->table[i].dwAddr;
printf_s("index %d old: %s new: %s", 
          adapterTbl->table[i].dwIndex, inet_ntoa(oldIP),
           inet_ntoa(newIP));

Output is:

index 11 old: 192.168.1.1 new: 192.168.1.1

Now, I have checked that the values of oldip and newip are different by breaking before the print statement, and I have also tried to make the following function and use it in the print statement (instead of inet_ntoa):

char *convertIP (DWORD ip)  
{  
    IN_ADDR *addr = new IN_ADDR;
    memset(addr, 0, sizeof(IN_ADDR));
    addr->S_un.S_addr = (u_long) ip;
    return inet_ntoa(*addr);
} 

The output with this was:

192.168.1.1
192.168.1.2
index 11 old: 192.168.1.1 new: 192.168.1.1

Why am I seeing this behavior, and how can I fix it?
Thanks :)

2012-04-03 20:03
by Michael S.


6

inet_ntoa retuns:

a statically allocated buffer, which subsequent calls overwrite

(This is from the Linux manpage.)

You can't use it twice in a function, only the output of the second call will be visible (and you don't know which one of the two calls is the second - order of evaluation of arguments to a function is unspecified).

Do two separate printfs, or copy the output of inet_ntoa to local buffers and output those.

From POSIX:

The return value of inet_ntoa() may point to static data that may be overwritten by subsequent calls to inet_ntoa().

So this behavior is probably not limited to Linux, and you can't rely on it not overwriting a static buffer.

2012-04-03 20:07
by Mat


1

For the reasons that Mat described, you must use the result of one call to inet_ntoa before performing the next call.

Here is one easy way to do that:

IN_ADDR oldIP;
oldIP.S_un.S_addr = iptable[j]->ipAddress;
IN_ADDR newIP;
newIP.S_un.S_addr = adapterTbl->table[i].dwAddr;
std::string s_old(inet_ntoa(oldIP));
std::string s_new(inet_ntoa(newIP));
printf_s("index %d old: %s new: %s", 
      adapterTbl->table[i].dwIndex, s_old.c_str(),
       s_new.c_str());

Note that the string constructor makes a copy of the passed-in C string. Thus, when inet_ntoa is invoked again, it is free to overwrite its previously-stored value.

2012-04-03 20:20
by Robᵩ