C与指针——操作符和表达式(二)

编程练习

3.

请编写程序

unsigned int reverse_bits(unsigned int value);

这个函数的返回值是把value的二进制位模式从左到右变换一下后的值。例如,在32位机器上,25这个值包含下列各个位:
0000 0000 0000 0000 0000 0000 0001 1001
函数的返回值应该是2 550 136 832,它的二进制位模式是:
1001 1000 0000 0000 0000 0000 0000 0000
编写函数时要注意不要让它依赖于你的机器上整型值的长度。

程序如下:

#include<stdio.h>

unsigned int reverse_bits(unsigned int value)
{
	unsigned int dst=0;
	int i;
	for(i=1;i!=0;i<<=1)//循环32次,i每循环一次就向左移一位(后补0)
	{
		dst<<=1; //相当于dst=dst<<1,将dst转换为二进制后向左移一位 
		if(value&1) //value与1,全1为1 
		dst|=1; //相当于dst=dst|1,位或运算,全零为零 
		value>>=1; //将value转换为二进制后右移一位,value=value>>1
	}
	return dst;
}

int main()
{
	unsigned int b,d;
	scanf("%u",&d);
	b=reverse_bits(d);
	printf("%u",b);
	return 0;
}

运行结果:
在这里插入图片描述

  1. unsighed(%u)与int(%d)的区别
    unsigned 是无符号数,int是有符号数;
    二者占空间大小相同, 区别是最高位,int表示符号位,而unsigned表示数据位.
    所以 int可以表示负数,而unsigned不能. 当unsigned可以表示的正数范围比int大一倍.
  2. 常见格式说明符
    %u——读入十进制无符号整数
    %d——读入十进制有符号整数
    %p——读入指针的值
    %e——读入指数形式的浮点数
    %x——读入十六进制整数
  3. &的使用—可以作为“按位与”或是“取地址”运算符

下面是作为两种用法的介绍:
1.按位与运算
按位与运算符"&"是双目运算符。其功能是参与运算的两数各对应的二进位相与。只有对应的两个二进位均为1时,结果位才为1 ,否则为0。参与运算的数以补码方式出现。
例如:9&5可写算式如下: 00001001 (9的二进制补码)&00000101 (5的二进制补码) 00000001 (1的二进制补码)可见9&5=1。
按位与运算通常用来对某些位清0或保留某些位。例如把a 的高八位清 0 , 保留低八位, 可作 a&255 运算 ( 255 的二进制数为0000000011111111)。
2.取地址
&作为一元运算符,结果是右操作对象的地址。
例如&x返回x的地址。
地址本身是一个抽象的概念,用于表示对象在存储器中的逻辑位置

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);
void test_bit(char bit_array[],unsigned bit_number);

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

分析: 注意题干中访问的是位。假设一个字符数组中有n个字符,每个字符有m个二进制位数,则一个字符数组中共有n*m个二进制位数。所以在程序中添加了将bit_number定位到字符数组中第几个字符的函数character_offset,和将bit_number定位到字符数组中第几个二进制位数的函数bit_offset。使用CHAR_BIT来代替机器上字符的二进制位数,即这里假设的m。
&为与运算,可通过任何值&1后值不变,任何值&0后值为零的性质来达到清零的目的;
|为或运算,可通过任何值|1后值为1,任何值|0后值不变的性质来达到置1的目的。x |= 1 << k 的作用其实就是将x的第k位设置为1。

程序如下:

#include<stdio.h>
#define CHAR_BIT 8

void set_bit(char bit_array[20],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_array[],unsigned bit_number);

int main()
{
    unsigned bit_number,value;
    char bit_array[20];
    int b;
    printf("请输入字符数组:\n");
    gets(bit_array);
	printf("请输入指定位:\n");
	scanf("%d",&bit_number);
	set_bit(bit_array, bit_number);
	printf("前两个函数的结果为:\n");
	puts(bit_array);
	clear_bit(bit_array, bit_number);
	puts(bit_array);
	printf("请输入value的值:\n");
	scanf("%d",&value);
	assign_bit(bit_array,bit_number, value);
	printf("后两个函数的结果为:\n");
	puts(bit_array);
	b=test_bit(bit_array, bit_number);
    printf("%d",b);
	return 0;
} //编写主程序进行测试

