本章问题

1.这个表达式的类型和值为多少?

(float)(/)

answer:The cast is applied to the result of the division,and because both operand are integers,a truncating(截断) integer division is done,The value therefore is 2.0,If you wanted to do a floating-point divisioin,use this expression instead (float)25/10,the same applies to other integer expressions.

(由于两个操作数都是整数,因此除法完成的时候得到一个被截断的整数再转化为浮点数,因此值为2.0,如果你想要做浮点数除法,那么应该用表达式 (float)25 / 10,这也适应其他整型表达式)

2.下面这个程序的结果是什么?

int
func (void)
{
static int counter = ; return ++counter;
} int
main()
{
int answer;
answer = func() - func() * func();
printf("%d\n",answer);
}

answer:Trick question! The obvious answer is -10(2 - 3 * 4),but in fact it is implemnentation dependent,The multiplication must be completed before the addition,but there isn't a rule mat determines the order in which the function calls are done,Thus,the answer could be any of the following:

(一个狡猾的问题,显然的结果是-10,不过事实上根据编译器的不同而不同,乘法一定先于加法计算,不过函数计算的先后顺序并没有规定,所以下面答案都有可能)

- ( -  * ) or ( -  * )
- ( - * ) or ( - * )
- ( - * ) or ( - * )

3.你认为位操作符和移位操作符可以用在什么地方?

answer:They are often used when programming device controllers to set or test specific bits in specific locations,If anyone comes up with other good answers,please mail them to me at kar@cs.rit.edu!

(它们经常被使用于当程序设计控制设计或测试特殊的位在特殊的地方,如果任何人发现其他更好的答案,请把答案发邮箱给我kar@cs.rit.edu)

4.条件操作符在运行时较之if语句是更快还是更慢?试比较下面两组代码:

//left:
if(a > )
i = b + ;
else
i = c * ; //right:
i = a > ? b + : c * ;

answer:Not they each do precisely(精确的) the same work,If you want to get picky(挑剔),the if version might possibly be a tad(一点儿) longer because it has two instructions to store into i.However,only one of them will be executed,so there isn't any difference in speed.

(不,它们同样的工作,如果你想要挑剔,if语句可能要长一点因为它有两条指令来存储i,然而只有一条语句将被执行,所以它们在速度上没有区别)

5.可以被4整除的年份是闰年,但是其中能够被100整除的又不是闰年,不过,其中能被400整除的又是闰年,请用一条赋值语句,如果变量year的值是闰年,把leap_year的值设置为真,如果year不是闰年,把leap_year设置为假。

answer:

leap_year =  (year %  ==  || (year %  ==  && year %  != ))

6.哪些操作符具有副作用,它们具有什么副作用?

answer:The () operator doesn't have any side effects,but the function being called might.

(括号操作符没有任何副作用,不过当函数被调用的时候可能有副作用)

Operator Side Effect
++ , --

In both prefix and postfix forms,these operators modify the L-value on which they operate.

(前缀和后缀形式都会修改它们的左值操作数)

=

And all of the other assignment operators:They all modify the L-value given as the left operand.

(所有的复制操作符,都会修改它们的左值操作数)

7.下面这个代码段的结果是:

int a = ;
...
if( <= a <= )
printf("In Range\n");
else
printf("Out of Range\n");

answer:it print In Range,This is because 1 <= a is true and evaluates to 1;The expression then tests 1 <= 10,which is also true.

(将打印出In Range,这是因为1 <= a是真的并且等于1,这个表达式之后测试 1 <= 10也是对的)

8.改写下面的代码段,消除多余的代码

a = f1(x);
b = f2(x + a);
for(c = f3(a,b); c > ; c = f3(a,b)){
statements
a = f1(++x);
b = f2(x + a);
}

answer:

while(a = f1(x),b = f2(x + a),c = f3(a,b),c > ){
statement
++x;
}

9.下面的循环能实现它的目的吗?

non_zero = ;
for(i = ; i < ARRAY_SIZE; i += )
non_zero += array[i];
if(!non_zero)
printf("Values are all zero\n");
else
printf("there are nonzero values\n");

