六、C语言归纳笔记总结4进阶篇4(交互图形设计与文件)

六、交互图形设计

在Windows命令行里编译ACLLIB的程序

6.1 图形程序的终端输入输出

用C语言编写图形程序不是C语言所擅长的。本周我们安排的这些内容的最终目的也不是在于图形界面程序设计。

我们所希望你学会的,是函数指针和交互图形界面程序中所体现出来的设计模式。

6.11 图形程序的终端输入输出

#include "acllib.h"
#include <stdio.h>

int Setup()
{
	initWindow("test",DEFAULT,DEFAULT,800,800);//初始化程序窗口。该函数必须在 Setup 函数中最先调用,并且只能够调用一次。
	initConsole();//初始化终端窗口(增加一个黑窗)。在该函数执行后,才能使用 printf 和 scanf。
	printf("Hello\n");
	beginPaint();//函数负责初始化绘图操作
	int x;
	line(10,10,100,100);
	scanf("%d",&x);
	line(100,100,x,0);
	endPaint();
	
	return 0;
}

在这里插入图片描述

6.2 函数指针及其应用

6.21 函数指针

#include<stdio.h>

void f(void)
{
	printf("in f(), %d\n");
}

int main() {
	int i=0;
	int *p = &i;
	*p = 20;
	int a[] = { 1,2 };
	void(*pf)(void) = f;//定义一个新的变量pf,类型是指向函数的指针,返回是void(前面那个),参数表是(void),把函数f交给这个变量
	f();//直接调用f函数
	(*pf)();//通过指针调用f函数
	printf("%p\n", a);
	printf("%p\n",main);
	printf("%p\n", pf);

	return 0;
}

输出

in f(), 728323280
in f(), 619158464
000000F50534F568
00007FF624E71195
00007FF624E713B1

6.22 函数指针的使用

  • 数组的每一项都指向一个函数,通过一个变量让数组的某个函数运行
#include<stdio.h>

void f(int i)
{
	printf("in f(), %d\n", i);
}
void g(int i)
{
	printf("in g(), %d\n", i);
}
void h(int i)
{
	printf("in h(), %d\n", i);
}
void k(int i)
{
	printf("in k(), %d\n", i);
}

int main(void) {
	int i = 0;
	//定义函数指针的数组fa,里面每一项都是一个函数指针
	void(*fa[])(int) = { f,g,h,k };

	scanf("%d", &i);
	if (i >= 0 && i < sizeof(fa)/sizeof(fa[0])) {
		(fa[i](0));
	}
	/*
	switch(i)
	{
	  case 0:f(0);break;
	  case 1:g(0);break;
	  case 2:h(0);break;
	}
	*/
	return 0;
}

输出

0
in f(), 0
  • 函数cal运行时,从外面传入其他函数,传入的函数不同,函数cal流程不变,运算方式改变
#include<stdio.h>

int plus(int a, int b)
{
	return a + b;
}
int minus(int a, int b)
{
	return a - b;
}
void cal(int (*f)(int, int))
{
	printf("%d",(*f)(2,3));
}

int main(void) {
	cal(plus);
	printf("\n------\n");
	cal(minus);

	return 0;
}

输出

5
------
-1

6.3 交互图形程序设计

6.31 回调函数

  • 鼠标点下来时,运行对应程序
#include "acllib.h"
#include <stdio.h>

void mouseListoner(int x,int y,int button, int event) 
{
	printf("x=%d,y=%d,button=%d,event=%d\n",x,y,button,event);
}
int Setup()
{
	initWindow("test",DEFAULT,DEFAULT,800,800);//初始化程序窗口。该函数必须在 Setup 函数中最先调用,并且只能够调用一次。
	initConsole();//初始化终端窗口(增加一个黑窗)。在该函数执行后,才能使用 printf 和 scanf。
	printf("Hello\n");

	
	
	registerMouseEvent(mouseListoner);
	
	beginPaint();//函数负责初始化绘图操作

	
	
	line(10,10,100,100);

	endPaint();
	
	return 0;
}

运行截图
在这里插入图片描述

  • 回调函数原理
    函数mL若是死循环,信息无法传回去,会无法关掉(黑窗)
    在这里插入图片描述

6.32 图形交互信息

