对'\0'的敬畏——由阶乘想到的

来源:互联网 发布:安卓 安装 linux 编辑:程序博客网 时间:2024/05/05 13:04

背景:

验证DSPLink,正在艰难入门TI的DaVinci平台。

老大给的任务是验证DSPLink,一开始不理解,看TI的文档也没什么感觉,后来说计算CRC也可以,那时对CRC还没了解(不会应用于代码中),干脆搞个阶乘得了。

于是上网找了个大数阶乘的例子,修改一下。代码如下:

char result[128] = {0};// num <= 80void factorial(int num){    char fac[128] = {0};    int i, j;    int len = 1;    int tmp;    int carry;     fac[0] = 1;    if (num == 0 || num == 1)        goto ret;    for (i = 2; i <= num; i++)    {        carry = 0;        for (j = 1; j <= len; j++)        {            tmp = fac[j - 1] * i + carry;            fac[j - 1] = tmp % 10;            carry = tmp / 10;            if (j == len && carry != 0)                len++;        }    }ret:    for (i = len; i >= 1; i--)    {        fac[i - 1] += '0';    }    for (i = 0; i < len; i++)    {        result[i] = fac[len - i - 1];    }    result[len] = '\0'; // 手动添加'\0'}
因为分配的缓冲区是以128字节为单位的,这里也指定数组大小为128,因此,传入的参数就不能太大,像100的阶乘,都差不多160位了,太大了。

刚开始没有手动添加'\0',因为觉得一开始已经初始化为0了,有效位数也是0,——在PC上测试时的确是这样的。但在其它场合却不一样。

举一个例子,先计算50!,将结果打印出来:

aa: 3041409320171337804361260816606476884437764156896051200000000000033 30 34 31 34 30 39 33 32 30 31 37 31 33 33 37 38 30 34 33 36 31 32 36 30 38 31 36 36 30 36 34 37 36 38 38 34 34 33 37 37 36 34 31 35 36 38 39 36 30 35 31 32 30 30 30 30 30 30 30 30 30 30 30 30 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 40 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
这是正确的。

接着计算10!:

aa: 3628800320171337804361260816606476884437764156896051200000000000033 36 32 38 38 30 30 33 32 30 31 37 31 33 33 37 38 30 34 33 36 31 32 36 30 38 31 36 36 30 36 34 37 36 38 38 34 34 33 37 37 36 34 31 35 36 38 39 36 30 35 31 32 30 30 30 30 30 30 30 30 30 30 30 30 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 40 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
这就不正确的。怎么10!还是这么大?
原来,10的阶乘是3628800,前面的确是10的阶乘,但却与原来计算的结果连在一起显示了。上面的函数是将计算结果放到数组中,而字符串的结束标志是'\0',程序在并没有找到位于362800后面的那一位'\0',而是找到了原来字符串的结束符。从打印的十六进制的结果来看,原来占用内存的内容没有被删除,因为还没有操作到这些内存,原来是什么就是什么。

再看一下手动添加'\0'的情形,同样计算10!,结果如下:

aa: 362880033 36 32 38 38 30 30 0 32 30 31 37 31 33 33 37 38 30 34 33 36 31 32 36 30 38 31 36 36 30 36 34 37 36 38 38 34 34 33 37 37 36 34 31 35 36 38 39 36 30 35 31 32 30 30 30 30 30 30 30 30 30 30 30 30 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 40 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
可以看到,输出的结果正确了,但十六进制打印的结果显示,没有修改到的内存内容依然不变,还是原来的值。

所以说,有时候,额外做一些看似多余的工作还是有很大作用的。或许这个错误是自己学识不够造成的,有了这个教训,以后就有印象了。


作为写代码的工人,不得不对内存十分了解及理解。

这里也顺便说一下指针的操作。

下面是原来的代码:

for (i = 0; i < info->bufferSize; i++) {                     *buffer++ = result[i];                }
贪方便,直接用++操作,但后面的发送函数也是以buffer为指针的,而由于++的操作,buffer已经不再是原来的buffer了。当初一直得不到正常的结果,原来收到的都是正常结果后面的那些内存数据,结果不正常倒是很正常的。

后面修改如下:

for (i = 0; i < info->bufferSize; i++) {                    *(buffer+i) = result[i];                }

以后对++又有些深刻的认识了。


后记:

在gpp端的代码,已经将这片内存初始化为'\0'了,而且dsp端也初始化result数组为'\0'了,按理说,每次运行程序,没有用到的内存应该是0才对,但实际情况并非如此。只能说,对于字符串,还是手动添加'\0'比较安全可靠。

原创粉丝点击