answer:No,It fails if the array contains nonzero values that happen to sum to zero.

(不能,当数组包括不为零的值但是它们之和为零,这种情况就是错的)

10.根据下面的变量声明和初始化,计算下列每个表达式的值,如果某个表达式具有副作用,注明它们,在计算每个表达式时,每个变量所使用的是开始时给出的初始值,而不是前一个表达式的结果。

int a = , b = -;
int c = ; d = ;
int e = ; a. b
b. b++
c. --a
d. a/
e. a %
f. b %
g. a << h. b >>
i. a > b
j. b = a
k. b == a
l. a & b
m. a ^ b
n. a | b o. ~b
p. c && a
q. c || a
r. b ? a : c
s. a +=
t. b &= u. b >>=
v. a %=
w. d = a > b
x. a = b = c = d
y. e = d +( c = a + b) + c
z. a + b * aa. b >> a -
bb. a != b != c
cc. a == b == c
dd. d < a < e
ee. e > a > d
ff. a - > b +
gg. a & 0x1 == b & 0x1 hh. a | b << a & b
ii. a > c || ++a > b
jj. a > c && ++a > b
kk. !~b++
ll. b++ & a <=
mm. a - b, c += d, e - c
nn. a <<= > oo. a <<= d > ? b && c++ : d--

answer:

a. -
b. -, b = -
c. , a =
d.
e.
f. -
g.
h. -
i.
j. , b =
k.
l.
m. -
n. -
o.
p.
q.
r.
s. , a =
t. , b =
u. -, b = -
v. , a =
w. , d =
x. , a = , b = , c =
y. -, c = -, e = -
z. -
aa. -
bb.
cc.
dd.
ee.
ff.
gg.
hh. -
ii.
jj. , a =
kk. , b = -
ll. , b = -
mm. , c =
nn. 20
oo. , a = , d =

11.下面列出几个表达式,请判断编译器是如何对各个表达式进行求值的,并在不改变求值顺序的情况下,尽可能去除多余的括号。

a. a+(b/c)
b. (a+b)/c
c. (a*b)%
d. a*(b%)
e. (a + b) ==
f. ! (( a >= ’’) && (a <= ’’) )
g. (( a & 0x2f ) == ( b | )) && ((~ c) > 0)
h. (( a << b ) – )<( b <<( a + 3))
i. ~ (a ++)
j. ( (a == 2) || (a == 4) ) && ( (b == 2) || (b == 4) )
k. (a&b)^(a|b)
l. (a+(b+c))

answer:

 a. a+b/c
b. (a+b)/c
c. a*b%
d. a*(b%)
e. a + b ==
f. ! ( a >= ’’ && a <= ’’ )
g. ( a & 0x2f ) == ( b | ) && ~ c >
h. ( a << b ) – < b << a +
i. ~ a ++
j. ( a == || a == ) && ( b == || b == )
k. a&b^(a|b)
l. a+(b+c)

12.如何判断在你的机器上对一个有符号值进行右移位操作时执行的是算数移位还是逻辑移位?

answer:On a two's complement(补码) machine,declare a signed integer,assign it a negative value,right shift it one bit,and then print the result,If it is negative,an arithmetic shift was used;positive indicates a logical shift.

(声明一个有符号数,赋值一个负数,右移一位后结果如果是负数则为算数移位,否则为逻辑右移)

本章练习 

1.编写一个程序,从标准输入读取字符,并把它们写到标准输出中。除了大写字母字符要转换为小写字母之外,所有字符的输出形式应该和他的输入形式完全相同。

answer:

#include <stdio.h>

int main()
{
int ch;
while((ch = getchar()) != EOF){
if(ch >= 'A' && ch <= 'Z')
ch -= ('A' - 'a');
putchar(ch);
}
return ;
} /*以上代码只能在以ASCII码运行的机器上,在其他字母不是连续编码的系统中不具备可移植性*/
/*下面代码是采用库函数来完成转换,注意tolower本身并不会改变字符的值,返回值会输出小写字符*/
#include <stdio.h>
#include <ctype.h> int main()
{
int ch;
while((ch = getchar()) != EOF){
putchar(tolower(ch));
}
return ;
}

