C 程序设计语言——第二章练习题

C 程序设计语言——第二章——Types, Operators and Expressions——练习题

  • 2.1 at the end of this chapter summarizes precedence and associativity for all operators.
  • 2.2 Write a loop equivalent to the for loop above without using && or ||.
  • 2.3 Write a function htoi(s), which converts a string of hexadecimal digits (including an optional 0x or 0X) into its equivalent integer value. The allowable digits are 0 through 9, a through f, and A through F.
  • 2.4 Write an alternative version of squeeze(s1,s2) that deletes each character in s1 that matches any character in the string s2.
  • 2.5 Write the function any(s1,s2), which returns the first location in a string s1 where any character from the string s2 occurs, or -1 if s1 contains no characters from s2. (The standard library function strpbrk does the same job but returns a pointer to the location.)
  • 2.6 Write a function setbits(x,p,n,y) that returns x with the n bits that begin at position p set to the rightmost n bits of y, leaving the other bits unchanged.
  • 2.7 Write a function invert(x,p,n) that returns x with the n bits that begin at position p inverted (i.e., 1 changed into 0 and vice versa), leaving the others unchanged.
  • 2.8 Write a function rightrot(x,n) that returns the value of the integer x rotated to the right by n positions.
  • 2.9 In a two's complement number system, x &= (x-1) deletes the rightmost 1-bitin x. Explain why. Use this observation to write a faster version of bitcount.
  • 2.10 Rewrite the function lower, which converts upper case letters to lower case, with a conditional expression instead of if-else.

2.1 at the end of this chapter summarizes precedence and associativity for all operators.

参考:C语言运算符优先级
优先级总结:
括号,指向成员类优先级 > 单目运算符 > 算数运算符 (乘除求余 高于 加减)> 移位运算符 > 关系运算符 > 按位运算符 > 逻辑运算符 > 条件运算符 > 赋值运算符 > 逗号运算符

结合性:
只有单目运算符,条件运算符(? :)和赋值运算符是从右向左,其余从左往右。

2.2 Write a loop equivalent to the for loop above without using && or ||.

分析:
题中的for循环如下:

for(i = 0; i < lim - 1 && (c = getchar())!='\n' && c != EOF;++i)
s[i] = c;

要求实现相同的功能但不用逻辑运算符:

int i = 0;
char c;
while(i < lim - 1)
{
	c = getchar(); 
	if(c == EOF)
		break;
	else if (c == '\n')
		break;
	else
		s[i++] = c;
}

2.3 Write a function htoi(s), which converts a string of hexadecimal digits (including an optional 0x or 0X) into its equivalent integer value. The allowable digits are 0 through 9, a through f, and A through F.

分析:

  1. 提示输入十六进制字符数,注意大小,整型范围最大为 unsigned long,有8字节,算上十六进制前缀和字符串结束符,输入字符串长度最大19,需进行判断,长度超过重新输入。
  2. 判断字符串是不是合法:以 0x 或 0X 前缀开始,字符位于 0 ~9,a ~f, A ~ F。
  3. 字符转化为转换,首先将十六进制字符转换为对应的数需要分情况,这里大小写可以分开分情况探讨,一共三种情况,或者用 tolower()函数转化为小写字符。
  4. 十六进制转化为十进制:

十六进制数 0xa1a2a3a4,对应的十进制数计算为:
a4 + a3 * 16 + a2 * 16 * 16 + a1 * 16 * 16 * 16 =
a4 + 16* (a3 + 16 * (a2 + 16 * a1))