unsigned character_offset(unsigned bit_number)
{
    return bit_number / CHAR_BIT;
} //将bit_number转换为字符数组中的元素

unsigned bit_offset(unsigned bit_number)
{
    return bit_number % CHAR_BIT;
} // 将bit_number转换到需要改变的精确位,取余的方法可达到此目的 

/*以上两个函数计算了bit_number是在第几个字节中的第几位*/ 
 
void set_bit(char bit_array[], unsigned bit_number)
{
    bit_array[character_offset(bit_number)] |= 1 << bit_offset(bit_number);
} //将bit_array(字符数组)中某个元素(字符)的bit_number位置1,先左移再或操作 

void clear_bit(char bit_array[], unsigned bit_number)
{
    bit_array[character_offset(bit_number)] &=~ (1 << bit_offset(bit_number));
} //x &=~k的作用是将k按位取反后的值和x做与运算最后赋值给x,实际就是将x的第~k位清零
 
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_array[], unsigned bit_number)
{
    if(bit_array[character_offset(bit_number)] & 1 << bit_offset(bit_number) !=  0)
        return 1;
    else
        return 0;
}

运行结果:
在这里插入图片描述

说明: 为了简化计算量,在测试这个程序的时候,就只输入了单个字符,输入的a会在机器中转换为ASCII码,进而再转换成二进制进行运算。

补充优先级知识点:

优先级与求值顺序无关。
如a+b && bc,虽然优先级最高,但这个表达式求值顺序是从左到右。
优先级从上到下依次递减,最上面具有最高的优先级,逗号操作符具有最低的优先级。
相同优先级中,按结合性进行结合。大多数运算符结合性是从左到右,只有三个优先级是从右至左结合的,它们是单目运算符、条件运算符、赋值运算符。
基本的优先级需要记住:
指针最优,单目运算优于双目运算。如正负号。
先算术运算,后移位运算,最后位运算。
请特别注意:1 << 3 + 2 & 7等价于 (1 << (3 + 2))&7.
逻辑运算最后结合。
同一优先级的运算符,结合次序由结合方向所决定。
简单记就是:! > 算术运算符 > 关系运算符 > && > || > 赋值运算符

5

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

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

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

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

提示: 把一个值存储到一个整数中指定的几个位分为5个步骤。以上表最后一行为例:
1.创建一个掩码(mask),它是一个值,其中需要存储的位置相对应的几个位设置为1。此时掩码为0011 1110 0000 000。
2.用掩码的反码对原值执行AND操作,将那几个位设置为0。原值1111 1111 1111 1111,操作后变为1100 0001 1111 1111。
3.将新值左移,使它与那几个需要存储的位对齐。新值0000 0001 0010 0011(0x123),左移后变为0100 0110 0000 0000。
4.把移位后的值与掩码进行位AND操作,确保除那几个需要存储的位之外的其余位都设置为0.进行这个操作之后,值变为0000 0110 0000 0000。
5.把结果值与原值进行位OR操作,结果为1100 0100 1111 1111(0xc7ff),也就是最终的返回值。
在所有任务中,最困难的是创造掩码。你一开始可以把~0这个值强制转换为无符号值,然后再对它进行移位。

分析: 我将提示中的五个步骤书面过了一遍,前两个步骤可相当于处理原始值,三四步骤则处理的是需要存储的值,即提示中的新值,而第五个步骤就是将处理过后的原始值与需要存储的值进行或(OR)运算。第二个步骤中的原始值是和掩码的非进行与运算;第四个步骤是将左移后的需要存储的值和掩码进行与运算。

程序如下:

#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;
    } //提示中第1步,在掩码中将需要存储的对应位赋值为1 
    
    unmask = ~mask;
    original_value &= unmask;
	//第2步,掩码求反和原值相与,将存储位置0 

    i = ending_bit;
    while(i != 0){
        i--;
        value_to_store <<= 1;
    } //第3步,需存储的值与存储位对齐 

    value = value_to_store & mask;
    //第4步,移位后存储的值与掩码相与,将除存储位外其余置0 

    value |= original_value;
	//第5步,将第4步处理后的需存储值与经第2步处理过的原值进行或运算 
    return value;
}

运行结果:
在这里插入图片描述

要记住,有人在替你负重前行。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值