2.编写一个程序,从标准输入读取字符,并把它们写到标准输出中,所有非字母字符都完全按照它的输入形式输出,字母字符在输出之前进行加密。

加密的方法很简单:每个字母被修改为在字母表上距其13个位置(前或后)的字母,例如,A被修改为N,B被修改为O,Z被修改为M,以此类推,注意大小写字母都应该被转换。提示:记住字符实际上是一个较小的整型这一点可能会对你有所帮助。

answer:

#include <stdio.h>

int main()
{
int ch;
while((ch = getchar()) != EOF){
if((ch >= 'A' && ch <= 'M') || (ch >= 'a' && ch <= 'm'))
ch += ;
else if((ch >= 'N' && ch <= 'Z') || (ch >= 'n' && ch <= 'z'))
ch -= ;
putchar(ch);
}
return ;
}

3.请编写函数

unsigned int reverse_bits(unsigned int value);

这个函数的返回值是吧value的二进制位模式从左到右变换一下之后的值。例如,在32位机器上,25这个值包含下列各位:

,,,

函数的返回值应该是2550136832,它的二进制位模式是:

,,,

编写函数时注意不要让它依赖于你的机器上整型值的长度。

answer:

unsigned int reverse_bits(unsigned int value)
{
unsigned int dst = ;
int i;
for(i = ; i != ; i <<= ){
dst <<= ;
if(value & )
dst |= ;
// dst <<= 1;
value >>= ;
}
return dst;
}

注意dst <<= 1这条语句必须出现在dst |= 1之前,否则dst的最后一位不管是什么都会被左移一位,正确的答案相当于做了一次左移位运算。

4.编写一组函数,实现位数组,函数的原型如下:

void set_bit(char bit_array[], unsigned bit_number);

void clear_bit(char bit_array[], unsigned bit_number);

void assign_bit(char bit_array[], unsigned bit_number, int value);

int test_bit(char bit_arrar[], unsigned bit_number);

每个函数的第一个参数是字符数组,用于实际储存所有的位,第二个参数用于标识需要访问的位。函数的调用者必须确保这个值不要太大,以至于超出数组边界。第一个函数把指定的位设置为1,第二个函数把指定的位清零,如果value的值为1,第三个函数就把指定的位清零,否则置一,至于最后一个函数,如果参数中指定的位不为零就返回真,否则返回假。

answer:

/*这个题目如果不仔细读题的话,可能一下子写出下面的代码出来,以第一个函数为例*/
void set_bit(char bit_array[], unsigned bit_number)
{
if(bit_number <= sizeof(bit_array)){
bit_array[bit_number] |= << bit_number;
}
}
/*如果是这样写的话,那就错得很厉害了*/
//这是正确的代码
unsigned character_offset(unsigned bit_number)
{
return bit_number / CHAR_BIT;
} unsigned bit_offset(unsigned bit_number)
{
return bit_number % CHAR_BIT;
} void set_bit(char bit_array[], unsigned bit_number)
{
bit_array[character_offset(bit_numer)] |=
<< bit_offset(bit_number);
} void clear_bit(char bit_array[], unsigned bit_number)
{
bit_array[character_offset(bit_number)] &=
~ ( << bit_offset(bit_number));
} void assign_bit(char bit_array[], unsigned bit_number, int value)
{
if(value != )
set_bit(bit_array,bit_number);
else
clear_bit(bit_array,bit_number);
} int test_bit(char bit_arrar[], unsigned bit_number)
{
if(bit_array[character_offset(bit_number)] &
<< bit_offset(bit_number) != )
return ;
else
return ;
}
/*刚开始看我是不太懂的,咋整得这么麻烦呢,接下来分析一下:*/
/*
首先bit_array是一个字符数组,它里面存的是字符,假定在你的机器上每个字符的二进制位数为n,
那么bit_array的每一个元素都有n位,如果bit_array有m个元素,那么总的bit_array就有m * n个位,
如果指定在bit_number位的话,那么应该就是数组的第bit_number / n 个元素,
所以character_offset函数的作用就是将bit_number 转换为字符数组中的元素
因为每一个元素都只有n位,而bit_number这个值是针对所有的元素位数的,
所以你需要将它定位在第bit_number这个元素的位数,
而这个位可以用取余的方法bit_number % n来得到,
所以bit_offset的作用就是把bit_number转换到需要改变的元素的精确位
而CHAR_BIT就是机器上字符的二进制位数
*/

