《C程序设计语言第二版》第二章-类型、运算符和表达式(2.2--2.12)

2.2 数据类型与长度

练习2-1


原文链接:https://blog.csdn.net/taolusi/article/details/52291798

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

方法1 打印标准头文件中的响应值

#include <stdio.h>
#include <limits.h>


//determing ranges of types
int main()
{
    //    signed types
    printf("Signed char min = %d\n", SCHAR_MIN);
    printf("Signed char max = %d\n", SCHAR_MAX);
    printf("Signed short min = %d\n", SHRT_MIN);
    printf("Signed short max = %d\n", SHRT_MAX);
    printf("Signed int min = %d\n", INT_MIN);
    printf("Signed int max = %d\n", INT_MAX);
    printf("Signed long min = %ld\n", LONG_MIN);
    printf("Signed long max = %ld\n", LONG_MAX);

    //    unsigned types
    printf("unsigned char max = %u\n", UCHAR_MAX);
    printf("unsigned short max = %u\n", USHRT_MAX);
    printf("unsigned int max = %u\n", UINT_MAX);
    printf("unsigned long max = %lu\n", ULONG_MAX);

    return 0;

}

在这里插入图片描述

方法2 利用按位运算符进行计算

#include<stdio.h>
 
//determing ranges of types
int main()
{
 
 
//    signed types
    printf("Signed char min = %d\n", -(char)((unsigned char) ~0 >> 1));
    printf("Signed char max = %d\n", (char)((unsigned char) ~0 >> 1));
    printf("Signed short min = %d\n", -(short)((unsigned short) ~0 >> 1));
    printf("Signed short max = %d\n", (short)((unsigned short) ~0 >> 1));
    printf("Signed int min = %d\n", -(int)((unsigned int) ~0 >> 1));
    printf("Signed int max = %d\n", (int)((unsigned int) ~0 >> 1));
    printf("Signed long min = %ld\n", -(long)((unsigned long) ~0 >> 1));
    printf("Signed long max = %ld\n", (long)((unsigned long) ~0 >> 1));
 
//    unsigned types
    printf("unsigned char max = %u\n", (unsigned char) ~0);
    printf("unsigned short max = %u\n", (unsigned short) ~0);
    printf("unsigned int max = %u\n", (unsigned int) ~0);
    printf("unsigned long max = %lu\n", (unsigned long) ~0);
 
    return 0;
 
}

(char)((unsigned char) ~0 >> 1)这个表达式的作用:
~0将0的各个二进制位取反全部转换为1,(unsigned char) ~0将结果值转换为unsigned char类型,(unsigned short) ~0 >> 1将unsigned char类型值右移一位以清除符号位,(char)((unsigned char) ~0 >> 1))将结果值进一步转换为char类型,最终得到了signed char类型的最大值。
在这里插入图片描述

2.6 关系运算符与逻辑运算符

练习2-2

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

#include <stdio.h>
#include <stdlib.h>    

#define MAXLENGTH 100

int getline(char line[], int limit);

int main()
{
	int length;//当前行的长度
	char line[MAXLENGTH] ;//保存当前行的字符串
	int i = 0;

	for (i = 0; i < MAXLENGTH; i++)
		line[i] = '\0';

	while ((length = getline(line, MAXLENGTH)) > 0)
	{
		printf("%d\n", length);
		printf("%s\n", line);
	}
	return 0;
}

/*getline函数:将输入的字符保存到line数组中,并返回数组长度。 */
int getline(char line[], int limit)
{
	int c;
	int i;
	c = i = 0;

	enum loop { NO, YES };
	enum  loop okloop = YES;

	while (okloop == YES) //进行循环的条件
	{
		if (i >= limit - 1)
			okloop = NO;
		else if ((c = getchar()) == '\n')
			okloop = NO;
		else if ((c = getchar()) == EOF)
			okloop = NO;
		else
			line[i] = c;
			++i;
	}

	if (c == '\n')
	{
		line[i] = c;
		++i;
	}

	line[i] = '\0';
	return i;
}

不够准确。

在这里插入图片描述

2.7 类型转换

练习2-3

编写函数 htoi(s),把由十六进制数字组成的字符串(包含可选的前缀0x或0X)转换为与之等价的整型值。字符串中允许包含的数字包括:0~9、a~f以及A~F。
C语言程序设计(第二版) 练习2-3 个人设计

