与sprintf一起使用的格式说明符%u正在创建崩溃

时间:2015-07-21 05:22:56

标签: c format-specifiers

当我在%u应用程序中使用sprintf()时崩溃,它可以正常使用%d

参见代码:

#include <stdio.h>
#include <string.h>
main()
{
    unsigned char dAddr[4];
    unsigned char sMask[4];
    unsigned char nHop[4];
    memset(dAddr,0,sizeof(dAddr));
    memset(sMask,0,sizeof(sMask));
    memset(nHop,0,sizeof(nHop));
    unsigned int u4IpDAddr = 0x01020304;
    unsigned int u4IpSNetMask = 0xffff01ff;
    unsigned int u4NHopGt = 0x01020304;
    char *dip = (char *)&u4IpDAddr;
    char *smk = (char *)&u4IpSNetMask;
    char *nhp = (char *)&u4NHopGt;
    sprintf(dAddr, "%u.%u.%u.%u", dip[3], dip[2], dip[1], dip[0]);  //if I used %d.%d.%d.%d its working fine
    sprintf(sMask, "%u.%u.%u.%u", smk[3], smk[2], smk[1], smk[0]); //if I used %d.%d.%d.%d its working fine
    sprintf(nHop, "%u.%u.%u.%u", nhp[3], nhp[2], nhp[1], nhp[0]); //if I used %d.%d.%d.%d its working fine
    printf("SAM: func %s line %d IpDAddr %s Mask %s NHop %s\n",__func__,__LINE__,dAddr,sMask,nHop);
}

当我用以下方式声明一个指针时,%u.%u.%u.%u格式的工作正常

unsigned char *dip = (unsigned char *)&u4IpDAddr;
unsigned char *smk = (unsigned char *)&u4IpSNetMask;
unsigned char *nhp = (unsigned char *)&u4NHopGt;

当我使用char指针时,任何人都可以解释发生了什么吗?

2 个答案:

答案 0 :(得分:9)

在你的情况下

unsigned char dAddr[4];
unsigned char sMask[4];
unsigned char nHop[4];

不足以保存词典输出。

当您在sprintf()中使用这些数组作为目标字符串时,基本上,您将超出分配的内存,创建undefined behaviour

您需要分配更多内存才能将这些数组用作sprintf()的目标。

答案 1 :(得分:1)

如果在您的平台上签署了char,那么char就会重视您已经解决的问题&#34;您的u4IpSNetMask很可能是否定的,因为您在0xF...中的字节以u4IpSNetMask开头。当您将此类char值发送至sprintf时,它们会转换为负int值,然后由unsigned说明符重新解释为%u值。该行为实际上是未定义的 - sprintf int%u值是违法的。但是,在实践中,您通常会得到一个巨大的正值,需要很多字符来表示。这些表示很容易溢出目标缓冲区,破坏程序堆栈并导致程序崩溃。

您可以自己查看sprintf来电在典型平台上生成的内容:http://coliru.stacked-crooked.com/a/6aec03cfdf28f8b2

中间sprintf生成4294967295.4294967295.1.4294967295。你期望这个怪物适合只有4个字符的缓冲区sMask吗?

此外,%d也会发生同样的溢出,但%d生成的字符串表示更短(-1代替4294967295)损坏程度较小,这可能就是为什么程序会在没有崩溃的情况下蹒跚而行。但这并没有改变字符串1.2.3.4需要一个至少8个字符长的字符缓冲区这一事实。你只提供了4个。

换句话说,您的计划已被%d打破,因为它已被%u打破。如果它没有与%d崩溃,那只是随机运气。