5.编写一个函数,把一个给定的值存储到一个整数中指定的几个位。它的原型如下:

int store_bit_field(int original_value, int value_to_store,
unsigned starting_bit, unsigned ending_bit);

假定整数中的位是从右向左进行编号。因此,起始位的位置不会小于结束位的位置,为了更清楚的说明,函数应该返回下列值。

原始值 需要储存的位 起始位 结束位 返回值
0x0 0x1 4 4 0x10
0xffff 0x123 15 4 0x123f
0xffff 0x123 13 9 0xc7ff

提示:把一个值存储到一个整数中指定的几个位分为5个步骤,以上表最后一行为例:

1).创建一个掩码,它是一个值,其中需要存储的位置相对应的那几个位设置为1,此时掩码为

,

2).用掩码的反码对原值执行AND操作,将那几个位设置为0.原值1111111111111111,操作后变为

,

3).将新值左移,使它与需要存储的位对齐,新值00000001,00100011(0x123),左移后变为

,

4).把移位后的值与掩码进行位AND操作,确保除那几个需要存储的位之外的其余位都设置为0,进行这个操作之后,值变为

,

5).把结果值与原值进行位OR操作,结果为(0xc7ff)

,

在所有任务中,最困难的是创建掩码,你一开始可以把~0这个值强制转换为无符号值,然后再对它进行移位。

answer:

#include <stdio.h>