#include
#include
#include
#define LEN 19 //unsigned long有8位,要16个十六进制字符,加上两个字符表示前缀,一个字符串结束符
int s_gets(char *s, int n);
void eatline(void);
int check(char *s);
unsigned long htoi(char *s);
int main(void)
{
	char s[LEN];
	int ok, good;
	unsigned long val;
	puts("Please enter a string of hexadecimal digis (optional 0x or 0X):");
    while(((good = s_gets(s, LEN)) == 0) || (ok = check(s)) == 0)
	{
		if(good == 0)
		{
			printf("Please enter no more than %d hexadecimal characters, " 
			"enter again:\n",LEN-1);
			continue;
		}
		else if(ok == 0)
		{
			puts("Not a string of hexadecimal digis, enter again:");
			continue;
		}
	}
	val = htoi(s);
	printf("%s corresponds to %lu\n",s, val);
	return 0;
}
void eatline(void)
{
	while(getchar() != '\n')
		continue;
}
int s_gets(char *s, int n)
{
	char *find;
	int ok = 0; //检查输入是否超过,超过则对应整数范围超过,需重新输入,不能直接将后面截断
	fgets(s, n, stdin);
	if(s)
	{
		find = strchr(s, '\n');
		if(find)
		{	*find = '\0';
			ok = 1;
		}
		else
		{
			if(getchar() == '\n')
				ok =  1;
			else
				eatline();
		}	
	}
	return ok;
}
int check(char *s)
{
	int i = 0;
	int flag = 0;
	if(s[i++] == '0')
	{
		if(s[i] == 'x' || s[i] == 'X')
			flag = 1;
	}
	while(s[++i] != '\0' && flag == 1)
	{
		if(s[i] < '0' || (s[i] > '9' && s[i] < 'A') || (s[i] > 'F' && s[i] < 'a') || s[i] > 'f') 
			flag = 0;
	}
	return flag;
}
unsigned long htoi(char *s)
{
	unsigned long n = 0;
	int h_d;
	for(int i = 2; s[i] != '\0'; i++)
	{
		if(s[i] >= '0' && s[i] <= '9')
			h_d = s[i] - '0'; 
		else if(s[i] >= 'A' && s[i] <= 'F')
			h_d = s[i] - 'A' + 10;
		else if(s[i] >= 'a' && s[i] <= 'f')
			h_d = s[i] - 'a' + 10;
		n = 16 * n + h_d;
	}
	return n;
}
/* 
// 进制转换另一种方法 
// 加上头文件 ctype.h
unsigned long pow_i(int x, int y)
{
	unsigned long sum = 1;
	for(int i = 0; i < y; i++)
		sum *= x;
	return sum;
}
unsigned long htoi(char *s)
{
	unsigned long n = 0;
	int i, j;
	int h_d;
	int len = strlen(s);
	for(i = len - 1, j = 0; i > 1; i--, j++)
	{
		s[i] = tolower(s[i]);
		if(s[i] >= '0' && s[i] <= '9')
			h_d = s[i] - '0';
		else
			h_d = s[i] - 'a' + 10;
		n += s[i] * pow_i(16, j);
	}
	return n;
}
*/

2.4 Write an alternative version of squeeze(s1,s2) that deletes each character in s1 that matches any character in the string s2.

#include
#include
#define LEN 41
char *s_gets(char *s, int n);
void squeeze(char *s1, char *s2);
int main(void)
{
  char s1[LEN], s2[LEN];
  printf("Please enter two strings (no more than %d characters:\n",LEN-1);
  s_gets(s1, LEN);
  s_gets(s2, LEN);
  printf("Delete each charater in \"%s\" that matches any character in \"%s\".\n",s1,s2);
  printf("\"%s\" becomes ",s1);
  squeeze(s1, s2);
  printf("\"%s\"\n",s1);
  return 0;
}
char *s_gets(char *s, int n)
{
  char *ret_val, *find;
  ret_val = fgets(s, n, stdin);
  if(ret_val)
  {
    find = strchr(s, '\n');
    if(find)
    *find = '\0';
    else
    while(getchar() != '\n')
    continue;
  }
  return ret_val;
}
void squeeze(char *s1, char *s2)
{
  int i, j, k;
  for(i = 0; s2[i] != '\0'; i++)
  {
      for(j = 0, k = 0; s1[j] != '\0'; j++)
          if(s1[j] != s2[i])
	  s1[k++] = s1[j];
	  s1[k] = '\0';
  }
}

2.5 Write the function any(s1,s2), which returns the first location in a string s1 where any character from the string s2 occurs, or -1 if s1 contains no characters from s2. (The standard library function strpbrk does the same job but returns a pointer to the location.)

#include
#include
#define LEN 41
char *s_gets(char *s, int n);
int mystrpbrk(char *s1, char *s2);
int main(void)
{
	char s1[LEN], s2[LEN];
	int pos;
	printf("Please enter two strings (no more than %d characters):\n",LEN-1);
	s_gets(s1, LEN);
	s_gets(s2, LEN);
	pos = mystrpbrk(s1, s2);
	if(pos != -1)
		printf("The first character in \"%s\" that also belongs to \"%s\" is %c.\n",s1, s2, s1[pos]);
	else
		printf("\"%s\" does not have the same characters as \"%s\".\n",s1, s2);
	return 0;
}
char *s_gets(char *s, int n)
{
	char *ret_val, *find;
	ret_val = fgets(s, n, stdin);
	if(ret_val)
	{
		find = strchr(s, '\n');
		if(find)
			*find = '\0';
		else
			while(getchar() != '\n')
			continue;
	}
	return ret_val;
}
int mystrpbrk(char *s1, char *s2)
{
	int i, j;
	for(i = 0; s1[i] != '\0'; i++)
		for(j = 0; s2[j] != '\0'; j++)
			if(s1[i] == s2[j])
				return i;
	return -1;
}