#include <stdio.h>
#include <stdlib.h>

int htoi(char s[]);

int main()
{
    char s[] = "0xA1F";
    printf("%d\n", htoi(s));
    system("pause");
    return 0;
}

int htoi(char s[])                              /*十六进制转换函数*/
{
    int i, n, num;                               /*定义循环变量,中间变脸,最终转换值*/

    n = 0;
    num = 0;                                       /*以下两行为变量设置初始值*/
    i = 0;

    if (s[i] == '0') {                              /*以下四行为判断是否为十六进制数值,以0x或0X开头作为条件*/
        ++i;
        if (s[i] == 'x' || s[i] == 'X') {
            ++i;
            for (; s[i] != '\0'; ++i) {            /*判断字符串是否结束并循环*/
                if (s[i] >= '0' && s[i] <= '9')       /*如果字符为数字,转换为相应值*/
                    n = s[i] - '0';
                if (s[i] >= 'A' && s[i] <= 'F')       /*如果字符为大写字母,转换为相应十进制的值,如F在十进制中的值为15.*/
                    n = s[i] - 'A' + 10;
                if (s[i] >= 'a' && s[i] <= 'f')       /*如果字符为小写字母,转换为相应十进制的值。*/
                    n = s[i] - 'a' + 10;
                num = num * 16 + n;                   /*循环求值,同下面的十进制2564从左边开始计算求其十进制值2564一样,这是十六进制A1F从左边开始计算求其十进制值2591的一个规律。*/
            }
        }
    }
    return num;
}

后面一个值,是用前面相同的值计算出来的,则考虑用循环(for或者while)
有自增量时,用for;没有时,用while.

文件的结束符 EOF ;字符串的结束符 ‘\0’
C语言三个结束符:EOF ‘\0’ ‘\n’

在这里插入图片描述
骑码的佳俊

OK,我们现在讲一下解题步骤中第四步那个公式的来源:

如果你去百度或谷歌十六进制转换十进制,告诉你的方法都是从十六进制的最右端开始,乘以16的几次方一类的,这个方法适用于人,但是如果用C来实现,显得有点麻烦了,所以我才去了从高位开始转换,就是从最左端

我举个十进制的例子,大家就会豁然开朗了

比如有一个字符串‘2564’,他是十进制

它等于什么?

我们有那个公式来展开2564的话就是这样:
  0 * 10 + 2 = 2
  2 * 10 + 5 = 25
  25 * 10 + 6 = 256
  256 * 10 + 4 = 2564
因为是十进制所以我们乘以10,十六进制我们乘以16.

#include<stdio.h>
#define MAXLEN 1024
#define YES 1//是十六进制
#define NO 0//不是十六进制

int htoi(char array[]);//进制转换函数
int getlines(char array[], int maxlen);//字符串读取函数

int main()
{
	int len;//字符串长度
	int ten_number;//十进制数
	char array[MAXLEN];
	while ((len = getlines(array, MAXLEN)) > 0)
	{
		ten_number = htoi(array);
		printf("%d", ten_number);
		putchar('\n');
	}
	return 0;
}

int getlines(char array[], int maxlen)
{
	int c, i;
	for (i = 0; i < maxlen - 1 && (c = getchar()) != EOF && c != '\n'; i++)
	{
		array[i] = c;
	}
	if (c == '\n')
	{
		array[i] = c;
		i++;
	}
	array[i] = '\0';
	return i;
}

int htoi(char array[])
{
	int i, n;
	int result = 0;
	int state = YES;//判断是否是十六进制
	i = 0;
	if (array[i] == '0')
	{
		i++;
		if (array[i] == 'x' || array[i] == 'X')
		{
			i++;
			n = 0;
			for (; array[i] != '\0' && state == YES; i++)
			{
				if (array[i] >= '0' && array[i] <= '9')
				{
					result = array[i] - '0';
				}
				else if (array[i] >= 'A' && array[i] <= 'F')
				{
					result = array[i] - 'A' + 10;
				}
				else if (array[i] >= 'a' && array[i] <= 'f')
				{
					result = array[i] - 'a' + 10;
				}
				else
				{
					state = NO;    \\不止是开头两个字符,所有的字符如有任意一个不在十六进制的表达范围,则state被赋值为NO,所以这个命令语句处在这个位置
				}
				if (state == YES)   \\接着上面的判断,所有的字符都在十六进制的表达范围之中,则state仍为YES 
				{
					n = n * 16 + result;
				}
			}
		}
	return n;
	}
	else
		printf("error");
}

