练习2-1:
编写一个程序以确定分别由signed及unsigned限定的char、short、int及long类型变量的取值范围。采用打印标准头文件中的相应值以及直接计算两种方式实现:
采用打印标准头文件中的相应值:
#include <stdio.h>
#include <limits.h>
//determine ranges of types
int main()
{
printf("采用打印标准头文件limits.h中的相应值:\n");
//signed types
printf("signed char min = %d\n", SCHAR_MIN);
printf("signed char max = %d\n", SCHAR_MAX);
printf("signed short min = %d\n", SHRT_MIN);
printf("signed short max = %d\n", SHRT_MAX);
printf("signed int min = %d\n", INT_MIN);
printf("signed int max = %d\n", INT_MAX);
printf("signed long min = %ld\n", LONG_MIN);
printf("signed long max = %ld\n", LONG_MAX);
//unsigned types
printf("unsigned char max = %u\n", UCHAR_MAX);
printf("unsigned short max = %u\n", USHRT_MAX);
printf("unsigned int max = %u\n", UINT_MAX);
printf("unsigned long max = %lu\n", ULONG_MAX);
system("pause");
return 0;
}
这种实现方法没什么好讨论的,只要知道相应的头文件和变量名就可以了。
直接计算:
#include <stdio.h> //determine ranges of types int main() { printf("采用直接计算的方式:\n"); //signed types printf("signed char min = %d\n", -(char)((unsigned char)~0>>1)-1); printf("signed char max = %d\n", (char)((unsigned char)~0>>1)); printf("signed short min = %d\n", -(short)((unsigned short)~0>>1)-1); printf("signed short max = %d\n", (short)((unsigned short)~0>>1)); printf("signed int min = %d\n", -(int)((unsigned int)~0>>1)-1); printf("signed int max = %d\n", (int)((unsigned int)~0>>1)); printf("signed long min = %ld\n", -(long)((unsigned long)~0>>1)-1); printf("signed long max = %ld\n", (long)((unsigned long)~0>>1)); //unsigned types printf("unsigned char max = %u\n", (unsigned char)~0); printf("unsigned short max = %u\n", (unsigned short)~0); printf("unsigned int max = %u\n", (unsigned int)~0); printf("unsigned long max = %lu\n", (unsigned long)~0); system("pause"); return 0; }
小结:
利用按位运算符进行计算。表达式:(char)((unsigned char)~0>>1)
先把数字0的各个二进制位全部转换为1:取反,~0
然后,将结果值转换为unsigned char类型:(unsigned char)~0
再把这个unsigned char类型值右移一位以清除符号位:(unsigned char)~0 >> 1
最后,把它转换为char类型:(char)((unsigned char)~0>>1)
这一系列操作的最终结果就得到了signed类型字符的最大值。
而其signed类型字符的最小值加负号后还需减去1才是最小值。(K&R书的中文习题解答此处并没有减去1)
练习2-2
在不使用&&或||的条件下编写一个与下面的for循环语句等价的循环语句
for(i = 0;i < lim - 1 && (c = getchar()) != '\n' && c != EOF;++i)
与之等价的循环语句:
enum loop {NO,YES}; enum loop okloop = YES; i = 0; while(okloop == YES) if (i >= lim - 1) okloop = NO; else if((c = getchar()) = '\n') okloop = NO; else if(c == EOF) okloop = NO; else{ s[i] = c; ++i; }
练习2-3
编写函数htoi(s),把由十六进制数字组成的字符串(包含可选的前缀0x或0X)转换为与之等价的整数值。
#include <stdio.h> #define YES 1 #define NO 0 int hoti(char s[]); main() { int c,i; char s[10]; i = 0; while ((c = getchar()) != '\n' ) s[i++] = c; printf("the number is %d\n", hoti(s)); getch(); } /* convert hexdecimal strings to intrger */ int hoti(char s[]) { int hexdigit, i, inhex, n; i = 0; if(s[i] == '0'){ if(s[++i] == 'x' || s[i] == 'X') i++; } n = 0; inhex = YES; while(inhex == YES){ if(s[i] >= '0' && s[i] <= '9') hexdigit = s[i] - '0'; else if (s[i] >= 'a' && s[i] <= 'f') hexdigit = s[i] - 'a' + 10; else if (s[i] >= 'A' && s[i] <= 'F') hexdigit = s[i] - 'A' + 10; else inhex = NO; if(inhex == YES) n = n * 16 + hexdigit; i++; } return n; }在网上看到一段大牛写的利用移位实现本题要求功能的代码,看不太懂先贴在下面。据说移位是c语言的精华所在,善于利用移位可以解决很多问题。只是我目前还没有这样的体会,可能是见识的太少了。
=============使用移位的思想================
BOOL HexToDec( LPCTSTR shex,int& idec )
{
int i,mid;
int len = lstrlen( shex );
if( len>8 ) return FALSE;
mid = 0; idec = 0;
for( i=0;i<len;i++ )
{
if( shex[i]>='0'&&shex[i]<='9' ) mid = shex[i]-'0';
else if( shex[i]>='a'&&shex[i]<='f' ) mid = shex[i] -'a' +10;
else if( shex[i]>='A'&&shex[i]<='F' ) mid = shex[i] -'A' +10;
else return FALSE;
mid <<= ((len-i-1)<<2); //最精妙的地方,需要好好琢磨。
idec |= mid;
}
return TRUE;
}
练习2-6
编写一个函数setbits(x,p,n,y),该函数返回对x执行些列操作后的结果值:将x中从第p位开始(向右!)的n个位设置为y中最右边的n位的值,x的其余各位保持不变。
#include <stdio.h> #include <stdlib.h> unsigned setbits(unsigned ,int,int,unsigned); main() { unsigned x = 0331; unsigned y = 0251; int p = 5,n = 4; printf("The convert number 0f %o is %o.",x,setbits(x,p,n,y)); system("pause"); } /*setbits:set n nits of x at position p with bits of y */ unsigned setbits(unsigned x,int p, int n, unsigned y) { return x & ~(~(~0 << n) << (p + 1 - n)) | (y & ~(~0 << n)) << (p + 1 - n); }看到一个递归实现2进制输出的小程序如下:
void digui(int x)
{
int j;
j=x%2;
x=x/2;
if(x>0)
digui(x);
printf("%d",j);
}
按位运算符的运用:
C语言中提供了6个位操作符。这些运算符只能作用于整型操作数,即只能作用于带符号或无符号的char、short、int与long类型:
& | ^ >> << ~
1.按位与的运用:&
提取特定位、清零其余位:
例如:mask中要保留的位上为1,其他位为0,a=a0&mask
判断int的奇偶(效率比%2高得多):
例如:(a&1)==0则为偶数,反之为奇数。(原理:因为奇数二进制末位总为1,偶数总为0。原数与00…001进行按位与运算,就得到了a二进制末位的值。)
2.按位异或运用:^
特定位取反
例如:mask中要取反的位为1,其余为0,a=a0^mask
不用中间变量交换两数的值
例如:要交换a、b的值只需:a=a^b;b=a^b;a=a^b;即可。
(原理:上式即a=(a^b)^(a^b)^b,b=a^b^b。由于一个数与它本身进行“按位异或”运算得到0,任何一个数与0进行“按位异或”运算得到它本身,故上式即是:a=[b的原值],b=[a的原值]。这样就达到了交换a、b的目的。)
将x中从p位开始的n个位求反,x的其余各位保持不变。
事实上,只要用对应位为1,其余位为0 的屏蔽码与x进行异或操作即可实现。与1异或取反,与0异或不变。
练习2-8
编写函数rightrot实现循环右移。
/* rightrot: rotate x to the right by n positions */ unsigned rightrot(unsigned x,int n) { int wordlength(void); int rbit; /* rightmost bit */ while (n-- > 0){ rbit = (x & 1) << (wordlength() - 1); x = x >> 1; x = x | rbit; } return x; }
函数wordlength()的作用是计算运行程序的计算机所用的字长
int wordlength(void) { int i; unsigned v = (unsigned) ~0; for(i = 0;(v = v >> 1) > 0; i++); return i; }K&R的书中文习题解答中给出的另外一种无循环的版本如下:
unsigned rightrot(unsigned x, int n) { int wordlength(void); unsigned rbits; if ((n = n % wordlength()) > 0){ rbits = ~(~0 << n) & x; rbits = rbits << (wordlength() - n); x = x>> n; x = x | rbits; } return x; }
练习 2-9
利用x &= (x - 1)可以删除x中的最右边为1 的一个二进制位,重写bitcount函数,计算x中1的个数。
/* bitcount: count 1 bits in x -fast version */ int bitcount(unsigned x) { int b; for (b = 0; x != 0; x &= x - 1) //for(b = 0;x != 0;x >>= 1) b++; //if(x & 01) b++; old version return b; }