The callbacks (回调)
  • typedef void (*KeyboardEventCallback)(const char key);
    //键盘,特殊功能键,按下和抬起
  • typedef void(*CharEventCallback)(int key);
    //键盘可读可显示字符,收到哪个键用哪个
  • typedef void( *MouseEventCallback)(int x, int y, int button, int status);
    //鼠标的移动,按下,抬起
  • typedef void (*TimerEventCallback)(int timerID);
    //鼠标定时器
#include "acllib.h"
#include <stdio.h>

void mouseListoner(int x,int y,int button, int event) 
{
	static int ox=0;
	static int oy=0;
	printf("x=%d,y=%d,button=%d,event=%d\n",x,y,button,event);
    beginPaint();
    line(ox,oy,x,y); //画线	
    endPaint();
    ox=x;oy=y;
} 
int Setup()
{
	initWindow("test",DEFAULT,DEFAULT,800,800);//初始化程序窗口。该函数必须在 Setup 函数中最先调用,并且只能够调用一次。
	initConsole();//初始化终端窗口(增加一个黑窗)。在该函数执行后,才能使用 printf 和 scanf。
	printf("Hello\n");
	
	registerMouseEvent(mouseListoner);
	
	beginPaint();//函数负责初始化绘图操作

	line(10,10,100,100);

	endPaint();
	
	return 0;
}

运行截图
在这里插入图片描述

  • 鼠标、键盘、计时器得到回调消息
#include "acllib.h"
#include <stdio.h>

void mouseListoner(int x,int y,int button, int event) 
{
	static int ox=0;
	static int oy=0;
	printf("x=%d,y=%d,button=%d,event=%d\n",x,y,button,event);
    beginPaint();
    line(ox,oy,x,y); //画线	
    endPaint();
    ox=x;oy=y;
} 
void keyListoner(int key,int event)
{
	printf("key=%d,event=%d\n",key,event);
 } 
 void timeListoner(int id)
{
	static int cnt=0;
	printf("id=%d\n",id);
	if( id == 0)
	{
		cnt++;
		if(cnt == 5)
		cancelTimer(0);
	}
 } 
 
 
int Setup()
{
	initWindow("test",DEFAULT,DEFAULT,800,800);//初始化程序窗口。该函数必须在 Setup 函数中最先调用,并且只能够调用一次。
	initConsole();//初始化终端窗口(增加一个黑窗)。在该函数执行后,才能使用 printf 和 scanf。
	printf("Hello\n");

	
	
	registerMouseEvent(mouseListoner);//鼠标 
	registerKeyboardEvent(keyListoner);//键盘 
	registerTimerEvent(timeListoner);//定时器 
	startTimer(0,500);
	startTimer(1,1000);	
	beginPaint();//函数负责初始化绘图操作

	
	
	line(10,10,100,100);

	endPaint();
	
	return 0;
}

输出
在这里插入图片描述

6.4 游戏设计思路

6.41 MVC设计模式

  • 图形界面三部分组成
    • view 读数据,从model里取数据
    • model 存有数据,通知view数据是否改变
    • ctrl 告诉model什么数据怎么改
  • 鼠标的动作不直接引起图像的改变,而是数据的改变
    在这里插入图片描述

6.42 游戏设计思路

  • 炮台打飞机
    • bs for ob,ects(对象的数据结构或链表)
    • timer (时间间隔)
    • scan (扫描所有的数据结构)more
    • hit,crash (打击以及各种状况)
    • refresh (draw重画)

七、文件

7.1 文件

本章是关于C语言如何做文件和底层操作的。

文件操作,从根本上说,和C语言无关。这部分的内容,是教你如何使用C语言的标准库所提供的一系列函数来操作文件,最基本的最原始的文件操作。你需要理解,我们在这部分所学习的,是函数库的使用,而非C语言。顺便我们还学习了很多和计算机相关的知识,比如重定向、文本文件和二进制文件的江湖恩怨。但是既然不是C语言,也就意味着你将来的工业环境下,未必还会使用这么原始的文件操作函数了。这些函数,只是一个标本,让你知道可以这样来操纵文件。但是,不见得所有的库都是以这样的方式来操纵文件的。

围绕文件操作,还有一个C语言受时代局限,处理得不够好的东西,就是错误处理。因为文件操作的每一个步骤都很可能在实行过程中遇到问题:文件打不开啦,读了一半出错啦,等等等等,所以设计文件操作函数如何反馈和处理这类运行时刻的问题是需要很好的手段的。C的这个函数库,主要是通过特殊的返回值来实现的。后来的语言,如C++和Java,则引入了异常机制来处理这些事情。

