来源于项目中一个真实的bug,最近项目中要适配32位linux系统时发现一段检测磁盘剩余空间的程序,低于一定空间的话退出安装程序安装失败.
代码简化如下:
参考如下代码:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/vfs.h>
#include <errno.h>
#include <stdint.h>
#include <limits.h>

int main()
{
    struct statfs diskInfo;
    /*调试时指定一个存在的目录*/
    char *pszInstallDir="/root";

    memset(&diskInfo, 0, sizeof(struct statfs));
    /*1500MB*/
    uint64_t uByteSize = 1500*1024*1024;

    if (statfs(pszInstallDir, &diskInfo) != 0) {
        return -1;
    }

    long m = diskInfo.f_bfree * diskInfo.f_bsize;

    printf("m = %ld  LONG_MAX = %ld\n",m,LONG_MAX);

    printf("%ld  %ld\n", diskInfo.f_bfree , diskInfo.f_bsize);

    if ((diskInfo.f_bfree * diskInfo.f_bsize) < uByteSize)
    {
        errno = ENOSPC;
        printf("check disk free space (%s) failed with error code = %d",
                 pszInstallDir,
                 errno);
        return -1;
    }

    return 0;
}

执行结果:

[root@localhost ~]# ./statfs 
m = 119513088  LONG_MAX = 2147483647
12612090  4096

明显可以看出long最大为2147483647,实际结果为51659120640,超过最大值,发生溢出,因此在下面的代码中可以将结构体diskInfo中的字段保存在uint64_t中进行计算,可以确保在合理值范围内不会发生溢出。

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/vfs.h>
#include <errno.h>
#include <stdint.h>
#include <limits.h>

int main()
{
    struct statfs diskInfo;
    /*调试时指定一个存在的目录*/
    char *pszInstallDir="/root";

    memset(&diskInfo, 0, sizeof(struct statfs));
    /*1500MB*/
    uint64_t uByteSize = 1500*1024*1024;

    if (statfs(pszInstallDir, &diskInfo) != 0) {
        return -1;
    }

    long m = diskInfo.f_bfree * diskInfo.f_bsize;

    printf("m = %ld  LONG_MAX = %ld\n",m,LONG_MAX);

    uint64_t f_bfree = diskInfo.f_bfree;

    uint64_t f_bsize = diskInfo.f_bsize;
    
    uint64_t totalfree = f_bfree * f_bsize;

    printf("correct = %llu\n",totalfree);

    if (totalfree < uByteSize)
    {
        errno = ENOSPC;
        printf("check disk free space (%s) failed with error code = %d",
                 pszInstallDir,
                 errno);
        return -1;
    }

    return 0;
}

执行结果:

[root@localhost ~]# ./statfs 
m = 119513088  LONG_MAX = 2147483647
correct = 51659120640

以上程序中结构体diskInfo中的f_bfree和f_bsize均位long类型,在32位系统中long为int,我们可以在头文件中看到32位系统中long类型能表示的最大值为2147483647,当我们的计算结果超过这个值时将发生溢出,结果不可信。
系统库目录 /usr/include/limits.h

/* Minimum and maximum values a `signed long int' can hold.  */
#  if __WORDSIZE == 64
#   define LONG_MAX 9223372036854775807L
#  else
#   define LONG_MAX 2147483647L
#  endif
#  define LONG_MIN  (-LONG_MAX - 1L)

我们可以使用uint64_t可以避免溢出,参考/usr/include/stdint.h

/* Unsigned.  */
typedef unsigned char       uint8_t;
typedef unsigned short int  uint16_t;
#ifndef __uint32_t_defined
typedef unsigned int        uint32_t;
# define __uint32_t_defined
#endif
#if __WORDSIZE == 64
typedef unsigned long int   uint64_t;
#else
__extension__
typedef unsigned long long int  uint64_t;
#endif

__extension__是gcc对标准c语言的扩展,使用这些扩展功能时,编译器会发提出警告,使用__extension__关键字会告诉gcc不要提出警告.使用stdint.h中类型可以增强程序的可移植性。
需要在异构平台上传递的结构,不直接使用随平台不一样而导致长度不一的数据类型,主要指:long,unsigned long;可使用在不同平台保持长度一致的各种类型别名,如:int32_t/int64_t,U32/I32/U64/I64,WORD/DWORD等。