部分答案参考了官方题解和网上的答案,仅供参考,可能也有部分bug未发现或解决。代码在gitee上面。

exercise2-1

编写一个程序以确定分别由 signed 及 unsigned 限定的 char、short、int 与 long 类型变量的取值范围。采用打印标准头文件中的相应值以及直接计算两种方式实现。后一种方法的实现较困难一些,因为要确定各种浮点类型的取值范围。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#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 short min %d\n", SHRT_MIN);
    printf("Size of short max %d\n", SHRT_MAX);
    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);
    printf("Size of long max %ld\n", LONG_MAX);
    printf("Size of unsigned char %u\n", UCHAR_MAX); 
    printf("Size of unsigned short %u\n", USHRT_MAX);
    printf("Size of unsigned int %u\n", UINT_MAX);   
    printf("Size of unsigned long %lu\n", ULONG_MAX);
    return 0;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Size of char 8
Size of char max 127
Size of char min -128
Size of short min -32768
Size of short max 32767
Size of int min -2147483648
Size of int max 2147483647
Size of long min -9223372036854775808
Size of long max 9223372036854775807
Size of unsigned char 255
Size of unsigned short 65535
Size of unsigned int 4294967295
Size of unsigned long 18446744073709551615

exercise2-2

在不使用运算符&&或||的条件下编写一个与上面的 for 循环语句等价的循环语句。

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

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

exercise2-3

编写函数 htoi(s),把由十六进制数字组成的字符串(包含可选的前缀 0x或 0X)转换为与之等价的整型值。字符串中允许包含的数字包括:0~9、a~f 以及 A~F。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <stdio.h>
#include <string.h>
#include <math.h>
#define MAXLINE 1024

int mpow(int a, int b) {
    int ans = 1;
    while (b--)
        ans *= a;
    return ans;
}

int calculate(char line[]) {
    int len = strlen(line), ans = 0;
    for (int i = 0; i < len; i++) {
        ans += line[len - 1 - i] * mpow(16, i);
    }
    return ans;
}

int parse(char c) {
    if (c >= '0' && c <= '9')
        return c - '0';
    if (c >= 'a' && c <= 'f')
        return 10 + c - 'a';
    if (c >= 'A' && c <= 'F')
        return 10 + c - 'A';
    return -1;
}

int Getline(char line[]) {
    int len = 0, c, digit;
    while ((c = getchar()) != '\n') {
        if (len == 1 && (c == 'x' || c == 'X') && line[0] == 0) {
            len = 0;
            continue;
        }
        if ((digit = parse(c)) == -1) {
            printf("Wrong input!\n");
            return -1;
        } else {
            line[len++] = digit;
        }
    }
    line[len] = 0;
    return len;
}

int main() {
    char line[MAXLINE];
    while (Getline(line) != -1) {
        printf("value is = %d\n", calculate(line));
    }
}
1
2
3
4
5
6
0x43997a7b
value is = 1134131835
12345
value is = 74565
98765
value is = 624485

exercise2-4

squeeze(s1, s2),将字符串 s1 中任何与字符串 s2 中字符匹配的字符都删除。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <string.h>

void squeeze(char s1[], char s2[]) {
    char count[256] = {0};    // 用来记录每个字符是否出现在s2中
    int len1 = strlen(s1), len2 = strlen(s2);
    char ans[len1 + 1];
    int i, j;
    while (len2--)
        count[s2[len2]] = 1;
    
    for (i = 0, j = 0; i < len1; i++)
        if (count[s1[i]] == 0) 
            s1[j++] = s1[i]; 
    
    s1[j] = '\0';
}

int main() {
    char s1[] = "12345  6789";
    char s2[] = "24 68";
    printf("s1 = %s\ns2 = %s\n", s1, s2);
    squeeze(s1, s2);
    printf("ans = %s\n", s1);
}

结果

1
2
3
s1 = 12345  6789
s2 = 24 68
ans = 13579

exercise2-5

编写函数 any(s1, s2),将字符串 s2 中的任一字符在字符串 s1 中第一次出现的位置作为结果返回。如果 s1 中不包含 s2 中的字符,则返回-1。(标准库函数 strpbrk 具有同样的功能,但它返回的是指向该位置的指针。)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <stdio.h>
#include <string.h>

void any(char s1[], char s2[], int pos[]) {
    // count存储字符在s2中第一次出现的位置,
    int len1 = strlen(s1), len2 = strlen(s2), count[256];
    for (int i = 0; i < 256; i++)
        count[i] = -1;

    for (int i = 0; i < len2; i++) 
        if (count[s2[i]] == -1)
            count[s2[i]] = i;    

    // pos存储s1中每一个字符在s2中第一次出现的位置
    // pos大小为s1的元素个数
    for (int i = 0; i < len1; i++) 
        pos[i] = count[s1[i]];    
}