关于底层操作,我们主要是介绍了按位操作,包括按位的运算、移位和位段。这些操作在日常程序中难得遇到,主要是用于接触硬件的底层程序的。这些内容在本课程是选读的。

7.11 格式化输入输出

printf
  • %[flags][width][.prec][hlL]type
Flag (标志)
Flag含义
-左对齐
+在前面放+或-
(space)正数留空
00填充
#include<stdio.h>

int main(void) {
	
	printf("%9d\n",123);//输出占9个字符的输出空间(右对齐)
	printf("%-9d\n",123);// - 号是左对齐
	printf("%+9d\n", 123);// + 号是在前面放+或-(强制输出那个 + 号)
	printf("%-+9d\n", 123);// 左对齐
	printf("%+-9d\n", 123);//与上面一个意思
	printf("%-+9d\n", -123);// 输出负数
	printf("%-9d\n", -123);// 与上面一个意思
	printf("%09d\n", 123);//0补齐9位
	printf("%-09d\n", 123);//左对齐(后面不填0)

	return 0;
}

输出

      123
123
     +123
+123
+123
-123
-123
000000123
123
width 或 prec (宽度或精度)
width 或 prec含义
number最小字符数
*下一个参数是字符数
.number小数点后的位数
.*下一个参数是小数点后的位数
#include<stdio.h>

int main(void) {
	
	int len = 6;
	printf("%9.2f\n",123.0);//总共占9个字符的位置, .2 (.number) 是小数点后占2位空间
	printf("%*d\n",6, 123);//6是用来满足 * 号的,123是满足%d (输出总共占据6个字符的位置)
	printf("%6d\n", 123);//等价上面一列
	printf("%*d\n", len, 123);//len 可以设置为一个变量

	return 0;
}

输出

   123.00
   123
   123
   123
hlL (类型修饰)
类型修饰含义
hh单个字节
hshort
llong
lllong long
Llong double

注:l 是字符L的小写

#include<stdio.h>

int main(void) {
	
	
	printf("%hhd\n", 12345);//16进制0x3039 (有些时候会报错)只取最低位0x39 10进制57
	printf("%hhd\n", (char)12345);//无报错

	return 0;
}

输出

57
57
type (类型)
type含义
i 或 dint
uunsigned int
o八进制
x十六进制
X字母大写的十六进制
f 或 Ffloat,6
e或 E指数
gfloat
Gfloat
a 或 A十六进制浮点
cchar
s字符串
p指针
n读入/写出的个数 (“n”格式说明符现已禁用)
scanf
  • %[flag]type
flag含义
*跳过
数字最大字符数
hhchar
hshort
llong double
lllong long
Llong double
  • %*
#include<stdio.h>

int main(int argc, char const *argv[]) {

	int num;
	scanf("%*d%d",&num);// * 意思是跳过
	printf("%d ", num);

	return 0;
}

输出

123 456789
456789     //跳过第一个,输出第二个
type含义
dint
i整数,可能为十六进制或八进制
uunsigned int
o八进制
x十六进制
a,e,f,gfloat
cchar
s字符串(单词)
[…]所允许的字符
p指针
  • %i
#include <stdio.h>

int main()
{
	int num;
	while (1) {
	scanf("%i", &num);
	printf("num = %d\n", num);

	}

	return 0;

}

输出

123        //十进制
num = 123
0x12       //十六进制
num = 18 
012       //八进制
num = 10  
  • [^.]

    • //$GPRMC,004319.00,A,3016.98468,N,12006.39211,E, 0.047,,130909,,,D*79
    • scanf("%*[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,]", sTime,sAV,sLati,&sNW,sLong,&sEW,sSpeed,sAngle,sDate);
printf和scanf的返回值
  • 读入的项目数

  • 输出的字符数

  • 在要求严格的程序中,应该判断每次调用scanf或printf的返回值,从而了解程序运行在是否存在问题

#include <stdio.h>

int main()
{
	int num;
	int i1 = scanf("%i",&num);//读入一个1234
	int i2 = printf("%d\n", num);//输出1234第五个是回车换行\n
	printf("%d: %d\n", i1,i2);

	return 0;

}

输出

1234     
1234
1: 5

7.12 文件输入输出

用>和<做重定向
FILE
  • FILE *fopen(const char *restrict path,const char * restrict mode);
  • int fclose(FILE *stream);
  • fscanf(FILE*,...);
  • fprintf(FILE*,...);