int store_bit_field(int original_value, int value_to_store,
unsigned starting_bit,unsigned ending_bit); int main(void)
{
printf("%x\n",store_bit_field(0x0,0x1,,));
printf("%x\n",store_bit_field(0xffff,0x123,,));
printf("%x\n",store_bit_field(0xffff,0x123,,));
return ;
} int store_bit_field(int original_value, int value_to_store,
unsigned starting_bit,unsigned ending_bit)
{
int value;
int i = ending_bit;
int unmask = ;
int mask = ;
int num = starting_bit - ending_bit + ;
while(num != ){
mask <<= ;
mask |= ;
num--;
}
while(i != ){
i--;
mask <<= ;
} unmask = ~mask;
original_value &= unmask; i = ending_bit;
while(i != ){
i--;
value_to_store <<= ;
} value = value_to_store & mask; value |= original_value;
return value;

《C与指针》第五章练习的更多相关文章

  1. C和指针 第五章 位数组

    5.4的习题:编写一组函数,实现维数组,函数原型如下: //指定位设置为1void set_bit(char bit_array[], unsigned bit_number); //指定位清零 vo ...

  2. C和指针 第五章 习题

    下列输出的值: #include <stdio.h> int func(){ static int count = 1; return ++count; } int main() { in ...

  3. C和指针 第五章 警告总结

    1.有符号的值得右移位操作是不可移植的 2.移位操作的位数是个负数,是未定义的 3.连续赋值的各个变量的长度 不一,导致变量值截断. #include <stdio.h> int main ...

  4. C和指针 第五章 逻辑位移与算术位移

    对于操作数的左位移都是相同的,右边空出来的位置用0补齐. 但是对于右位移,对于有符号和无符号数是不一样的,最高位的1有两种处理方式.逻辑位移和算术位移. 逻辑位移:右移入位用0补齐 算术位移:右移入位 ...

  5. 深入理解 C 指针阅读笔记 -- 第五章

    Chapter5.h #ifndef __CHAPTER_5_ #define __CHAPTER_5_ /*<深入理解C指针>学习笔记 -- 第五章*/ /*不应该改动的字符串就应该用 ...

  6. C和指针 (pointers on C)——第五章:操作符和表达式

    第五章 操作符和表达式 这一章假设没做过玩过单片机.汇编的话,读起来可能比較吃力,尤其是在移位运算符.位运算符应用上.另外多注意一下左值和右值的理解. 总结: 算术操作符.赋值操作符.关系操作符.条件 ...

  7. C和指针 第六章 习题

    6.1编写一个函数,它在一个字符串中进行搜索,查找所有在一个给定字符集中出现的字符,返回第一个找到的字符位置指针,未找到返回NULL #include <stdio.h> char * f ...

  8. 《Linux内核设计与实现》读书笔记 第五章 系统调用

    第五章系统调用 系统调用是用户进程与内核进行交互的接口.为了保护系统稳定可靠,避免应用程序恣意忘形. 5.1与内核通信 系统调用在用户空间进程和硬件设备间添加了一个中间层, 作用:为用户空间提供了一种 ...

  9. 《Linux内核设计与实现》课本第五章学习笔记——20135203齐岳

    <Linux内核设计与实现>课本第五章学习笔记 By20135203齐岳 与内核通信 用户空间进程和硬件设备之间通过系统调用来交互,其主要作用有三个. 为用户空间提供了硬件的抽象接口. 保 ...

  10. 《java编程思想》读书笔记(一)开篇&第五章(1)

    2017 ---新篇章  今天终于找到阅读<java编程思想>这本书方法了,表示打开了一个新世界. 第一章:对象导论 内容不多但也有20页,主要是对整本书的一个概括.因为已经有过完整JAV ...

随机推荐

  1. sql语句中left join、inner join中的on与where的区别

    table a(id, type): id     type ---------------------------------- 1      1 2      1 3      2 table b ...

  2. Android Studio build dex jar

    Gradle配置 Build配置文件gradle.build中添加如下task task clearJar(type: Delete) { delete 'build/outputs/mylib.ja ...

  3. webform简单控件

    表单元素: 文本类: text password textarea hidden text,password,textarea实现控件:textbox   textmode属性选择password或m ...

  4. 从BATS交易所获取空头头寸

    Getting short volume from BATS   In my last post I have gone through the steps needed to get the sho ...

  5. Android 四大组件之一(Activity)

    Activty的生命周期的也就是它所在进程的生命周期. 一个Activity的启动顺序: onCreate()——>onStart()——>onResume() 当另一个Activity启 ...

  6. 控制反转(IOC: Inverse Of Control) & 依赖注入(DI: Independence Inject)

    举例:在每天的日常生活中,我们离不开水,电,气.在城市化之前,我们每家每户需要自己去搞定这些东西:自己挖水井取水,自己点煤油灯照明,自己上山砍柴做饭.而城市化之后,人们从这些琐事中解放了出来,城市中出 ...

  7. nodejs之process进程

    虽然node对操作系统做了很多抽象的工作,但是你还是可以直接和他交互,比如和系统中已经存在的进程进行交互,创建工作子进程.node是一个用于事件循环的线程,但是你可以在这个事件循环之外创建其他的进程( ...

  8. HTML5的 2D SVG和SVG DOM的学习笔记(2)---SVG动画

    SVG支持动画.可以通过以下几种方法获得动画效果: 使用SVG动画元素.SVG可以描述随时间变化的图形对象,使用不同的动画元素可以定义运动路径,淡入淡出效果和对象的膨胀.收缩.旋转和变换颜色. 使用S ...

  9. 算法课堂笔记13—Online Algorithm

    今天的算法课是学习离线算法,在计算机科学中,一个在线算法是指它可以以序列化的方式一个个的处理输入,也就是说在开始时并不需要已经知道所有的输入.相对的,对于一个离线算法,在开始时就需要知道问题的所有输入 ...

  10. Java设计模式系列2--工厂方法模式(Factory Method)

    2014-02-26 09:56:45 声明:本文不仅是本人自己的成果,有些东西取自网上各位大神的思想,虽不能一一列出,但在此一并感谢! 工厂方法模式分为三种: 1. 普通工厂模式 建立一个工厂类,对 ...