天文数字下的bug--文件的大小的显示

原始的代码来自:【MoreWindows工作笔记5】StrFormatByteSize64 高端大气的显示文件大小

但是代码中有一个BUG,你看出什么问题来了吗?

// 【MoreWindows工作笔记5】StrFormatByteSize64 高端大气的显示文件大小
#include <stdio.h>
#include <math.h>
#include <shlwapi.h>
#pragma comment(lib, "shlwapi.lib")
void NormFileSize(LONGLONG file_length, char* buffer, int buffer_size) {
        static double BASE_NUMBER = 1024.0;
        static char* SIZE_NAME[] = {"B","KB", "MB", "GB", "TB", "PB"};
        double length = static_cast<double>(file_length);
        int i = 0;
        while (i < ARRAYSIZE(SIZE_NAME) && length > BASE_NUMBER) {
                length /= BASE_NUMBER;
                i++;
        }
        sprintf(buffer, "%lf %s", length, SIZE_NAME[i]);
}
int main()
{
        printf("   【MoreWindows工作笔记5】StrFormatByteSize64 高端大气的显示文件大小\n");    
        printf(" - http://blog.csdn.net/morewindows/article/details/16358955 -\n");    
        printf(" -- By MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");  

        LONGLONG file_length = 34578;

        for (int i = 0; i < 10; i++)
        {
                printf("字节数 = %15I64d -- 文件大小:", file_length);
                char file_length_str1[100] = {0};
                NormFileSize(file_length, file_length_str1, 100);
                printf("%15s   ", file_length_str1);
                WCHAR file_length_str2[100] = {0};
                StrFormatByteSizeW(file_length, file_length_str2, 100);  // StrFormatByteSizeW会截断尾数
                printf("%ls\n", file_length_str2); // 还可以试试StrFormatKBSize,这个只用KB来表示
                file_length *= 11;
        }

        return 0;
}