2.6 Write a function setbits(x,p,n,y) that returns x with the n bits that begin at position p set to the rightmost n bits of y, leaving the other bits unchanged.

setbits(x,p,n,y) 函数返回对x执行下列操作后的结果值: 将x中从第p位开始的n个(二进制)位设置为y中最右边n位的值,x的其余各位保持不变。

分析:

  1. 将 x 中需要改变的第 p 位到第 p-n+1 位置0,其余不变。
mask1 = ~(~(~0 << n) << (p-n+1));
x = x & mask1;
  1. 将 y 的最低 n 位移到 p 到 p-n+1 位处,其余位全为0
y = y & (~(~0 << n)) << (p-n+1);
  1. 将 y 的 p 到 p-n+1 位的值替换 x 对应的位,其余不变
x = x | y;  // 0 | x = x;
unsigned setbits(unsigned x, int p, int n, unsigned y)
{
	unsigned mask1;
	mask1 = ~(~(~0 << n) << (p-n+1));
	x = x & mask1;
	y = y & (~(~0 << n)) << (p-n+1);
	x = x | y;  // 0 | x = x;
	return x;
}

2.7 Write a function invert(x,p,n) that returns x with the n bits that begin at position p inverted (i.e., 1 changed into 0 and vice versa), leaving the others unchanged.

invert(x,p,n) 函数返回对x执行下列操作后的结果值: 将x中从第p位开始的n个(二进制)位取反,x的其余各位保持不变。

分析:
取反考虑异或 ^,有如下关系:

1 ^ 1 == 0; // 1 与 x 异或,使 x 取反
1 ^ 0 == 1; 
0 ^ 0 == 0;  // 0 与 x 异或, x 不变
0 ^ 1 == 1;

因此,得到:

mask = ~(~0 << n) << (p-n+1); // 将 p 到 p-n+1 置1,其余0;
x = x ^ mask;

函数:

unsigned invert(unsigned x, int p, int n)
{
 	unsigned mask1;
	mask1 = ~(~(~0 << n) << (p-n+1));
	x = x ^ mask;
	return x;
}

2.8 Write a function rightrot(x,n) that returns the value of the integer x rotated to the right by n positions.

rightrot(x,n) 函数返回将x循环右移(即从最右端移出的位将从最左端移入)n(二进制)
位后所得到的值。

分析:

  1. 存储右移 n 位后最低端的 n-1 位到 0 位,用于以后将其填充到最高位。
unsigned y = ~(~0 << n) & x;
  1. 将第一步存储的数值移到最高位端,因此需要知道总共比特数
int len(void)
{
	unsigned i
	int len = 0;
	for(i = 1; i != 0; i << 1)
	++len;
	return len;
}
// len 就是unsigned 的位数
y = y << len - n;
  1. 将 y 存储的高位值替换 x 右移后空出的位
x = y | (x >> n);

函数:

int len(void)
{
	unsigned i
	int len = 0;
	for(i = 1; i != 0; i << 1)
	++len;
	return len;
}
unsigned y = ~(~0 << n) & x;
y = y << len - n;
x = y | (x >> n);
return x;

2.9 In a two’s complement number system, x &= (x-1) deletes the rightmost 1-bitin x. Explain why. Use this observation to write a faster version of bitcount.

分析:

  1. x - 1会将原来 x 的最后一个1变为0,因此 x & (x - 1)会将 x 中最右边一个1 去掉。
  2. bitcount() 函数统计数值的二进制表示中值为 1 的个数。原来书例子中的实现方式为:
int bitcount(unsigned x)
{
	int b;
	for(b = 0; x != 0; x >>= 1)
		if (x & 01)
			b++;
	return b;
}

改进:

int bitcount(unsigned x)
{
	int b = 0;
	while(x != 0)
	{
		b++;
		x &= (x - 1);
	}
	return b;
}

2.10 Rewrite the function lower, which converts upper case letters to lower case, with a conditional expression instead of if-else.

将一个字符串中的大写字符转化为小写字符,ASCLL 码中大些字母范围为 65 ~ 90,小写字母范围为 97 ~ 122,对于大些字母 c,将其转化为对应的小写字母:c + ‘a’ - 'A‘

void * lower(char *s)
{
	for(int i = 0; s[i] != '\0'; i++)
		s[i] = (s[i] >= 'A' && s[i] <= 'Z') ? (s[i] + 'a' - 'A') : s[i]);
}

你可能感兴趣的:(c,程序设计语言第二版练习题)