在这里插入图片描述

不同的main()函数的输出情况:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

原因:满足while语句的条件而且一直满足,没有被终止。
(1)在循环开始处加上break:输入并判断一行后,退出程序。
不要在循环的最后加,没有意义。
加入break表明要退出while循环,但while循环后面还有其他语句的话,还是会执行。

在这里插入图片描述
在这里插入图片描述

(2)在循环最后加入continue:输入一行,判断之后,无限循环输出。
加入continue表明要退出当次while循环,接着从while循环的开始部分重新执行下来。
在这里插入图片描述

在这里插入图片描述

(3)在循环最后加入return:输入一行后,退出函数。
加入return表明退出while循环,且后面还有其他语句的话,直接跳过不会执行。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.8 自增运算符和自减运算符

#include <stdio.h>

int main() 
{
	int i, j;

	char s[12] = { 'I',' ','a','m',' ','h','a','p','p','y','q'};
	int c = 'q';

	for (i = j = 0; s[i] != '\0'; ++i)
		if (s[i] != c) 
		{
			s[j++] = s[i];
			printf("%d %d\n", i, j);
		}
	s[j] = '\0';
	printf("%s\n",s);
	return  0;
}

在这里插入图片描述

P56(PDF)/ P37(书):
这里的 i 即:不需要使用任何具体值,仅需要递增变量。前缀后缀方式均可以。每次循环之后,i 的值总是加1的,不必关心 i++ 的值是多少。

需要使用 j 代表的下标,所以 j 必须先给值再自增。
因为 for 的执行顺序1-2-4-3-2-4-3-2-4-3-2-……,不管是i++ 还是++i,j 的值总比 i 大 1 .
如果用++j,那么读入的时候发生把s[0]写到s[1]的位置,后面依次类推,最终s[0] 的值不变,其他前一个写到后一个的位置,即全部都成为s[0]的值,且读不到字符数组结束符。

练习 2-4

骑马的佳俊《C程序设计语言》 练习2-4

第一种就是将s1字符串的字符,一个一个的去和s2的全部字符对比,一旦发现有重复的,比如s1中的第5个字符与s2中的某个字符重复,我们就把s1中,从第六个字符(包括第六个)之后的字符全部往前移动一位,这样第五个字符就被覆盖了,如同被删除。

#include<stdio.h>
#define MAXLEN 1024

void squeeze(char s1[], char s2[]);
void getlines(char array[], int maxlen);

int main()
{
	char s1[MAXLEN], s2[MAXLEN];
	getlines(s1, MAXLEN);
	getlines(s2, MAXLEN);
	squeeze(s1, s2);
	printf("%s", s1);
	return 0;
}

void squeeze(char s1[], char s2[])
{
	int i = 0, j = 0;
	for (i = 0; s1[i] != '\0'; i++)             //s1不动时,遍历s2,且依次处理s1(s2不动时,遍历s1,处理s1时也是每次都把s1中不相同的值前移。此方法中 i,j 两个for循环的顺序没有影响。)
	{
		for (j = 0; s2[j] != '\0'; j++)
		{
			if (s1[i] == s2[j])
			{
				int k = i;
				while (s1[k] != '\0')   
				{
					s1[k] = s1[k + 1];   //之后的字符全部往前移动一位
					k++;
				}
				j = -1;  //可以删除,没有什么实际作用
				s1[k] = '\0';  
			}
		}
	}
}
void getlines(char array[], int maxlen)
{
	int c, i;
	for (i = 0; i < maxlen - 1 && (c = getchar()) != EOF && c != '\n'; i++)
	{
		array[i] = c;
	}
	if (c == '\n')
	{
		array[i++] = c;
	}
	array[i] = '\0';
}

第二种也是要对比两个字符串,举个例子来解释这种方法,

例如s1中的第一个字符没有在s2中出现过,就将这个字符存到s1的第一个位置

再对比s1中第二个字符,如果这个字符在s2中也有,那么就不做任何操作

再对比s1中第三个字符,如果s2中没有这个字符,那么就将这个字符存进s1的第二个字符中

以此类推,最后给s1的最后一个位置存一个’\0’

其原理就是将不与s2重复的字符重新储存进s1中

