《C与指针》第五章练习

本章问题

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

(float)(25/10)

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 = 1;

    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,不过事实上根据编译器的不同而不同,乘法一定先于加法计算,不过函数计算的先后顺序并没有规定,所以下面答案都有可能)

-10 (2 - 3 * 4) or (2 - 4 * 3)
-5 (3 - 2 * 4) or (3 - 4 * 2)
-2 (4 - 2 * 3) or (4 - 3 * 2)

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 > 3)
    i = b + 1;
else
    i = c * 5;

//right:
i = a > 3 ? b + 1 : c * 5;

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 % 400 == 0 || (year % 4 == 0 && year % 100 != 0))

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

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

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

OperatorSide 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 = 20;
...
if(1 <= a <= 10)
    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 > 0; 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 > 0){
    statement
    ++x;
}

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

non_zero = 0;
for(i = 0; i < ARRAY_SIZE; i += 1)
    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 = 10, b = -25;
int c = 0; d = 3;
int e = 20;

a. b
b. b++
c. --a
d. a/6
e. a % 6
f. b % 10
g. a << 2

h. b >> 3
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 += 2
t. b &= 20

u. b >>= 3
v. a %= 6
w. d = a > b
x. a = b = c = d
y. e = d +( c = a + b) + c
z. a + b * 3

aa. b >> a - 4
bb. a != b != c
cc. a == b == c
dd. d < a < e
ee. e > a > d
ff. a - 10 > b + 10
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 <= 30
mm. a - b, c += d, e - c
nn. a <<= 3 > 0

oo. a <<= d > 20 ? b && c++ : d--
    

answer:

a. -25
b. -25, b = -24
c. 9, a = 9
d. 1
e. 4
f. -5
g. 40
h. -4
i. 1
j. 10, b = 10
k. 0
l. 2
m. -19
n. -17
o. 24
p. 0
q. 1
r. 10
s. 12, a = 12
t. 4, b = 4
u. -4, b = -4
v. 4, a = 4
w. 1, d = 1
x. 3, a = 3, b = 3, c = 3
y. -27, c = -15, e = -27
z. -65
aa. -1
bb. 1
cc. 1
dd. 1
ee. 0
ff. 1
gg. 0
hh. -25590
ii. 1
jj. 1, a = 11
kk. 0, b = -24
ll. 1, b = -24
mm. 17, c = 3
nn. 20
oo. 80, a = 80, d = 2

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

a. a+(b/c)
b. (a+b)/c
c. (a*b)%6
d. a*(b%6)
e. (a + b) == 6
f. ! (( a >= ’0’) && (a <= ’9’) )
g. (( a & 0x2f ) == ( b | 1 )) && ((~ c) > 0)
h. (( a << b ) – 3 )<( 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%6
d. a*(b%6)
e. a + b == 6
f. ! ( a >= ’0’ && a <= ’9’ )
g. ( a & 0x2f ) == ( b | 1 ) && ~ c > 0
h. ( a << b ) – 3 < b << a + 3
i. ~ a ++
j. ( a == 2 || a == 4 ) && ( b == 2 || b == 4 )
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 0;
}

/*以上代码只能在以ASCII码运行的机器上,在其他字母不是连续编码的系统中不具备可移植性*/
/*下面代码是采用库函数来完成转换,注意tolower本身并不会改变字符的值,返回值会输出小写字符*/
#include <stdio.h>
#include <ctype.h>

int main()
{
    int ch;
    while((ch = getchar()) != EOF){
        putchar(tolower(ch));
    }
    return 0;
}

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 += 13;
        else if((ch >= 'N' && ch <= 'Z') || (ch >= 'n' && ch <= 'z'))
            ch -= 13;
        putchar(ch);
    }
    return 0;
}

3.请编写函数

unsigned int reverse_bits(unsigned int value);

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

00000000,00000000,00000000,00011001

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

10011000,00000000,00000000,00000000

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

answer:

unsigned int reverse_bits(unsigned int value)
{
    unsigned int dst = 0;
    int i;
    for(i = 1; i != 0; i <<= 1){
        dst <<= 1;
        if(value & 1)
            dst |= 1;
//      dst <<= 1;    
        value >>= 1;
    }
    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] |= 1 << 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)] |=
        1 << bit_offset(bit_number);
}

void clear_bit(char bit_array[], unsigned bit_number)
{
    bit_array[character_offset(bit_number)] &=
        ~ (1 << bit_offset(bit_number));
}

void assign_bit(char bit_array[], unsigned bit_number, int value)
{
    if(value != 0)
        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)] &
       1 << bit_offset(bit_number) !=  0)
        return 1;
    else
        return 0;
}
/*刚开始看我是不太懂的,咋整得这么麻烦呢,接下来分析一下:*/
/*
首先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);

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

原始值需要储存的位起始位结束位返回值
0x00x1440x10
0xffff0x1231540x123f
0xffff0x1231390xc7ff

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

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

00111110,00000000

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

11000001,11111111

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

01000110,00000000

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

00000110,00000000

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

11000111,11111111

在所有任务中,最困难的是创建掩码,你一开始可以把~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,4,4));
    printf("%x\n",store_bit_field(0xffff,0x123,15,4));
    printf("%x\n",store_bit_field(0xffff,0x123,13,9));
    return 0;
}

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 = 0;
    int mask = 0;
    int num = starting_bit - ending_bit + 1;
    while(num != 0){
        mask <<= 1;
        mask |= 1;
        num--;
    }
    while(i != 0){
        i--;
        mask <<= 1;
    }
    
    unmask = ~mask;
    original_value &= unmask;

    i = ending_bit;
    while(i != 0){
        i--;
        value_to_store <<= 1;
    }

    value = value_to_store & mask;

    value |= original_value;
    return value;
}