K&R 第二章 类型、运算符与表达式
2-1 编写一个程序以确定分别由signed及unsigned限定的char、short、int与long类型变量的取值范围。采用打印标准头文件中的相应值以及直接计算两种方式实现。后一种方法的实现较困难一些,因为要确定各种浮点类型的取值范围。
#include <stdio.h>
#include <limits.h>
//第一种方法
int main()
{
printf("Size of Char %d\n", CHAR_BIT);
printf("Size of Char Max %d\n", CHAR_MAX);
printf("Size of Char Min %d\n", CHAR_MIN);
printf("Size of int min %d\n", INT_MIN);
printf("Size of int max %d\n", INT_MAX);
printf("Size of long min %ld\n", LONG_MIN); /* RB */
printf("Size of long max %ld\n", LONG_MAX); /* RB */
printf("Size of short min %d\n", SHRT_MIN);
printf("Size of short max %d\n", SHRT_MAX);
printf("Size of unsigned char %u\n", UCHAR_MAX); /* SF */
printf("Size of unsigned long %lu\n", ULONG_MAX); /* RB */
printf("Size of unsigned int %u\n", UINT_MAX); /* RB */
printf("Size of unsigned short %u\n", USHRT_MAX); /* SF */
return 0;
}
//第二种方法
int main()
{
printf("Size of Char Max %d\n", (char)((unsigned char)~0 >> 1));
printf("Size of Char Min %d\n", -((char) ((unsigned char)~0>>1)+1));
}
2-2 在不使用运算符&&和||的条件下编写一个与上面(P.32)for循环语句等价的循环语句。分析:分析该for语句含义(执行步骤)用关系运算符实现(等价 - C语句含义层面)
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),把由16进制数字组成的字符串(包含可选的前缀0x或0X)转换为与之等价的整型值。字符串允许包含的数字包括:0 ~ 9、a ~ f以及A ~ F。
#define YES 1
#define NO 0
int htoi(char s[]){
int hexdigit,i,inhex,n;
i=0;
if(s[i]=='0'){ //跳过前缀0X或0X
++i;
if(s[i]=='X'||s[i]=='x')
++i;
}
n=0;
inhex=YES; //假定是个有效的十六进制数
for(;inhex==YES;++i){
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=16*n+hexdigit;
}
return n;
}
//教材内容代码
//atoi:convert s to integer
int atoi(char s[])
{
int i,n;
i=n=0;
while(s[i]>='0'&&s[i]<='9'){
n=10*n+(s[i]-'0');
i++;
}
return n;
}
//convert c tolower case
int lower(int c)
{
if(c>='A'&&c<='Z')
return c+'a'-'A';
else
return c;
}
//删除字符串s中出现的字符c
void squeeze(char s[],int c)
{
int i,j;
for(i=j=0;s[i]!='\0';i++){
if(s[i] !=c)
s[j++]=s[i];
}
s[j]='\0';
}
//将字符串t连接到字符串s的尾部
void strcat(char s[],char t[])
{
int i,j;
i=j=0;
while(s[i]!='\0')
i++;
while(t[j] !='\0')
s[i++]=t[j++];
s[i]='\0';
}
2-4 重新编写squeeze(s1, s2)[P.37],将字符串s1中任何与字符串s2中字符匹配的字符都删除。
void squeeze(char s1[],char s2[])
{
int i,j,k;
i=j=0;
while(s2[j]!='\0'){
while(s1[i]!='\0'){
if(s1[i]==s2[j]){
k=i;
while(s1[k]!='\0'){ //删除字符,前移
s1[k]=s1[k+1];
k++;
}
s1[k]='\0';
i--; //防止重复值
}
i++;
}//s1遍历完成
i=0;
j++;
}
}
void squeeze_1(char s1[],char s2[])
{
int i,j,k;
for(i=k=0;s1[i]!='\0';i++){
for(j=0;s2[j]!='\0'&&s2[j]!=s1[i];j++)
;
if(s2[j]=='\0') //s2中没有与是s[i]匹配的字符
s1[k++]=s1[i]; //s[i]复制到结果字符串
}
s1[k]='\0';
}
2-5 编写函数any(s1, s2),将字符串s2中任一字符在字符串s1中第一次出现的位置作为结果返回。如果s1中不包含s2中的字符,则返回-1。(标准库函数strpbrk具有同样的功能,但它返回的是指向该位置的指针)。
int any(char s1[],char s2[])
{
int i,j;
for(i=j=0;s1[i]!='\0';i++){
for(j=0;s2[j]!='\0';j++)
if(s1[i]==s2[j])
return i;
}
return -1;
}
//返回x中从右边数第p位开始向右数n位的字段。假设最右边的一位是第0位
unsigned getbits(unsigned x,int p,int n)
{
return (x>>(p-n+1)) & ~(~0<<n);
}
2-6 编写一个函数setbits(x, p, n, y),该函数返回对x执行下列操作后的结果值:将x中从第p位开始的n个(二进制)位设置为y中最右边n位的值,x的其余各位保持不变。
//169:1010 1001 7:0000 0111 189:101 111 01
unsigned setbits(unsigned x,int p,int n,unsigned y)
{
return x& ~ (~(~0<< n) << (p+1-n)) | (y & ~(~0 << n)) <<(p+1-n);
}
2-7 编写一个函数invert(x, p, n),该函数返回对x执行下列操作后的结果值:将x中从第p位开始的n个(二进制)位求反(即,1变成0,0变成1),x的其余各位保持不变。
//0^a=a 1^a=~a;
unsigned invert(unsigned x,int p,int n)
{
return x^ (~(~0<<n) <<(p+1-n));
}
unsigned invert_1(unsigned x,int p,int n)
{
setbits(x,p,n,~x>>(p-n+1));
}
2-8 编写一个函数rightrot(x, n),该函数返回将x循环右移(即从最右端移出的位将从最左端移入)n(二进制)位后所得到的的值。
unsigned rightot_1(unsigned x,int n)
{
unsigned y=x&( ~(~0<<n) );
return setbits(x>>n,sizeof(unsigned)*8-1,n,y);
}
//计算出运行程序的计算机所使用的字长
int wordlength(void)
{
int i;
unsigned v=(unsigned)~0;
for(i=1;(v=v>>1)>0;i++)
;
return i;
}
//x&1 得到x的最后一位
unsigned rightot(unsigned x,int n)
{
int rbit;
while(n-- >0)
{
rbit=(x&1) << (wordlength()-1);
x=x>>1;
x=x|rbit;
}
return x;
}
unsigned rightot_2(unsigned x,int n)
{
unsigned rbits;
if((n=n%wordlength()) >0)
{
rbits = ~(~0 << n) &x; //得到x最右端的值
rbits = rbits << (wordlength() -n);
x = x >> n;
x = x | rbits;
}
return x;
}
2-9 在求对二的补码(Two’s complement)时,表达式x &=(x - 1)可以删除x中最右边值为1的一个二进制位(如1000中的1)。请解释这样做的道理。用这一方法重写bitcount函数(P.40 ~ P.41),以加快其执行速度。
//x声明为unsigned,保证右移后,左位补零
int bitcount(unsigned x)
{
int b;
for(b=0;x!=0;x>>1)
{
if(x&01)
b++;
}
return b;
}
int bitcount_1(unsigned x)
{
int b;
for(b=0;x!=0;x &=x-1)
++b;
return b;
}
2-10 重新编写将大写字母转换为小写字母的函数lower(P.34) 并用条件表达式替换其中的if-else结构。
int lower(int c)
{
return c>='A'&&c<='Z' ? c+'a'-'A':c;
}