#include<stdio.h>
#define MAXLEN 100

void getlines(char array[], char maxlen)
{
    int i, c;
    for (i = 0; i < maxlen - 1 && (c = getchar()) != EOF && c != '\n'; i++)
    {
        array[i] = c;
    }
    if (c == '\n')
    {
        array[i] = c;
        i++;
    }
    array[i] = '\0';
}

void squeeze(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++)  //对不相等的值其实进行了后面的拷贝操作,但是是在遍历完成s2[]之后才进行的,并不是立刻拷贝。
        {   //此方法中就必须保持s1不动,遍历s2,处理s1
            ;
        }
        if (s2[j] == '\0')
        {
            s1[k++] = s1[i]; //拷贝操作,在遍历完s2之后,把不相等的那一个s1[i]的值重新写入s1中
        }
        /* 如果在遍历s2时,中间出现一个与s1相同的,for跳出,if不满足跳出,就什么都不执行,且j也不会发生自增,这个重复的值不执行拷贝,也就相当于被删除了。
         接着判断s2中后面的字符与该s1中的字符是否相同,若后面的与该字符s1中的字符都相同,因为不满足for循环的条件,所以j不会自增,即到不了s2结束符处,也是什么都不执行。  */
         //如果s1的某个字符,与s2整个字符串都不相同,就会把s2遍历到最后,使s2[j] == '\0',就会执行上面这个if语句,不相同的值从s1[0]开始保存。
    }
    s1[k] = '\0';
}

int main()
{
    char s1[MAXLEN];
    char s2[MAXLEN];

    getlines(s1, MAXLEN);
    getlines(s2, MAXLEN);

    squeeze(s1, s2);
    printf("%s", s1);
    return 0;
}

在这里插入图片描述

练习 2-5

编写函数 any(s1, s2),将字符串s2中的任一字符在字符串s1中第一次出现的位置作为结果返回。如果s1中不包含s2中的字符,则返回-1。

#include<stdio.h>


#define MAXLEN 1024

int getlines(char array[], int limit);
void any(char s1[], char s2[]);

int main()
{
    char s1[MAXLEN];
    char s2[MAXLEN];

    int lens1, lens2;
    lens1 = 0;
    lens2 = 0;

    lens1 = getlines(s1, MAXLEN);
    lens2 = getlines(s2, MAXLEN);

    any(s1, s2);

    return 0;
}


int getlines(char array[], int limit)
{
    int i, c;
    for (i = 0; i < limit - 1 && (c = getchar()) != EOF && c != '\n'; i++)
    {
        array[i] = c;
    }
    if (c == '\n')
    {
        array[i] = c;
        i++;
    }
    array[i] = '\0';
    i++;
    return i;
}


void any(char s1[], char s2[])
{
    int i, j, sign, cnt;
    i = j = sign = cnt = 0;


    for (i = 0; s2[i] != '\0' && s2[i] != '\n'; i++)
    {
        sign = 0;
        for (j = 0; sign == 0 ; j++)   //sign == 0 保证只判断及返回某个字符第一次出现的位置。当第二次出现时,因为不满足判断条件,不进入这个for循环。
        {
            if (s1[j] == s2[i] && (s1[j] != '\0'))
            {
                sign = 1;
                cnt++;
                printf("%c在%d位置首次出现。\n", s2[i], j + 1);
            }
        }

        if (s1[j] == '\0')  //一次也没有执行上面的循环,说明s1中不包含s2中的这个字符。不能用cnt == 0 及 sign 作为判断标准。
        {
            printf("s1中不包含s2中的字符%c,%d\n", s2[i], -1);
        }
    }

    if (s2[i] == '\0')  //一次也没有执行上面的循环,说明s1中不包含s2中的这个字符。不能用cnt == 0 及 sign 作为判断标准。
    {
        printf("s1中不包含s2中的任意一个字符.");
    }
}

有多余行输出:

在这里插入图片描述
当全部不包含时,乱输出或者不输出:
在这里插入图片描述
在这里插入图片描述

#include <stdio.h>

int any(int p[], char s[], char t[]);        /*定义any函数*/
int length(char t[]);        /*定义字符串s2的长度函数*/