int main() {
    char s1[] = "abcdefga";
    char s2[] = "aceg";
    printf("s1 = %s\ns2 = %s\n", s1, s2);
    int len = strlen(s1);
    int pos[len];
    for (int i = 0; i < len; i++)
        pos[i] = -1;
    any(s1, s2, pos);
    printf("s1中每一个字符在s2中第一次出现的位置(按照s1中的下标):\n");
    for (int i = 0; i < len; i++) 
        printf("%d ", pos[i]);
    printf("\n");
    return 0;
}
1
2
3
4
s1 = abcdefga
s2 = aceg
s1中每一个字符在s2中第一次出现的位置(按照s1中的下标):
0 -1 1 -1 2 -1 3 0

exercise2-6

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>

unsigned setbits(unsigned x, int p, int n, unsigned y) {
    // 构建掩码,用于将x中的第p位开始的n位置为0
    unsigned mask = ~(~0 << n) << (p + 1 - n);
    // x中从第p位开始的n位置为0
    x = x & ~mask;
    // 取出y中最右边的n位,并将其移到合适的位置
    unsigned yn = (y & ~(~0 << n)) << (p + 1 - n);
    
    return x | yn;
}

int main() {
    unsigned x = 0b10101011;  
    unsigned y = 0b00111010;  
    int p = 5;
    int n = 3;

    unsigned ans = setbits(x, p, n, y);
    printf("setbits(%u(%08b), %d, %d, %u(%08b)) = %d(%08b)\n", x, x, p, n, y, y, ans, ans);
}
1
setbits(171(10101011), 5, 3, 58(00111010)) = 147(10010011)

exercise2-7

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

unsigned invert(unsigned x, int p, int n) {
    // 构建掩码,用于选定从第p位开始的n位
    unsigned mask = (~(~0 << n)) << (p + 1 - n);
    
    // 对选定的位进行求反,并保持其余位不变
    return x ^ mask;
}

int main() {
    unsigned x = 0b11011001; 
    int p = 4;
    int n = 3;

    unsigned ans = invert(x, p, n);
    printf("invert(%u(%08b), %d, %d) = %u(%08b)\n", x, x, p, n, ans, ans);
}
1
invert(217(11011001), 4, 3) = 197(11000101)

exercise2-8

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <stdio.h>

unsigned rightrot(unsigned x, int n) {
    // 制作掩码
    unsigned mask = (~(~0 << n) & x) << (8 - n);
    
    // 将x右移n位之后与掩码或操作
    return mask | (x >> n);
}

int main() {
    unsigned x = 0b00000111;
    int n = 3;

    unsigned ans = rightrot(x, n);
    printf("rightrot(%u(%08b), %d) = %u(%08b)\n", x, x, n, ans, ans);
}
1
rightrot(7(00000111), 3) = 224(11100000)

exercise2-9

在求对二的补码时,表达式 x &= (x – 1)可以删除 x 中最右边值为 1 的一个二进制位。请解释这样做的道理。用这一方法重写 bitcount 函数,以加快其执行速度。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

int bitcount(unsigned x) {
    int ans = 0;
    // 不断除去最右边的1,直至变为0
    while (x != 0) {
        x &= (x - 1);
        ans++;
    }
    return ans;
}

int main() {
    int x = 0b00100101;

    int count = bitcount(x);
    printf("bitcount(%u(%08b)) = %d\n", x, x, count);
}
1
bitcount(37(00100101)) = 3

若有变量 x,在其最右边一个 1 之后比特位都为 0,若对其进行减一的操作,则在比特位上的表现为:将最后一个 1 及其右边的比特位全部反转,例如将 $00101000$变成 $00100111$,进行b & (b - 1)操作之后,会将反转的位都变成 0,从而实现了去除最右边一个 1 的操作。

exercise2-10

重新编写将大写字母转换为小写字母的函数 lower,并用条件表达式替代其中的 if-else 结构。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <string.h>
#define MAXLINE 1024

void lower(char str[]) {
    int len = strlen(str);
    for (int i = 0; i < len; i++) {
        str[i] += (str[i] >= 'A' && str[i] <= 'Z') ? 'a' - 'A' : 0;
    }
}

int main() {
    char str[MAXLINE];
    scanf("%[^\n]", str); // 捕获直到换行符
    lower(str);
    printf("%s\n", str);
}
1
2
This Is A GOOD idea
this is a good idea