打开文件的标准代码
FILE* fp =fopen(“file”,“r”);//文件名,读
if ( fp ) {
    fscanf(fp,...);
    fclose(fp);  //关掉
} else{
...
}
#include <stdio.h>

int main()
{
	FILE* fp= fopen("D:\\dev\\workspaces\\eg001\\12.in", "r");//文件名,读
	if (fp) {
		int num;
		fscanf(fp,"%d",&num);
		printf("%d\n",num);
		fclose(fp);  //关掉
	}
	else {
		printf("无法打开文件\n");
	}


	return 0;
}

输出(txt改为in输出结果一样)

在这里插入图片描述

fopen
r打开只读
r+打开读写,从文件头开始
w打开只写。如果不存在则新建,如果存在则清空
w+打开读写。如果不存在则新建,如果存在则清空
a打开追加。如果不存在则新建,如果存在则从文件尾开始
…x(wx,ax)只新建,如果文件已存在则不能打开

7.13 二进制文件

二进制文件
  • 其实所有的文件最终都是二进制的
  • 文本文件无非是用最简单的方式可以读写的文件(Unix)
    • more、tail
    • cat
    • vi
  • 而二进制文件是需要专门的程序来读写的文件
  • 文本文件的输入输出是格式化,可能经过转码
文本VS二进制1
  • Unix喜欢用文本文件来做数据存储和程序配置
    • 交互式终端的出现使得人们喜欢用文本和计算机 “talk”
    • Unix的shell提供了一些读写文本的小程序
  • Windows喜欢用二进制文件
    • DOS是草根文化,并不继承和熟悉Unix文化
    • PC刚开始的时候能力有限,DOS的能力更有限,二进制更接近底层
文本VS二进制2
  • 文本的优势是方便人类读写,而且跨平台
  • 文本的缺点是程序输入输出要经过格式化,开销大
  • 二进制的缺点是人类读写困难,而且不跨平台
    • int的大小不一致,大小端的问题…
  • 二进制的优点是程序读写快
程序为什么要文件
  • 配置
    • Unix用文本,Windows用注册表
  • 数据
    • 稍微有点量的数据都放数据库了
  • 媒体
    • 这个只能是二·进制的
  • 现实是,程序通过第三方库来读写文件,很少直接读
    写二进制文件了
二进制读写
  • size_t fread(void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream);
    //读入
  • size_t fwrite(const void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream);
    //写入
  • 注意FILE指针是最后一个参数
  • 返回的是成功读写的字节数
为什么nitem?
  • 因为二进制文件的读写一般都是通过对一个结构变量
    的操作来进行的
  • 于是nitem就是用来说明这次读写几个结构变量!
student.h
#pragma once

#ifndef _NODE_H_
#define  _NODE_H_

#define STR_LEN 20
//const int STR_LEN=20;

typedef struct _student {

	char name[STR_LEN];
	int gender;
	int age;
}Student;

#endif
main.c
#include <stdio.h>
#include <malloc.h>
#include "student.h"

void getList(Student aStu[],int number);
int save(Student aStu[],int number);

int main(int argc, char const *argv[])
{
	int number ;
	printf("输入学生数量:");
	scanf("%d", &number);
	//Student aStu[number];
	Student *aStu = (Student*)malloc(sizeof(Student) * number);//分配内存

	getList(aStu, number);
	if (save(aStu, number)) {
		printf("保存成功\n");
	}
	else {
		printf("保存失败\n");
			
	}
	return 0;
}
void getList(Student aStu[], int number)
{
	char format[STR_LEN];
	sprintf(format,"%%%ds",STR_LEN - 1);
	//"%19s"

	int i;
	for (i = 0; i < number; i++) {
		printf("第%d个学生:\n",i);
		printf("\t姓名: ");
		scanf(format,aStu[i].name);
		printf("\t性别(0-男,1-女,2-其他):");
		scanf("%d",&aStu[i].gender);
		printf("\t年龄: ");
		scanf("%d",&aStu[i].age);
	}
}

int save(Student aStu[], int number)
{
	int ret = -1;
	FILE *fp = fopen("student.data", "w");
	if (fp) {
		ret = fwrite(aStu, sizeof(Student), number, fp);
		fclose(fp);

		return ret == number;
	}
}

输出

输入学生数量:20个学生:
        姓名: zhang
        性别(0-男,1-女,2-其他):0
        年龄: 191个学生:
        姓名: li
        性别(0-男,1-女,2-其他):1
        年龄: 20
保存成功