/*主程序为判断字符串s2中的任一字符在字符串s1中第一次出现的位置*/
int main()
{
    int i;
    int c;
    i = c = 0;

    int p[100];
    char a[] = "abc";     //函数中的字符串s
    char b[] = "abcdef";  //函数中的字符串t,将b中的每一个字符依次与a中的每一个字符比较,相等的返回该字符在a中的位置。

    any(p, a, b);   //并没有使用函数any的返回值,但是数组p中元素已经被赋给相应的值,可以取用其中的任意一个。
    c = length(b);    //确保把b中的元素全部判断完
    for (i = 0; i < c; i++)
        printf("%d  ", p[i]);
    printf("\n");

    return 0;
}

/*any函数程序*/
int any(int p[], char s[], char t[])
{
    int i, j, m, k;

    for (j = m = 0; t[j] != '\0'; j++) {        /*字符串t中的字符从第一个开始与字符串s中的字符配对*/
        k = 0;
        for (i = 0; (s[i] != '\0') && (s[i] != t[j]); i++)    /* 字符串t中的每一个字符与s中的第一个字符不相等,则k等于1,后面的还要接着判断,若有一个相等的(且还没有到达s的结束符)(此时k不会返回去等于0,仍为1)则要返回此时s的位置。即下面if中的前一个条件;第一个就相等则k=0,可知要返回该字符在s中的位置,即1,后面的不用再判断。  */
            k = 1;
        if (((k == 1) && (i != length(s))) || ((k == 0) && (i == 0)))        
                                                                              
            p[m++] = i + 1;                                    //将该字符在s中的位置,赋给数组p的元素,且数组p中的下标增加1
        else
            p[m++] = -1;                                      //否则就将-1赋给数组p中的元素,且数组p中的下标增加1
    }

    return p[m];
}

int length(char t[])
{
    int i, j;

    for (i = 1; t[i] != '\0'; ++i)
        ;
    j = i;

    return j;
}

2.9 按位运算符

#include <stdio.h>

int main()
{
	int n = 4049;
	n = n & 127;
	printf("%d", n);
	return 0;
}   

//结果为81

练习 2-6

骑码的佳俊:《C程序设计语言》 练习2-6 及 位运算总结

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

前提假定:最右边的一位是第0位

#include<stdio.h>

unsigned setbits(unsigned x, int p, int n, unsigned y)
{
    return ((x & ~(~(~0 << n) << p + 1 - n)) | ((~(~0 << n) & y) << p + 1 - n));
}
int main()
{
    printf("%d\n", setbits(93, 4, 3, 211));
    return 0;
}

在这里插入图片描述

