练习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)
{
}
按位运算符的运用:
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;
}