D:\VS\MyProjects\Project1\x64\Debug\Project1.exe (进程 22124)已退出,返回代码为: 0

在这里插入图片描述

在文件中定位
  • long ftell(FILE *stream);
  • int fseek(FILE *stream, long offset, int whence);
    • SEEK_SET:从头开始
    • SEEK_CUR:从当前位置开始
    • SEEK_END:从尾开始(倒过来)
可移植性
  • 这样的二进制文件不具有可移植性
    • 在int为32位的机器上写成的数据文件无法直接在int
      为64位的机器上正确读出
  • 解决方案之一是放弃使用int,而是typedef具有明确
    大小的类型
  • 更好的方案是用文本
main.c
#include <stdio.h>
#include <malloc.h>
#include "student.h"

void read(FILE *fp,int index);

int main(int argc, char const *argv[])
{
	FILE *fp = fopen("student.data", "r");

	if (fp) {
		fseek(fp, 0L, SEEK_END);
		long size = ftell(fp);
		int number = size / sizeof(Student);
		int index = 0;
		while (1) {
		printf("有%d个数据,你要看第几个:", number);
		scanf("%d", &index);
		read(fp, index - 1);
		if (index == 0)break;
		}
		fclose(fp);
	}
	return 0;
}
void read(FILE *fp, int index) {
	fseek(fp, index * sizeof(Student), SEEK_SET);
	Student stu;
	if (fread(&stu, sizeof(Student), 1, fp) == 1) {
		printf("第%d个学生:", index + 1);
		printf("\t姓名: %s\n",stu.name);
		printf("\t性别: ");
			switch (stu.gender) {
			case 0: printf("男\n"); break;
			case 1: printf("女\n"); break;
			case 2: printf("其他\n"); break;
			}
		printf("\t年龄: %d\n", stu.age);
	}
}

输出

2个数据,你要看第几个:11个学生:     姓名: zhang
        性别: 男
        年龄: 192个数据,你要看第几个:22个学生:     姓名: li
        性别: 女
        年龄: 202个数据,你要看第几个:32个数据,你要看第几个:0

7.2 位运算

7.21 按位运算

C有这些按位运算的运算符
  • & 按位的与
  • | 按位的或
  • ~ 按位取反
  • ^ 按位的异或
  • << 左移
  • >> 右移
按位与 &
  • 如果 (x)i == 1 并且(y)i == 1,那么 (x & y)i =1
  • 否则的话 (x & y)i = 0
  • 按位与常用于两种应用:
  • 让某一位或某些位为0:x & 0xFE
  • 取一个数中的一段:x & 0xFF
按位或 |
  • 如果 (x)i == 1 或 (y)i == 1,那么 (x | y)i = 1
  • 否则的话, (x | y)i == 0
  • 按位或常用于两种应用:
    • 使得一位或几个位为1:x | 0x01
    • 把两个数拼起来:0x00FF | 0xFF00
按位取反 ~
  • (~x)i = 1 - (x)i
  • 把1位变0,0位变1
  • 想得到全部位为1的数:~0
  • 7的二进制是0111,x | 7使得低3位为1,而 x & ~7,就使得低3位为0
#include <stdio.h>

int main(int argc, char const *argv[])
{

     unsigned char c=0xAA;
     printf("c=%hhx\n",c);
     printf("c=%hhx\n",(char)~c);//按位取反 
     printf("c=%hhx\n",(char)-c);//补码 

	return 0;

}

c=aa
c=55
c=56
逻辑运算vs按位运算
  • 对于逻辑运算,它只看到两个值:0和1
  • 可以认为逻辑运算相当于把所有非0值都变成1,然后
    做按位运算
  • 5 (101)& 4 (100)—>4(100)
    而 5 && 4 —> 1 & 1 —> 1
  • 5 (101)| 4 (100)—> 5 (101)
    而 5 || 4 —> 1 | 1 —> 1
  • ~4 (100) —> 3 (011)
    而 !4 —> !1 —> 0
按位异或^
  • 如果(x)i== (y)i ,那么(x ^ y)i = 0
  • 否则的话,(x ^ y)i == 1
  • 如果两个位相等,那么结果为0;不相等,结果为1
  • 如果x和y相等,那么x ^ y的结果为0
  • 对一个变量用同一个值异或两次,等于什么也没做
    • x ^ y ^ y —> x

7.22 移位运算

