V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
commoccoom
V2EX  ›  C

一段源码, Visual Studio 2019 上编译不成功, Linux 上可以

  •  
  •   commoccoom · 2021-03-04 10:02:20 +08:00 · 4391 次点击
    这是一个创建于 1389 天前的主题,其中的信息可能已经有所发展或是发生改变。
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    /* unsigned long to string */
    char * ultostr(unsigned long value, char * ptr, int base);
    
    int main(void)
    {
    	char var[] = "0A84010000000301";
    	unsigned long value = strtoul(var, NULL, 16);
        
        
    	printf("value is %lu\n\n", value);
    
        char * str;
        ultostr(value, str, 16);
    
        printf("Received '%s'\n\n", str);
    
    	return 0;
    }
    
    /* unsigned long to string */
    char* ultostr(unsigned long value, char* ptr, int base)
    {
        unsigned long t = 0, res = 0;
        unsigned long tmp = value;
        int count = 0;
    
        if (NULL == ptr)
        {
            return NULL;
        }
    
        if (tmp == 0)
        {
            count++;
        }
    
        while (tmp > 0)
        {
            tmp = tmp / base;
            count++;
        }
    
        ptr += count;
    
        *ptr = '\0';
    
        do
        {
            res = value - base * (t = value / base);
            if (res < 10)
            {
                *--ptr = '0' + res;
            }
            else if ((res >= 10) && (res < 16))
            {
                *--ptr = 'A' - 10 + res;
            }
        } while ((value = t) != 0);
    
        return(ptr);
    }
    

    VS2019 提示使用了未初始化的局部变量 str,但是 gcc 编译就没出问题,gcc 是 8.3.0

    这是为啥?

    第 1 条附言  ·  2021-03-04 11:41:16 +08:00

    把指针变量 str 稍微修改了一下,现在 VS2019 编译不会出错了,但是 Windows 平台和 Linux 平台的结果为什么会不一样

    结果

    # Linux下
    value is 757731736816714497
    
    Received 'A84010000000301'
    
    # Windows下
    value is 4294967295
    
    Received 'FFFFFFFF'
    

    源码

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    /* unsigned long to string */
    char * ultostr(unsigned long value, char * ptr, int base);
    
    int main(void)
    {    
        char var[] = "0A84010000000301";    
        unsigned long value = strtoul(var, NULL, 16);   
        
        printf("value is %lu\n\n", value);    
        
        int size = sizeof(var);
        char * str = malloc(size + 1);
    
        ultostr(value, str, 16);
    
        printf("Received '%s'\n\n",str);
    
        return 0;
    }
    
    /* unsigned long to string */
    char* ultostr(unsigned long value, char* ptr, int base)
    {
        unsigned long t = 0, res = 0;
        unsigned long tmp = value;
        int count = 0;
    
        if (NULL == ptr)
        {
            return NULL;
        }
    
        if (tmp == 0)
        {
            count++;
        }
    
        while (tmp > 0)
        {
            tmp = tmp / base;
            count++;
        }
    
        ptr += count;
    
        *ptr = '\0';
    
        do
        {
            res = value - base * (t = value / base);
            if (res < 10)
            {
                *--ptr = '0' + res;
            }
            else if ((res >= 10) && (res < 16))
            {
                *--ptr = 'A' - 10 + res;
            }
        } while ((value = t) != 0);
    
        return(ptr);
    }
    
    第 2 条附言  ·  2021-03-04 13:10:11 +08:00

    经 @ysc3839 指点,使用 unsigned long long 数据类型就可以使 Windows上与Linux上结果一致了。

    感谢各位的帮助!

    第 3 条附言  ·  2021-03-04 15:59:13 +08:00

    更正后

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    /* unsigned long long to string */
    char * ultostr(unsigned long long value, char * ptr, int base);
    
    int main(void)
    {    
        char var[] = "0A84010000000301";    
        unsigned long long value = strtoull(var, NULL, 16);   
        
        printf("value is %llu\n", value);    
        
        int size = sizeof(var);
        char * str = malloc(size);    
        ultostr(value, str, 16);
    
        printf("Received '%s'\n",str);
    
        free(str);
    
        return 0;
    }
    
    /* unsigned long long to string */
    char* ultostr(unsigned long long value, char * ptr, int base)
    {
        unsigned long long t = 0, res = 0;
        unsigned long long tmp = value;
        int count = 0;
    
        if (NULL == ptr)
        {
            return NULL;
        }
    
        if (tmp == 0)
        {
            count++;
        }
    
        while (tmp > 0)
        {
            tmp = tmp / base;
            count++;
        }
    
        ptr += count;
    
        *ptr = '\0';
    
        do
        {
            res = value - base * (t = value / base);
            if (res < 10)
            {
                *--ptr = '0' + res;
            }
            else if ((res >= 10) && (res < 16))
            {
                *--ptr = 'A' - 10 + res;
            }
        } while ((value = t) != 0);
    
        return(ptr);
    }
    
    29 条回复    2021-03-04 22:02:55 +08:00
    codehz
        1
    codehz  
       2021-03-04 10:06:06 +08:00 via Android   ❤️ 4
    好家伙,你这 str 准备存哪里,编译不炸运行时炸的节奏(没炸也是运气好)
    commoccoom
        2
    commoccoom  
    OP
       2021-03-04 10:07:38 +08:00
    @codehz 但是为啥 gcc 没报错😂
    venicejack
        3
    venicejack  
       2021-03-04 10:09:03 +08:00
    gcc 肯定是报 warning 了,编译成功不代表运行不 core
    rocksolid
        4
    rocksolid  
       2021-03-04 10:11:03 +08:00   ❤️ 2
    侧面印证了 Visual Studio 果然是宇宙第一 IDE
    aloxaf
        5
    aloxaf  
       2021-03-04 10:11:26 +08:00   ❤️ 2
    因为你没加 `-Wall -Wextra -Werror`,加了 gcc 就也会拒绝编译了(
    ReVanTis
        6
    ReVanTis  
       2021-03-04 10:17:39 +08:00   ❤️ 1
    关掉 sdl 检查或者去掉编译器的 /sdl 选项,msvc 也可以编译过
    ReVanTis
        7
    ReVanTis  
       2021-03-04 10:19:03 +08:00
    @ReVanTis

    https://stackoverflow.com/questions/18559425/how-to-ignore-uninitialized-variable-error-in-msvc

    用这个宏应该也可以
    #pragma warning (disable: 4703)

    在某些特殊场景下,不排除需要这么做的可能性
    IDAEngine
        8
    IDAEngine  
       2021-03-04 10:24:25 +08:00
    还是 VS 专业些
    commoccoom
        9
    commoccoom  
    OP
       2021-03-04 10:27:47 +08:00
    https://gist.githubusercontent.com/yloveyy/77bf89ff4b249e67185d99de81d3a1d7/raw/c923dba61b4962c66a3ee5faf60bfeefbd632a28

    修改了一下,加了
    int size = sizeof(var);
    char * str = malloc(size + 1);

    但是 Windows 平台和 Linux 平台运行的结果不一样,Linux 运行符合预期,但是 Windows 结果

    value is 4294967295

    Received 'FFFFFFFF'

    为啥。。。。
    pisc
        10
    pisc  
       2021-03-04 10:30:44 +08:00 via Android   ❤️ 3
    楼上都答不到点上来啊

    @commoccoom 因为这个程序本来就是合法的,没有错,但这么用很危险,所以 vs 默认把这种“警告级别”升级为了“编译错误”,因为本身程序是合法的,所以 GCC 编译成功是没问题的,但一般会有警告信息,GCC 也可以配置成
    xdeng
        11
    xdeng  
       2021-03-04 10:31:38 +08:00
    指针变量 除非你有十足的把握 否则应该给默认值 null
    aloxaf
        12
    aloxaf  
       2021-03-04 10:42:12 +08:00
    @commoccoom 盲猜你 Windows 是 32 位的(或者默认编译 32 位程序
    moonmagian
        13
    moonmagian  
       2021-03-04 10:47:19 +08:00 via iPhone   ❤️ 1
    局部变量的初始值是和 os 相关的随机值,gcc 编译能跑只是因为随机的地址恰好可读可写而且不会覆盖其他内存里的东西。即使偶尔能跑,多跑几次或者换台机器跑就不一定了...
    aloxaf
        14
    aloxaf  
       2021-03-04 10:48:26 +08:00   ❤️ 2
    newmlp
        15
    newmlp  
       2021-03-04 10:58:07 +08:00
    说明 gcc 就是辣鸡编译器
    yuruizhe
        16
    yuruizhe  
       2021-03-04 11:12:07 +08:00
    @newmlp 好家伙,Apache 沉默 GUN 流泪
    402124773
        17
    402124773  
       2021-03-04 11:47:19 +08:00
    @yuruizhe
    直呼好家伙就可以了
    iyezi
        18
    iyezi  
       2021-03-04 12:27:39 +08:00
    @commoccoom #9 可能 sizeof 求出来的是一个指针的 size 吧。你想写的是不是 strlen ?
    littlewing
        19
    littlewing  
       2021-03-04 12:38:54 +08:00 via iPhone
    说明 linux 辣鸡,windows 天下第一
    翻译一下就是 linus 辣鸡,印度啊三天下第一
    littlewing
        20
    littlewing  
       2021-03-04 12:41:02 +08:00 via iPhone
    @littlewing 对了,还得加上 GNU 辣鸡,所以 RMS 辣鸡
    ysc3839
        21
    ysc3839  
       2021-03-04 12:41:28 +08:00 via Android   ❤️ 2
    第一个问题是指针变量未初始化就使用。
    第二个问题是 unsigned long 在不同平台上长度不一致,建议改成 strtoull 。
    commoccoom
        22
    commoccoom  
    OP
       2021-03-04 12:43:08 +08:00
    @iyezi 这个对结果没有影响吧
    CismonX
        23
    CismonX  
       2021-03-04 12:45:04 +08:00   ❤️ 2
    用 gcc/clang 编译的时候,加参数 -Wall -Wextra -Wpedantic 是基操,加上以后可以看到如下警告:

    warning: variable 'str' is uninitialized when used here [-Wuninitialized]
    felixcode
        24
    felixcode  
       2021-03-04 13:01:06 +08:00 via Android
    就该把 Linux 内核放到 Visual Studio 编译
    fighte97
        25
    fighte97  
       2021-03-04 16:05:50 +08:00
    C#可以多用用 能搞懂很多 C/C++的坑
    jim9606
        26
    jim9606  
       2021-03-04 17:34:38 +08:00   ❤️ 1
    局部变量没有初始化的话初值不一定为 0,指针好像也是这样的,所以 char*还是习惯初始化为 NULL 或者 malloc 。

    如果需求定长 64 位的 int 类型,应该用 stdint.h 标头里的 uint64_t,在 printf/scanf 中用 inttypes.h 标头里的宏 PRIu64/SCNu64,如果是地址应该用 intptr_t/PRIiPTR/SCNiPTR
    Rrobinvip
        27
    Rrobinvip  
       2021-03-04 21:27:28 +08:00 via iPhone
    注意到你最后更正的地方还有一个致命问题,可能是因为要转换的 str 太短了没体现出来

    你用 `int size = sizeof(Val)`, 然后给 str malloc 这么多空间。事实上无论你的 val 指向的 str 到底有多大,这个 size 永远都是 8,因为你得到的是一个 pointer 的大小,而不是实际 string 的长度。

    我感觉你的想法是,看一下 val 这个 str 有多长,然后给 str malloc 这么多空间吧?

    所以你应该用 `size_t size=strlen(Val)+1` ,然后再 malloc 。
    ysc3839
        28
    ysc3839  
       2021-03-04 21:58:59 +08:00
    @Rrobinvip 这里的 char var[] 是数组,sizeof 得到的是数组长度而不是指针长度。

    #include <stdio.h>
    int main()
    {
    char var[] = "0A84010000000301";
    printf("%u", sizeof(var));
    }

    可以自行运行以上代码看看结果。
    Rrobinvip
        29
    Rrobinvip  
       2021-03-04 22:02:55 +08:00 via iPhone
    @ysc3839 学艺不精,受教了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   999 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 18:44 · PVG 02:44 · LAX 10:44 · JFK 13:44
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.