执行顺序:
(1)执行按位或 | 前:
~0
(~0 << n)
(0 << n) 建立了最右边n位全为1的屏蔽码,前面的高位全部为0
((0 << n) << p + 1 - n) 将这n位全为1的屏蔽码左移p+1-n位(第n-1位移至第p位(n-1+p+1-n,原位数加移动的步数),第0位移至第p+1-n位),低位用0补全.除这n位屏蔽码为1之外,其余前后位均为0.
((~0 << n) << p + 1 - n) 之后再取反,此时除这n位屏蔽码为0之外,其余全部为1.
(x & ((~0 << n) << p + 1 - n) 将x中的屏蔽码对应的n位二进制位全部都变为0,剩余的保持原值

(2)执行按位或 | 后:
~0
(~0 << n)
(0 << n) 建立了最右边n位全为1的屏蔽码,前面的高位全部为0
((0 << n) & y) 保持y中最右边低n位的值不变,其余左边高位的二进制全部变为0
(((0 << n) & y) << p + 1 - n) 将y中最右边的低n位左移p+1-n位(第n-1位移至第p位(n-1+p+1-n,原位数加移动的步数),第0位移至第p+1-n位),低位用0补全,前面的二进制位也为0

(3)执行按位或 |
(x & ((~0 << n) << p + 1 - n)) | (((0 << n) & y) << p + 1 - n)
只分别取x中的原值(最高位至第p+1位,第p-n位至最低位第0位);
y中的原值(第p位至第p+1-n位,共n位)

用倒推法做:
x中间n位全为0:利用屏蔽码的左移(左移的步数p + 1 - n)及取反,保留x前后部分的原值;
y中 保留y中最右边低n位的原值,并将该部分左移(左移的步数p + 1 - n);及除与x中目标n位对应的部分之外 y中高位及低位 要全部为0.
(左移后,y中的低位会自动补0;
因此任务即是要把y中的低位部分屏蔽掉再左移,保留其原值再左移。
将y的高位变为0,保留低位原值,利用不移动的屏蔽码即可实现。)

整数在计算机中补码的各二进制位,不是数组,不是字符串,不能用元素赋值。

一种改变十进制整数值的思路:利用最右边低位全为1的屏蔽码(0 << n) 。

屏蔽码(0 << n) 的意思即:在用屏蔽码与整数进行按位与预算&时,保留整数的二进制码中 与该屏蔽码中为1的n位部分 对应的部分的原值,使该部分不受影响,如同被屏蔽掉。
屏蔽码(0 << n) 是从整数的低位开始屏蔽(保留整数原值)的。其中为1的片段长度n不变时, 该片段自身可通过进行左移、取反的运算,实现在与整数进行按位与运算&时,改变对整数中屏蔽的位置(保留整数原值的位置)。 如(2)中屏蔽整数前后两端,保留整数前后两端原值。
屏蔽码(0 << n) 中为1的片段长度n变长,则屏蔽的范围变大。

(1)如果不左移该屏蔽码(0 << n) 全为1的部分,直接与整数进行按位与&运算,则保留该整数中最右边低n位 与屏蔽码中为1的n位部分 对应的部分 原值,其他高位二进制全部变为0。将该整数中最右边低n位的部分屏蔽掉(保留原值)

(2) 如果先将屏蔽码中为1的部分左移,再将移动后的屏蔽码取反,然后与该十进制计算机中的二进制补码进行按位与运算&,即可保留该整数中与 左移后屏蔽码为1的部分所在的片段 对应的部分的原值。即将整数中左移后屏蔽码为1的n位部分所在的片段 的前后部分屏蔽掉(保留原值)

当屏蔽码为1的部分所在的片段长度n不变,从屏蔽码最右端左移到最左端时,则整数中前面部分前n位会被屏蔽掉(保留原值)

练习 2-7

Chris Hunter 练习 2-7

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

前提假定:最右边的一位是第0位

#include <stdio.h>

unsigned invert(unsigned x, int p, int n);

int main()
{
	int i;
	unsigned a;
	for(i = 0; i < 3; i++)
	{
		printf("输入一个整数:");
		scanf_s("%X",&a,sizeof(unsigned));
		printf("对第二三四位求反:%#X\n",invert(a,4,3));
	}

	return 0;
}

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

按位异或运算^ :两个操作数的对应位相同的时候设置为0,不同的时候设置为1.

((0<<n) << (p-n+1)) 将屏蔽码中的1移到中间 与x需要处理位的相同位置处
将该屏蔽码与x进行异或运算^,正好实现题目中的求反要求。

X^1的结果 与 ~X的结果相同。

输入16进制数,返回16进制数
在这里插入图片描述

#include <stdio.h>

unsigned invert(unsigned x, int p, int n);

int main()
{
	int i;
	unsigned a;

	a = 26;

	printf("对第二三四位求反:%d\n", invert(a, 4, 3));

	return 0;
}

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

设定a为十进制某数,输出十进制数。

在这里插入图片描述

练习 2-8

骑码的佳俊 练习 2-8

第一种:就是默认二进制位数,前面没有多余的0来补位,比如111的二进制是1101 111,我就认为题目的函数对1101 111进行循环右移,就是把最右端的数字放到最左端,即 1101 111

#include<stdio.h>

int rightrot(unsigned int x, int n)
{
    int k, i;
    int ribits;
    k = x;
    for (i = 0; k != 0; i++)//算出x二进制形式有多少位
    {
        k = k >> 1;   //先将K右移一位,再将移动后的K值赋成新的K值
    }

    /* K经过多次右移,会变成0 .K =0时,i的值为K右移的次数,也即K二进制形式的位数。 */

    ribits = (~(~0 << n) & x) << (i - n);  //(~(~0 << n) & x)结果X中低n位二进制全部都保留原值,高位二进制全部归为0. ribits是将X中保留原值的n位部分移i-n位到最前面,其余低(i-n)位全部归为0(先从右端移出的也先进入左端,所以直接把从右端移出的部分按原顺序平移到最前即可).
    x = (x >> n) | ribits;  //x >> n时,X中高n位补0.
    return x;  
}

int main()
{
    printf("%d", rightrot(111, 3));
    return 0;
}

在这里插入图片描述

第二种:这一种理解方式是比较符合实际的,就是计算机中二进制数位是有规定的,比如有的计算机是32位。
那么111的二进制就不是1101 111了,而是0000 0000 0000 0000 0000 0000 0110 1111(在前面补了25位0,对数值大小无影响)。
那么我们把最右端的一个数字放到最左端就是1000 0000 0000 0000 0000 0000 0110 111 结果显然与第一种不同。

#include<stdio.h>
 
unsigned rightrot(unsigned x, unsigned n)
{
    while (n > 0) {
        if ((x & 1) == 1)    //x&1用来判断x的奇偶性,x&1结果为1则n为奇数,为0则n为偶数。
            x = (x >> 1) | ~(~0U >> 1);//U表示无符号,相当于unsigned. 0U表示无符号整型0
        else
            x = (x >> 1);
        n--;
    }
    return x;
}
 
int main()
{
    printf("%d",rightrot(111,3));
    return 0;
}

X为奇数,如111时,共移动右三位。

每次只移动一位,每次都判断被移动数的奇偶性:

(1)当被移动数为奇数时,即将其最后一位的1移到最前面,其余保留原值且右移一位:
x: 0000 0000 0000 0000 0000 0000 0110 1111
x >> 1: 0 0000 0000 0000 0000 0000 0000 0110 111

~0U:1111 1111 1111 1111 1111 1111 1111 1111
~0U >> 1: 0 1111 1111 1111 1111 1111 1111 1111 111
(0U >> 1):1 0000 0000 0000 0000 0000 0000 0000 000(不是屏蔽码(0 << n))

(x >> 1) | (0U >> 1) : 按位或运算 1 0000 0000 0000 0000 0000 0000 0110 111

(2)当被移动数为偶数时,即将其最后一位的0移到最前面,其余保留原值且右移一位:
使用 x >> 1即可满足要求,高位补0,低位舍弃,其余右移。

在这里插入图片描述

2.10 赋值运算符与表达式

函数bitcount统计X中值为1的二进制位数。

#include<stdio.h>

int bitcount(unsigned x)
{

    int b;

    for (b = 0; x != 0; x >>= 1)
    {
        if (x & 01)
            b++;
    }

    return b;
}

int main()
{
    printf("%d", bitcount(111));
    return 0;
}

//结果为6,对应111的二进制1101111.

练习 2-9

原bitcount函数:统计x中值为1的二进制位数。

#include<stdio.h>

int bitcount(unsigned x)
{

    int b;

    for (b = 0; x != 0; x >>= 1)
    {
        if (x & 01)
            b++;
    }

    return b;
}

int main()
{
    printf("%d", bitcount(111));
    return 0;
}

//结果为6,对应111的二进制1101111.

表达式 x &= (x - 1)可以删除x中最右边值为1的一个二进制位。
利用该表达式改进bitcount函数。

#include<stdio.h>

int bitcount(unsigned x)
{
    int b = 0;

    while (x != 0)
    {
        x &= (x - 1);   //等价于 x = x & (x - 1)
        b++;
    }

    return b;
}

int main()
{
    printf("%d", bitcount(111));
    return 0;
}

//结果为6,对应111的二进制1101111.
#include<stdio.h>

int bitcount(unsigned x)
{
    int b = 0;

    for (b = 0; x != 0; x &= (x - 1))
    {
        b++;
    }

    return b;
}

int main()
{
    printf("%d", bitcount(111));
    return 0;
}

//结果为6,对应111的二进制1101111.

2.11 条件表达式

练习 2-10

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

原lower函数:

#include<stdio.h>

int lower(int c)
{

    if (c >= 'A' && c <= 'Z')
        return c + 'a' - 'A';
    else
        return c;
}

int main()
{
    int c = 0;
    int n = 0;

    while ((c = getchar()) != EOF && c != '\n')
    {
        n = lower(c);
        printf("%c", n);   //printf("%d", n); 也可
    }

    return 0;
}

用条件表达式?: 改写后的lower函数。

#include<stdio.h>

int lower(int c)
{
    (c >= 'A' && c <= 'Z') ? (c + 'a' - 'A') || c;
}

int main()
{
    int c = 0;
    int n = 0;

    while ((c = getchar()) != EOF && c != '\n')
    {
        n = lower(c);
        printf("%c", n);  //printf("%d", n); 也可
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值