左移 <<
  • i << j
  • i中所有的位向左移动j个位置,而右边填入0
  • 所有小于int的类型,移位以int的方式来做,结果是int
  • x <<= 1 等价于 x *= 2
  • x <<= n 等价于 x *= 2 n
#include <stdio.h>

int main(int argc, char const *argv[])
{

     unsigned char c=0xA5;
     printf("c=%d\n",c);
     printf("c<<2=%d\n",c<<2);//左移2位,等价于*4 



	return 0;

}

左移2位,等价于*4 ( 2 2)

c=165
c<<2=660
右移 >>
  • i >> j
  • i中所有的位向右移j位
  • 所有小于int的类型,移位以int的方式来做,结果是int
  • 对于unsigned的类型,左边填入0
  • 对于signed的类型,左边填入原来的最高位(保持符号不变)
  • x >>= 1 等价于 x /= 2
  • x >>= n 等价于 x /= 2 n
#include <stdio.h>

int main(int argc, char const *argv[])
{

     int a=0x80000000; 
     unsigned int b=0x80000000;
     printf("a=%d\n",a);
      printf("b=%u\n",b);
     printf("a>>1=%d\n",a>>1);//右移1位(符号位不变)
     printf("b>>1=%u\n",b>>1);//右移1位(符号位不变)
	return 0;

}

a=-2147483648
b=2147483648
a>>1=-1073741824
b>>1=1073741824
no zuo no die
  • 移位的位数不要用负数,这是没有定义的行为
  • x << -2 //!!NO!!

7.23 位运算例子

输出一个数的二进制
#include <stdio.h>
int main(int argc,char const *argv[])
{
int number;
//scanf("%d",&number);
number=0x88888888; 
unsigned mask = 1u<<31;//左移31bit 
for(;mask;mask >>=1 ) {//循环完31个bit就结束
printf("%d",number&mask?1:0);
}
printf("\n");
return 0;
}
10001000100010001000100010001000
MCU的SFR(特殊功能寄存器)

Table 70: UARTO Line Control Register (U0LCR - OxE000C00C)

UOLCRFunctionDescriptionResetValue
1:0Word Length Select00:5 bit character length
01:6 bit character length
10:7 bit character length
11:8bit character length
0
2Stop Bit Select(停止位选择)0: 1 stop bit
1: 2 stop bits (1.5 if UOLCR[1:0]=00)
0
3Parity Enable0: Disable parity generation and checking
1: Enable parity generation and checking
0
5:4Parity Select00: Odd parity
01: Even parity
10: Forced “1” stick parity
11: Forced “0” stick parity
0
6Break Control0: Disable break transmission
1: Enable break transmission.
Output pin UARTO TxD is forced to logic 0 when UOLCR6 is active high.
0
7Divisor Latche Access Bit0: Disable access to Divisor Latches
1: Enable access to Divisor Latches
0
控制一个bit
  • const unsigned int SBS =1u<<2;//100
  • const unsigned int PE =1u<<3;//1000
  • U0LCR | =SBS | PE;//bit为1 ,1100
  • U0LCR & = ~ SBS;//清除bit,bit为0,1011
  • U0LCR & =~ (SBS | PE);

7.24 位段(底层硬件操作)

把一个int的若干位组合成一个结构
struct {
    unsugned int leading : 3 ;//后面数字表示前面成员占几个比特
    unsugned int FLAG1 : 1 ;
    unsugned int FLAG2 : 2 ;
    int trailing : 11;
};
#include <stdio.h>
void prtBin(unsigned int number);

struct U0{
unsigned int leading: 3;
unsigned int FLAG1:1;
unsigned int FLAG2:1;
int trailing:27;
};

int main(int argc,char const *argv[])
{
   struct U0 uu;
   uu.leading = 2;
   uu.FLAG1 = 0;
   uu.FLAG2 = 1;
   uu.trailing = 0;
   printf("sizeof(uu)=%lu\n",sizeof(uu));
   prtBin(*(int*)&uu);//把结构当int(不道德) 

return 0;

}

void prtBin(unsigned int number) { 
   unsigned mask = 1u<<31; 
   for ( ;mask; mask >>=1 ) {
   	 printf("%d", number&mask?1:0); 
   }
   
  printf("\n");
	
}
sizeof(uu)=4
00000000000000000000000000010010
  • 可以直接用位段的成员名称来访问
    • 比移位、与、或还方便
  • 编译器会安排其中的位的排列,不具有可移植性
  • 当所需的位超过一个int时会采多多个int
  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吃鱼从来不吐刺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值