看来只有在极端情况下才会出现
现在我们就来再现问题
首先有个疑问: LONGLONG 能表示的最大值是多少?
是9223372036854775807(具体看http://blog.csdn.net/wisepragma/article/details/23882069)
改动一下代码
将static char* SIZE_NAME[] = {"B","KB", "MB", "GB", "TB", "PB"}; 的static去掉

将9223372036854775807代入函数NormFileSize()


[文件为testprintf.cpp]

#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <stdio.h>
#include <math.h>
#include <shlwapi.h>
#pragma comment(lib, "shlwapi.lib")
void NormFileSize(LONGLONG file_length, char* buffer, int buffer_size) {
        static double BASE_NUMBER = 1024.0;
        char* SIZE_NAME[] = {"B","KB", "MB", "GB", "TB", "PB"};
        double length = static_cast<double>(file_length);
        int i = 0;
        while (i < ARRAYSIZE(SIZE_NAME) && length > BASE_NUMBER) {
                length /= BASE_NUMBER;
                i++;
        }
        sprintf(buffer, "%lf %s", length, SIZE_NAME[i]);
}
int main()
{
        printf("   【MoreWindows工作笔记5】StrFormatByteSize64 高端大气的显示文件大小\n");    
        printf(" - http://blog.csdn.net/morewindows/article/details/16358955 -\n");    
        printf(" -- By MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");  

        LONGLONG file_length = 34578;
        char file_length_str1[100] = {0};
        WCHAR file_length_str2[100] = {0};

        //for (int i = 0; i < 10; i++)
        //{
        //        printf("字节数 = %15I64d -- 文件大小:", file_length);
        //        NormFileSize(file_length, file_length_str1, 100);
        //        printf("%15s   ", file_length_str1);
        //        StrFormatByteSizeW(file_length, file_length_str2, 100);  // StrFormatByteSizeW会截断尾数
        //        printf("%ls\n", file_length_str2); // 还可以试试StrFormatKBSize,这个只用KB来表示
        //        file_length *= 11;
        //}
        file_length=9223372036854775807;
        NormFileSize(file_length, file_length_str1, 100);

        return 0;
}

运行后就会出现




接着我们要来抓鬼--为什么会出现这个问题呢?
在下面所示的这个循环内,i值一直都是合法的,而且语义上完全没有问题,

        while (i < ARRAYSIZE(SIZE_NAME) && length > BASE_NUMBER) 
       {
                length /= BASE_NUMBER;
                i++;
       }

但是循环下面的代码引用了这个变量,一般情况下,不会有问题,然后有人在问---我们一定要把没问题的东西测出问题来吗?但是如果在极端情况下,比如代入很大的值,于是退出循环后i==ARRAYSIZE(SIZE_NAME),我们知道,C++的数组const int MAX_INDEX=1000;int array[MAX_INDEX]下标范围是0~MAX_INDEX-1,元素为array[0],array[1],...array[MAX_INDEX-1],使用array[MAX_INDEX]就是越界!!!解决办法就是在越界前结束循环:

        while (i < ARRAYSIZE(SIZE_NAME)-1 && length > BASE_NUMBER) 
       {
                length /= BASE_NUMBER;
                i++;
       }

附上完整函数进化版本
#include <windows.h>
#include <stdio.h>
#include <tchar.h>




TCHAR* Bytes2StringEx(const ULARGE_INTEGER ullSizeInBytes,TCHAR *lpszSizeInfo)//StrFormatByteSize64在ANSI工作不正常
{          
	//from:MoreWindows <<StrFormatByteSize64 高端大气的显示文件大小>>
	//bytes
	//bytes/1024 kB
	//bytes/1024/1024 MB
	//bytes/1024/1024/1024 GB
	//bytes/1024/1024/1024/1024 TB         

	long double FileSize= static_cast<long double>(ullSizeInBytes.QuadPart);
	long double BASE_NUMBER = 1024.0;
	TCHAR *UnitName[] = {TEXT("B"),TEXT("kB"), TEXT("MB"),TEXT("GB"), TEXT("TB"),TEXT("PB")};
	int i = 0;
        int max_index=sizeof(UnitName)/sizeof(UnitName[0]) ; //int max_index2=ARRAYSIZE(UnitName);
       
	while (   i<max_index-1  &&    FileSize>1024.0  ) 
	{
		FileSize /= 1024.0;
		i++;
	}
        TCHAR *un=UnitName[i];//虽然上面的while中i不会超出max_index但是,当超出时,下面引用到的正是超出的这个下标
	#pragma warning(disable:4996)
	_sntprintf(lpszSizeInfo,10230, TEXT("%lf %s"),FileSize, un);                
	return lpszSizeInfo;
}     


TCHAR* Bytes2StringEx2(const ULARGE_INTEGER ullSizeInBytes,TCHAR *lpszSizeInfo)//StrFormatByteSize64在ANSI工作不正常
{          
        //from:MoreWindows <<StrFormatByteSize64 高端大气的显示文件大小>>
        // http://blog.csdn.net/morewindows/article/details/16358955#comments
        // ulx.QuadPart=14757395258967641292;//代入这个天文数字,然后已经不是什么PB
        //bytes
        //bytes/1024 kB
        //bytes/1024/1024 MB
        //bytes/1024/1024/1024 GB
        //bytes/1024/1024/1024/1024 TB         

        long double FileSize= static_cast<long double>(ullSizeInBytes.QuadPart);
        TCHAR *StorageUnit[] = {TEXT("B"),TEXT("kB"), TEXT("MB"),TEXT("GB"),TEXT("TB"),TEXT("PB"),TEXT("EB"),TEXT("ZB"),TEXT("YB"),TEXT("BB"),TEXT("NB"),TEXT("DB")};
        const int UNIT_MAX_INDEX=sizeof(StorageUnit)/sizeof(StorageUnit[0]);
        const long double BASE_NUMBER = 1024.0;

        int i = 0;
        while (   i<UNIT_MAX_INDEX-1   &&    FileSize>BASE_NUMBER  )//bug fixed:虽然上面的while中i不会超出UNIT_MAX_INDEX但是,当超出时,下面引用到的正是超出的这个下标所以要减一:i<UNIT_MAX_INDEX-1 

        {
                FileSize /=BASE_NUMBER;
                i++;
        }
        #pragma warning(disable:4996)
        _stprintf(lpszSizeInfo,TEXT("%lf%s"),FileSize, StorageUnit[i]);                
        return lpszSizeInfo;
}      


int _tmain()
{
        ULARGE_INTEGER  ulx;   
        ulx.QuadPart=-1;//(unsigned long long)-1==18446744073709551615,0xcccccccccccccccc=14757395258967641292;//代入这个天文数字,然后已经不是什么PB
	TCHAR lpszSizeInfo[20];
        _tprintf(TEXT("%I64u bytes"),ulx.QuadPart );
	Bytes2StringEx(ulx,lpszSizeInfo);
        _tprintf(TEXT("=%s "),lpszSizeInfo);
        Bytes2StringEx2(ulx,lpszSizeInfo);
        _tprintf(TEXT("=%s\n"),lpszSizeInfo);
        getchar();//pause 
	return 0;
}


转载于:https://my.oschina.net/u/160145/blog/264396

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值