练习 3-4 在数的对二的补码表示中,我们编写的itoa函数不能处理最大的负数,即n等于-2^(字长-1)的情况。请解释其原因。修改该函数,使它在任何机器上运行时都能打印出正确的值。

在原文中的itoa函数为:

/* itoa: convert n to characters in s */
void itoa(int n, char s[])
{
int i, sign;
if ((sign = n) < 0) /* record sign */
n = -n; /* make n positive */
i = 0;
do { /* generate digits in reverse order */
s[i++] = n % 10 + '0'; /* get next digit */
} while ((n /= 10) > 0); /* delete it */
if (sign < 0)
s[i++] = '-';
s[i] = '\0';
reverse(s);
}

我们知道,最大负数的二进制形式是10000……,在上面的函数itoa中,对最大负数进行n=-n运算时,只改变其符号位,我们可以看到n将会变成0,这样会得到错误的结果,这种情况下,我们可以将这个最大的负数变为无符号数,即n=(unsigned)n,这样子的话,这个无符号数的绝对值等于原来的最大负数的绝对值。为了解决这个问题,我们需要在最开始的位置对最大的负数进行识别并处理,剩下的程序部分不需要改变,直接照搬就行。

判别最大负数的方法有很多,利用它的特殊性质就可以了。比如,一,如果是负数的话,进行减一处理,得到的差如果大于零,说明他就是最大负数;二将这个最大负数变为无符号数,然后进行左移运算,得到结果为0的话,说明这个数就是最大负数。

#include 
#include 

void itoa1(int n, char s[]);//这是改进后可以将最大负数正常输出的函数
void itoa2(int n, char s[]);//这是原文中的函数,在输出最大负数时会出现错误
void reverse(char s[]);

int main(){
    int n=INT_MIN;
    char s[100];
    itoa2(n,s);
    for(int i=0;s[i]!='\0';++i){
        printf("%c",s[i]);
    }
    printf("\n");
    n=INT_MIN;
    itoa1(n,s);
    for(int i=0;s[i]!='\0';++i){
        printf("%c",s[i]);
    }
    return 0;
}

void itoa1(int n, char s[])
{
int i=0;
int sign=n;
unsigned n_copy;
if ((sign=n)<0){
    if((n-1)>0){
        n_copy=n;
    }else{
        n_copy = -n; 
    }
}else{
    n_copy=n;
}
do { 
s[i++] = n_copy % 10 + '0'; 
} while ((n_copy /= 10) > 0);
if (sign < 0)
s[i++] = '-';
s[i] = '\0';
reverse(s);
}

void itoa2(int n, char s[])
{
int i, sign;
if ((sign = n) < 0) 
n = -n; 
i = 0;
do { 
s[i++] = n % 10 + '0'; 
} while ((n /= 10) > 0);
if (sign < 0)
s[i++] = '-';
s[i] = '\0';
reverse(s);
}

void reverse(char s[]){
    int i;
    for(i=0;s[i]!='\0';++i){
        ;
    }
    --i;
    for(int j=0;i>j;--i,++j){
        char temp=s[i];
        s[i]=s[j];
        s[j]=temp;
    }
}

执行结果如下图所示:
练习 3-4 在数的对二的补码表示中,我们编写的itoa函数不能处理最大的负数,即n等于-2^(字长-1)的情况。请解释其原因。修改该函数,使它在任何机器上运行时都能打印出正确的值。_第1张图片

你可能感兴趣的:(C语言学习习题)