嵌入式笔记

** 1:原码、反码、补码**

规定:

正数的原码反码和补码相同

5

0000 0101 负数:-5 0000 0101 最高位为符号位 最高位为 1 代表是个负数 原码: -5 1000 0101 反码:除了符号位 其他位取反 1111 1010 补码:反码 +1 1111 1011 注意:负数在内存中是以补码形式存放的

2:static

是静态的意思

static 可以修饰全局变量、局部变量、函数

存储类型

auto extern re static

3:命名规则

在 c 语言中给变量和函数起名的时候,由字母、数字、下划线构成

必须以字母或者下滑线开头 例 5: int a2;//正确的 int a_2;//正确的 int _b;//正确的 int 2b;// 错误的 int $a2;//错误的

4:基本类型

char 、short int 、int、long int、float、double

 整型 100,125,-100,0  实型 3.14 , 0.125f,-3.789  字符型 ‘a’,‘b’,‘2’  字符串 “a”,“ab”,“1232”

float a= 12.5f;

unsigned long = 232324343443ul;

%m.nf

1.对于输出%m.nf格式:

m表示要输出这个数的宽度,包括小数点,如果实际数值的宽度大于m,则以实际的数据宽度为准,如果实际数值宽度小于m,那么默认右对齐,前面补空格。

n表示小数点后面数据的位数。

public static void main(String[] args) {
        float x=1.456f;  
        System.out.printf("%3.2f",x);
}

输出:1.46

public static void main(String[] args) {
        float x=1.456f;   
    System.out.printf("%13.3f",x);
}

输出: 1.456(前面有8个空格)

2.对于输出%mf格式:

m表示要输出这个数的宽度,包括小数点,*如果实际数值的宽度大于m,则以实际的数据宽度为准,如果实际数值宽度小于m,那么默认右对齐,前面补空格。*

注意:对于浮点型变量,小数点后默认有6位小数。

public static void main(String[] args) {
        double x=1.456;     //实际数据为1.456000
        System.out.printf("%4f",x);
    }

输出:1.456000

字符数据  字符常量: 直接常量:用单引号括起来,如:'a'、'b'、’0’等. 转义字符:以反斜杠“\”开头,后跟一个或几个字符、如'\n','\t'等,分别代表换行、横向跳格. ‘\’表示的是\ ‘%%’ ‘\’’  字符变量: 用 char 定义,每个字符变量被分配一个字节的内存空间 字符值以 ASCII 码的形式存放在变量的内存单元中; 注:char a; a = 'x'; a 变量中存放的是字符'x'的 ASCII :120

即 a=120 跟 a='x'在本质上是一致的.

字符串常量 是由双引号括起来的字符序列,如“CHINA”、”哈哈哈” “C program”,“$12.5”等都是合法的字符串常量. 字符串常量与字符常量的不同 ‘a’为字符常量,”a”为字符串常量 每个字符串的结尾,编译器会自动的添加一个结束标志位'\0',

“a”包含两个字符‘a’和’\0’

5:格式化输出字符:

%d 十进制有符号整数 %u 十进制无符号整数 %x, 以十六进制表示的整数 %o 以八进制表示的整数 %f float 型浮点数 %lf double 型浮点数 %e 指数形式的浮点数 %s 字符串 %c 单个字符 %p 指针的值 特殊应用: %3d %03d %-3d %5.2f %3d:要求宽度为 3 位,如果不足 3 位,前面空格补齐;如果足够 3 位,此语句无效 %03d:要求宽度为 3 位,如果不足 3 位,前面 0 补齐;如果足够 3 位,此语句无效 %-3d: 要求宽度为 3 位,如果不足 3 位,后面空格补齐;如果足够 3 位,此语句无效 %.2f:小数点后只保留 2 位

6:类型转换

1)当表达式中出现了 char 、short int 、int 类型中的一种或者多种,没有其他类型了 参加运算的成员全部变成 int 类型的参加运算,结果也是 int 类型的

例 8:
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("%d\n",5/2);
return 0;
}

如何实现类型转换 默认:整数就是int 小数就是double

short s = 10;(float )

float f= 12.5;(double型)

int a = 12.5; (double型)

        char --> unsigned char  
        ---> short  ---> unsigned short
        ---> int --->   unsigned int
        ---> long --->  unsigned long 
        ---> long long  --->   unsigned long long
        ---> float  ---> double  ---> long double  

2) 当表达式中出现了带小数点的实数,参加运算的成员全部变成 double 类型的参加运算,结果 也是 double 型。

例 9:
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("%lf\n",5.0/2);
return 0;
}

3) 当表达式中有有符号数 也有无符号数,参加运算的成员变成无符号数参加运算结果也是无符 号数.(表达式中无实数)

例 10:
#include <stdio.h>
int main(int argc, char *argv[])
{
int a=-8;
unsigned int b=7;
​
if(a+b>0)
{
printf("a+b>0\n");
}
else
{
printf("a+b<=0\n");
}
printf("%x\n",(a+b));
printf("%d\n",(a+b));
return 0;
}

4) 在赋值语句中等号右边的类型自动转换为等号左边的类型

例 11:
#include <stdio.h>
int main(int argc, char *argv[])
{
int a;
float b=5.8f;//5.8 后面加 f 代表 5.8 是 float 类型,不加的话,认为是 double 类型
a=b;
printf("a=%d\n",a);
return 0;
}

5) 注意自动类型转换都是在运算的过程中进行临时性的转换,并不会影响自动类型转换的变量的 值和其类型

例 12:
#include <stdio.h>
int main(int argc, char *argv[])
{
int a;
float b=5.8f;//5.8 后面加 f 代表 5.8 是 float 类型,不加的话,认为是 double 类型
a=b;
printf("a=%d\n",a);
printf("b=%f\n",b);//b 的类型依然是 float 类型的,它的值依然是 5.8
return 0;
}

强制转换

通过类型转换运算来实现 (类型说明符) (表达式)

功能: 把表达式的运算结果强制转换成类型说明符所表示的类型 例如: (float)a; // 把 a 的值转换为实型 (int)(x+y); // 把 x+y 的结果值转换为整型 注意: 类型说明符必须加括号

例 13:
#include <stdio.h>
int main(int argc, char *argv[])
{
float x=0;
int i=0;
x=3.6f;
i = x;
i = (int)x;
printf("x=%f,i=%d\n",x,i);
return 0;

7:运算符

1、&& 逻辑与 两个条件都为真,则结果为真 if((a>b) && (a<c)) if(b<a<c)//这种表达方式是错误的 2、|| 逻辑或 两个条件至少有一个为真,则结果为真 if((a>b) || (a<c)) 3、! 逻辑非 if(!(a>b)) { }

无论是正数还是负数,编译系统都是按照内存中存储的内容进行位运算。

1、&按位 与 任何值与 0 得 0,与 1 保持不变 使某位清 0 0101 1011& 1011 0100 0001 0000 2、| 按位或 任何值或 1 得 1,或 0 保持不变 0101 0011 | 1011 0100 1111 0111 3、~ 按位取反 1 变 0,0 变 1 0101 1101 ~ 1010 0010 4、^ 按位异或 相异得 1,相同得 0 1001 1100 ^ 0101 1010 1100 0110 5、位移

右移 << 左移 注意右移分:逻辑右移、算数右移 (1)、右移 逻辑右移 高位补 0,低位溢出 算数右移 高位补符号位,低位溢出 (有符号数) A)、逻辑右移 低位溢出、高位补 0 0101 1010 >>3 0000 1011 B)、算数右移: 对有符号数来说 低位溢出、高位补符号位

010 1101 >> 3 1111 010 1 0101 0011 >>3 0000 101 0 总结 右移: 1、逻辑右移 高位补 0,低位溢出 注:无论是有符号数还是无符号数都是高位补 0,低位溢出 2、算数右移 高位补符号位,低位溢出 (有符号数) 注:对无符号数来说,高位补 0,低位溢出 对有符号数来说,高位补符号位,低位溢出 在一个编译系统中到底是逻辑右移动,还是算数右移,取决于编译器

判断右移是逻辑右移还是算数右移
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("%d\n",-1>>3);
return 0;
}
如果结果还是-1 证明是算数右移

(2) 左移<< 高位溢出,低位补 0 5<<1 0000 0101 0000 1010

例 18:
#include <stdio.h>
int main(int argc, char *argv[])
{
int i=3;
int num;
num = (i++)+(i++)+(i++);
printf("num=%d\n",num);
return 0;
}

例 19:
#include <stdio.h>
int main(int argc, char *argv[])
{
int i=3;
int num;
num = (++i)+(++i)+(++i);
printf("num=%d\n",num);
return 0;
}

8:控制语句

注意 if 和 else 之间只能有一条语句,或者有一个复合语句,否则编译会出错

例 22:
if()
语句 1;
语句 2;
else
语句 3;
语句 4;

9:错误:if 和 else 之间只能有一条语句,如果有多条语句的话加大括号

TCP/IP

用来检测网络传输中差错的传输控制协议TCP

专门负责对不同网络进行互联

物理层 数据链路层 网络层 传输层 会话层 表示层 应用层

物 数 网 传会 表 应

扩展:如果在一行中定义多个指针变量,每个指针变量前面都需要加*来修饰 int p,q;//定义了两个整型的指针变量 p 和 q int * p,q;//定义了一个整型指针变量 p,和整型的变量 q

c 语言规定:数组的名字就是数组的首地址,即第 0 个元素的地址,就是&a[0],是个常量。

int a[5]; int *p; p=a; *(p+2)=100;//也是可以的,相当于 a[2]=100

p 是第 0 个元素的地址,p+2 是 a[2]这个元素的地址。 对第二个元素的地址取值,即 a[2]

10:数组名字取地址:变成 数组指针

一维数组名字取地址,变成一维数组指针,即加 1 跳一个一维数组 int a[10]; a+1 跳一个整型元素,是 a[1]的地址 a 和 a+1 相差一个元素,4 个字节 &a 就变成了一个一维数组指针,是 int(*p)[10]类型的。 (&a) +1 和&a 相差一个数组即 10 个元素即 40 个字节。

引用数组元素回顾: a[2]、*(a+2)、p[2]、

*(p+2) 都是对数组 a 中 a[2]元素的引用。

11:sizeof strlen的区别

sizeof 它的功能是: 获得保证能容纳实现所建立的最大对象的字节大小(包括\0)。

strlen(只与字符串相关)只关心存储的数据内容,不关心空间的大小和类型。sizeof可以用类型做参数,strlen只能用char*做参数,且必须是以''\0''结尾的。 (求字符串长度) sizeof还可以用函数做参数,比如: short f(); printf("%d\n", sizeof(f())); 输出的结果是sizeof(short),即2。 例子

char str[20] = “0123456789”; int a = strlen(str); int b = sizeof(str);

这时的 a = 10,b = 20;因为strlen计算的是字符串的长度,以’\0’为字符串结束标志;而sizeof计算的是分配的数组str[20]所占的内存空间的大小,不受里面存储的内容影响。

char *str1 = “abcde”; char str2[] = “abcde”; char str3[8] = {‘a’}; char ss[] = “0123456789”;

其计算结果为: sizeof(str1) = 4; sizeof(str2) = 6; sizeof(str3) = 8; sizeof(ss) = 11;

str1 是一个指针,只是指向了字符串"abcde"而已。所以sizeof(str1)不是字符串占的空间也不是字符数组占的空间,而是一个指针所占的空间。在C/C++中一个指针占四个字节。 str2是一个字符型数组,对于一个数组,返回这个数组所占的总空间,所以sizeof(str2)取得的是字符串"abcde"的总空间。"abcde"中,共有a b c d e \0六个字符,所以str2数组的长度时6,。 str3已经定义成了长度为8的数组,所以sizeof(str3)为8; str4和str2类似,共十一个字符,所以ss所占的空间是11.

12:数组

数组除去数组名就是数组类型

int arr[10];
int [10]类型
注意 下面两个描述都能表示
arr[2]-->*(arr+2);
2[arr]-->*(2+arr);

一旦数组被定义,那么只能通过下标的方式 存入数据

二维数组

初始化有两种方式(把一行看成一个元素)

int  [2][2] = {1,2,3,3};
int  [2][2] = {{1,2},{3,3}};
int  [][2] = {2,2,3,4};
可以没有行不能没有列
存储机制是先存完一行,再接着存一行
 第一行首地址a[0] 
 第二行首地址a[1]
 

数组名是首元素地址但是有两个是例外

sizeof(arr) 这里的arr 数组名表示整个数组,sizeof(数组名)计算的是整个数组的大小,单位是字节

& 数组名,数组名代表整个数组,&数组名,取出的是整个数组名的地址

&arr

13: #表示这是一条预处理 ,凡是以#开头的均为预处理命令

文件包含 宏定义 条件编译

14:文件包含

文件包含语句的功能是把指定的文件插入该语句行位置

一般形式为

#include""
#include<>

#include <>  和 "" 区别:
	<> 去系统提供的路径下去寻找   /usr/include   ...
	"" 去自己定义的路径下寻找,如果没有找到,就去系统提供的路径下寻找
		#include "带路径的.h文件"
		例子:
			#include "~/xx/yy/include/zz.h"

使用尖括号表示在系统头文件目录去查找,双引号表示首先在当前源文件目录去找,若未找到才到系统头文件目录中去找

15:gcc gdb makefile

gcc

gcc  -v  -Wall   -O0  -g 
    -E      //预处理
    -S      //编译
    -c      //汇编 
    -o      //生成
    -v      //详细显示
    -Wall   //详细警告
    -On     // n是个数字 优化程度
            -O0  不优化 
            -O1  
            -O2 
            数字越大,优化程度越高,编译时间越长,执行时间越短
    -g      // 可以进行 gdb 调试
    
    

gdb

gdb 调试 :   (运行时候错误查找)
    * 安装gdb  
        sudo apt-get install gdb 
    *编译的时候,后面要加 -g  
    *为什么要用?
        一步一步调试,查找段错误的位置
    *如何使用:
        gdb 可执行文件  
        例子: 
            gdb xx  
        l  :  list显示代码
        r  :  run 运行 
        start  : 启动 
                n  : next  不进入函数,直接执行
                s  : step  进入到每一个函数里面   
        p 变量名字 : printf 变量
            例子 :
                p i  打印出i的值     
        b n : breakpoint 
                n: 行 
            例子:
                b 15        
        i b : infomation breakpoint 
            查看所有的断点
        d 编号 : delete  num
            例子:
                d 3   //删除编号为3的断点   
        c  : continue 继续   一个断
        q : quit : 退出

make

make:
    *> make是一个项目管理工具
    *> 使用:
        *》安装make 
            sudo apt-get install make 
        *》书写 Makefile 的文件
        *》按照make的规则,在Makefile中书写 代码
    *Makefile基本姿势:
        目标 : 依赖 
        (tab键) 命令  
        *声明:
            依赖可以没有
            命令也可以没有 
            目标就是我们要生成的东西    
            如果不需要生成目标,称该目标为伪目标  (.PHONY:clean)
    *make的使用:
        make 目标 :
            查看该目标文件是否存在,如果存在是否为最新。
            如果不存在,则去生成,如果不需要生成,则该目标为伪目标
        如果目标是整个Makefile中的第一个目标,
        可以省略不写
    *》优势:时间戳管理方式:
        如果依赖没有目标新,那么就不需要重复编译,否则谁最新就编译谁
    *》依赖文件必须存在,如果依赖不存在,make想方设法去生成(将其作为目标去生成)
​
        cc -c -o xx.o xx.c 
        这是 Unix操作系统(linux)默认的编译方式 
        gcc -c -o xx.o xx.c
        这是GNU组织的c语言编译器
            sudo apt-get install gcc
            sudo apt-get install gdb
            sudo agt-get install make 
    *伪目标:
        我们不需要生成该文件,如果在项目中有该文件,则该伪目标由于没有依赖,永远是最新,顾不再执行,解决方式  
            .PHONY : 伪目标 
* 变量赋值:
    * 简单赋值  
        :=
        在当前行有效
    * 递归赋值
        =  
        递归查找某变量的最后一个值
    * 条件赋值 
        ?=
        如果某变量已经定义,那么该语句无效
    * 追加赋值
        += 
        对某个变量进行追加操作
* 如果想隐藏命令(只执行,不显示),在命令前面加上@
    echo : 打印的命令    
* 
    $^ ---> 代表所有的依赖 
    $@  ---> 代表目标
    $<  ---> 代表第一个依赖
    
    1. 
    make -C  文件夹(目录)
    -----》执行该 目录 下面的 Makefile下的第一个目标
    
    make -C  文件夹(目录)  目标
    -----》执行该 目录 下面的 Makefile下的目标
======================
内联函数:
    不是函数
    写在 .h 文件中 
    如果有人调用该函数,将其函数体全部拷贝过去

16:进制

进制不过是对数的描述(就像一个人有多个名字)

一个整数四种表示

八进制 标志:0(零)

十六进制 标志 :0x 0X

 十->二

 十->八
 
 十->十六

 以上都是除X逆向取余

二->十(权位相加)

八->十(权位相加)

十六->十(权位相加)

二进制运算

    %d   ----->  以 int 类型输出
    %hd  ----->  以 short 输出
    %hhd ----->  char类型的10进制输出
    %ld  ----->  long
    %lld ----->  long long 
    %c   ----->  以字符形式打印
    %s   ----->  字符串 
    %x   ----->  16进制打印     不带标志位0x
    %o   ----->  8进制打印      不带标志位0
    %#x   ----->  16进制打印    带标志位0x     !!!!!
    %#o   ----->  8进制打印     带标志位0      !!!!
​
    %u    ----->   unsigned int 
    %lu   ----->   unsigned long 
    %llu  ----->   unsigned long long
    %hu   ----->   unsigned short
    %hhu  ----->   unsigned char 
        
    %f   ----->  float      保留6位小数         !!!!
    %lf  ----->  double     保留6位小数         !!!!
    %g   ----->  float double  保留有效        !!!!
    
    float f = 0.00009000;
    printf("%g\n",f);
    输出 9e-05
    float f = 12345678;
    printf("%g\n",f);
    输出1.23457e+07
    %g用于打印浮点型数据时,会去掉多余的零,至多保留六位有效数字(不同于%e的默认保留小数点后6位)
​
1. 当%g用于打印超过6位的浮点型数据时,因为精度问题,%f不得不输出一个不精确的超过六位的数字,%e也是同样,而%g此时会选择%e格式进行输出,并且按第一条要求,去掉多余的零,并且四舍五入到6位数字。这是《C Primer Plus》中所说的超过精度的时候的情况。  (可见,这个6位,是按float类型精度来计算的。)
​
2. 当一个数字的绝对值很小的时候,要表示这个数字所需要的字符数目就会多到让人难以接受。举例而言,如果我们把π*10^-10写作0.00000000000314159就会显得非常丑陋不雅,反之,如果我们写作3.14159e-10,就不但简洁而且易读好懂。当指数是-4时,这两种表现形式大小相同。对于比较小的数值,除非该数的指数小于或者等于-5,%g才会采用科学技术发来表示,即,以%e的格式进行输出。
​
* 浮点型变量存储
    单精度: float          ----》 4个字节
            1个符号位  8个指数位   23个小数位
        float xx = -25.5;
        25.5 ---> 二进制: 11001.1 --->  1.10011*2^4
        1个符号位: 1
        8个指数位: 01111111 + 4  ---》 10000011
        23个小数位: 10011000000000000.....
        1 10000011 10011000000000....

双精度: double ----> 8个字节 (64个位) ​ 1个符号位 : 0正数 1负数 ​ 11个指数位 : 01111111111 ----》 指数0 ​ 52个小数位 : ​ 例子:(小数存储) ​ double d = 520.250; ​ 520 ---> 1000001000 ​ 0.25 ---> 0.01 ​ ====> 1000001000.01 ---> 1.00000100001*2^9 ​ 符号位: 0 ​ 指数位: 01111111111 + 9 ---》 10000001000 ​ 小数位: 00000100001000000000...... 52位 ​ 520.250的存储为: ​ 0 10000001000 00000100001000000000......

17:配置网络

(18以下)

sudo /etc/network/interfaces

sudo vim /etc/netplan/*.yaml(18版本以上)

18:;是一个语句(空语句)

else 离他最近的if相匹配

为了避免出错加花括号

if 语句的时候() 里面有常量 与变量比较的时候

格式(4 == num)这样写比较好 容易看出问题

break 跳出本层循环

continue 跳出本次循环(后面代码不执行)

输入缓冲区

19:for循环与while的区别

for(初始化;判断;调整)简单明了

while有可能迷失条件

for循环注意

1:不可在for循环体内修改循环变量,防止for循环失去控制

2:建议for语句的循环语句控制变量的取值采用“前闭后开区间”写法

	  int ret,ch;
	  char password[20] = {0};
	  printf("请输入密码:>");
	  scanf("%s",password); 
	  while((ch = getchar()) != '\n')
	  {
		  ;
	  }
	  printf("请输入(Y/N)");
	  ret = getchar();
	  if(ret == 'Y')
	  {
		   printf("确认成功");
	  }
	  else
	  {
		  printf("放弃确认\n");

3;一些for循环的变种

for(;;)死循环

for (x = 0, y = 0; x<2 && y<5; ++x, y++)

表达一条语句{}

三角形

	*
   ***
  *****
 *******
*********
#include<stdio.h>
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n-i;j++)
			printf(" ");
		for(int k=1;k<=2*i-1;k++)
			printf("*");
		printf("\n");
	}
	
	return 0;
}

倒三角形

*********
 *******
  *****
   ***
    *
#include<stdio.h>
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=n;i>0;i--)
	{
		for(int j=1;j<=n-i;j++)
			printf(" ");
		for(int k=1;k<=2*i-1;k++)
			printf("*");
		printf("\n");
	}
	return 0;
}

菱形

#include <stdio.h>
​
int main()
{
    int n,i,j,k;
    scanf("%d",&n);
    n=n/2+1;
    for(i=0; i<n; i++)//控制行数,上三角
    {
        for(j=1; j<n-i; j++)//控制前置空格,第n行前置空格为n-1
            printf("  ");//这里打印两个空格,因为每个*号后面有一个空格
        for(k=0; k<=2*i; k++)//控制输出的*,第n行有(2*n-1)个*
            printf("%c ",'*');
        putchar('\n');
    }
    for(i=1; i<n; i++)//下三角
    {
        for(j=0; j<i; j++)
            printf("  ");
        for(k=2*(n-i-1); k>=0; k--)
            printf("%c ",'*');
        putchar('\n');
    }
    return 0;
}
​

20:折半查找算法 二分查找算法

21:函数

void 函数名(类型名 ,类型名 )

return 0;可以省略

桌面显示方法

结束资源管理

一些函数

strcpy

比较

值传递

址传递

bzero(&a,4);

size_t

memset

strlen(字符串计数)

strate

const ---->修饰的变量不能改变

a的值在内存volitile(易变值防止cpu优化)

32位

4G虚拟内存怎么算的

一堆指令的集合a.out

char arr[30] = "ejfofif";

char *p = "fewofjf";(存在常量区)

const char p[20] ="fjfief";(在栈空间)

分页

(int *)malloc (9)//不被允许这样写(12)

22:指针

指针减去指针 等于指针相差个数

指针数组

int  a = 10;
int  b = 20;
int  c = 30;
int *arr[3] = {&a,&b,&c};(指针数组)
int i= 0;
for(i = 0;i<3;i++)
{
	printf("%d ",(arr[i]));
}
return 0;

字符数组

char * p = "cuffjfrf";

int (*a[10])[10]

int (*)[10]

去掉名字就是类型

函数传参

一维数组

  • void test(int arr[]);
  • void test(int arr[10];
  • void test(int *arr);//指针形式

int arr[10] ={0};

test(arr);

  • void test2(int *arr[20]);
  • void test2(int **arr);//因为数组元素为指针而数组名就是数组首地址所以一个指针的地址要用二级指针来接 int *arr2[20] = {0};

test2(arr2);

二维数组

  • void test(int arr[3 ][5);
  • void test(int arr[ ][5);
  • void test(int (*p)[5])

int arr[3][5 = {0};

test(arr);

函数指针

&函数名 和 函数名 都是函数的地址

int Add (int x,int y )
{
	;
}
int (*pa)(int,int) = Add;
int (*pa1)(int,int) = &Add;

int (*p)(int);

一些有趣的题目

  代码1
  (*(void (*)())0)();
  解:把0强制类型转换成:void(*)()函数指针类型  0就是一个函数的地址
  调用0地址的该函数
  
  代码2
   void (*signal(int,void(*)(int)))(int);
   一个函数 signsl参数为int型 和函数指针型 返回值是一个函数指针
   

23:结构体

1》概念: 自己定义的类型

2》姿势:

struct{结构成员;结构成员;};
struct{int len;int h;int w;}  xx;
			xx.len = 20;
			xx.h = 160;
			xx.w = 160;
		
		
		struct 结构标记{结构成员;结构成员;};
		*结构标记 结构成员 及变量名字可以相同
			例:
				struct x{int x;};	struct x x;	---	没有问题
				
				
				struct x{int x;} xx; 
				struct x xx;
					----》 以上两种方式是一样的




声明一个类型
struct xx {
int len ;
int h;
int w;
};

struct{int len;int h;int w;} xx;
xx.len
xx.h
xx.w

struct 结构标记{结构成员;结构成员;};
struct x{int  x;};   struct x x;
以上两种都可以定义变量

声明一个类型
struct  mm{
	int h;
	int w;
	int xw;
	int yw;
	int tw;
	char name[20];
	char addr[20];
}

struct mm sm;


strcpy (sm.name,"小郭子");
sm.h = 159;
sm.w = 234;
.....


struct mm *pp = &sm;
pp->h = 159;
printf("%d",pp->h);

面向对象  

面向过程  去北京  一步一步

8》结构体算字节数

* 32位操作系统最大按照4字节对其
* 64位操作系统最大按照8字节对其
* 每个数据成员存储的起始位置是自身大小的整数倍
* 结构体总大小(也就是sizeof的结果),必须是该结构体成员中最大的对齐模数的整数倍

9》手动修改结构体的对其方式 #pragma pack(n) n为 2的次方数 1 2 4 8 16 32

指针-> 结构体.

typedef struct Stu
{
     //成员变量
     char name [20];
     short age;
     char tele[12];
     char sex[5];
}Stu;
void Printf1(Stu temp)
{
    printf("name: %s\n",temp.name);
    printf("age: %d\n",temp.age);
    printf("tele: %s\n",temp.tele);
    printf("sex: %s\n",temp.sex);
}
​
void Printf(Stu *ps)//结构体指针取值用->
{
    printf("name: %s\n",ps->name);
    printf("age: %d\n",ps->age);
    printf("tele: %s\n",ps->tele);
    printf("sex: %s\n",ps->sex);    
}
​
int main()
{
    Stu s = {"李四",40,"32342344","男"};
    Printf1(s);
    Printf2(&s);
    return 0;
}

位域(节省内存)

1》概念: 带有预定宽度的变量称为位域

2》姿势: struct 标签{ 位域类型 位域变量名:位域宽度; 位域类型 位域变量名:位域宽度; };

	
		例子:
		struct xx{
			int a:2;			//占2位     00  01  10   11
			unsigned b:4;		//占4个位
		};

3》位域类型: 位域类型为整型 例: char short int unsigned int(简写为unsigned)

4》位域宽度:

不能超过该类型的的大小
		例子:
			int a:33 	//错误
			
		赋值时不能超过该类型的承受范围
		例子:
			unsigned char a:2  // 那么a只能存 0  1  2   3  这几个数字
			a = 5	;			//错误

4》位域对齐: 位域类似结构体,按照最大类型对齐

5》空域:
        位域宽度为0的位域称为空域,其作用是:该次对齐空间剩余部分全部补0
        例子:
        struct xx{
            int b:1 ;
            int :0  ;   // 32位对齐,b用了1位,所以该处共有31个0
            int c:3 ;   // 新的对齐行
        }
        
    6》无名位域:
        位域没有名字,仅仅占用空间,无名位域不能调用,其作用是隔开两个位域
        例子:
        struct xx{
            unsigned a:2
            unsigned: 3     //无名位域,将a和c隔开3个位,该3个位不能使用
            unsigned c:2 
        }
        
    7》位域大小计算(存储规则):
        1>如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止
        
        2>如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍
        
        3>如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++,GCC采取压缩方式
        
        4>如果位域字段之间穿插着非位域字段,则不进行压缩

联合体:

1》概念: 不同类型变量公用一块空间

2》姿势: union{ 变量类型 变量名; 变量类型 变量名; 变量类型 变量名; }; 2.1》联合体取别名: typedef union xx ux;

	union 标签{
		变量类型 变量名;
		变量类型 变量名;
		变量类型 变量名;
	};

不同类型变量共用一块空间

union {
double s;
int a;
short s;
}

枚举(实质是宏定义):

1》定义:
        enum 标签{成员,成员,成员};
        例子:
            enum WeekDay{MONDYA,TUEDAY,WENDAY,TURDAY,FRIDAY,SATDAY,SUNDAY};
            enum WeekDay day;
            
        枚举默认从0开始
        enum WeekDay{MONDYA,TUEDAY=3,WENDAY,TURDAY,FRIDAY=6,SATDAY,SUNDAY};
            WENDAY为4
            
    2》赋值:
        day = MONDAY;
        scanf("%u",&day);
        
    3》使用:
        enum BIT{SHINENG=1,XUANZE=3};
            ----    可以用于对配置寄存器配置操
            
#include<stdio.h>
emun js{OYNN=1,CXK,MBG,YYQX,PLX,CYW,ZLS};
int main (void)
{
    printf("请选择你的技师:\n");
    printf("1.OYNN,2.CXK,3.MBG,4.YYQX,5.PLX,6.CYW,7.ZLS\n");
    enum js ej;
    scanf("%u",&ej);
    swich(ej)
    {   
        case OYNN:
            printf("OYNN为您服务\n");
            break;
        case CXK:
            printf("CXK为您服务\n");
            break;
        case MBG:
            printf("MBG为您服务\n");
            break;
        case YYQX:
            printf("YYQX为您服务\n");
            break;
        case PLX:
            printf("PLX为您服务\n");
            break;
        case CYW:
            printf("CYW为您服务\n");
            break;
        case ZLS:
            printf("ZLS为您服务\n");
            break;
    }   
    return 0;
}

条件编译

if else if else

#if #elif #else #endif

基本姿势

#if 0

printf("ff");

#endif

递归:

自己调自己

fun(){
​
fun();
​
}  
------>设置退出的条件
​
例子
​
#include <stdio.h>
​
float my_power(float f,int n);
​
int main(void)
{
    float f;
    int n;
    scanf("%f %d",&f,&n);
    
    printf("%f\n",my_power(f,n));
    
    
    return 0;
}
​
/*
float my_power(float f,int n)
{
    if(n<0){
        f = 1/f;
        n = -n;
    }
    
    int i;
    float sum = 1.0;
    for(i=0;i<n;i++)
        sum *=f;
    
    return sum;
}
*/
​
​
float my_power(float f,int n)
{
    if(n==0)
        return 1.0;
    
    if(n<0){
        f = 1/f;
        n = -n;
    }
    
    return f*my_power(f,n-1);
}

24:数据的存储

大端存储,数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中;

小端存储,数据的低位保存在内存的低地址中,而数据的高位,保存在内存的高地址中;

逗号表达式

表达式结果为最后表达式结果

25:指针的进阶

字符指针

指针数组(是数组)

int arr[10] = {0};//整形数组
char ch[5]  = {0};//字符数组
int *parr[10] ;//存放整形指针的数组 - 指针数组
char *pch[7] ;//存放字符指针的数组 - 指针数组

指针数组用法

int arr1[] = {1,2,3,4,5,6,7};
int arr2[] = {2,3,4,5,6,7,8};
int arr3[] = {3,4,5,6,7,8,9};
int *parr[] = {arr1,arr2,arr3};
for(int i = 0;i<5;j++)
{
    int j = 0;
    for(j = 0;j<5;j++)
    {
        printf("%d",*(parr[i] +j))
    }
​
}

数组指针

指向数组的指针 -存放数组的地址(对一维数组不友好)

int arr[10] = {1,2,3,4,5,6,7,};
int (*p)[10] = &arr;//数组指针
​
char *arr[5];
char*(*pa)[5] = &arr;

二维数组的首地址得到的思路过程

首先要把二维数组看出一维数组(每行看成一个元素)

再取他的首地址得到一个一维数组的地址

1:linux基础

linux 是一种操作系统

为什么是linux

嵌入式(代码操作硬件)

2:基本地行模式命令

x 保存并退出

w hello.c 将编辑内容存到hello.c上

vs hello.c 分屏编辑

set nonu 临时取消行号

%s/nihao/hello/g 将文件全文的nihao变成hello

s/nihao/hello/g 将光标所在行nihao变成hello

.,$ s/nihao/hello/g 光标当前行到末尾行

/hello 去文本搜索hello字符 :r 文件 直接加载文件

set key=XXXXXX 设密码

3:配置vim:

sudo vim /etc/vim/vimrc ​ ​

                            set mouse=a         //鼠标控制光标
                            set cindent         //C语言的格式
                            set number          //行号
                            set st=4        
                            set sw=4
                            set sts=4           //tab键的长度   
​
                vim的固件
                        一键生成main函数
                    map mn i#include <stdio.h><Esc>o<Esc>oint main(void)<Esc>o{<Esc>oreturn 0;<Esc>o}<Esc>2ko

4:软件包管理

软件包存放的路径:cd /var/cache/apt/archives

        apt-get -h:
        update---更新
        install--安装
        remove---卸载

5: ubuntu软件源的更换

URL链接---软件商店的仓库:Index of /ubuntu ​ ​

                更换源操作:
                    1:切换到源的目录
                    cd /etc/apt
                    
                    2:把默认源备份
                    sudo cp sources.list sources.list.back
                    
                    3:网上搜索18.04国内源:中科大源,阿里源,清华源,腾讯源,163源
                    sudo vim sources.list
                
                    4:把list里面的默认源清空,添加复制的国内源
                        保存并退出

​ 5:执行: ​ sudo apt-get update

6:用户切换

            切换成超级用户:sudo su 
            切换成其他用户:su 用户名

7:标准文件的重定向(了解)----应用层:IO编程 ---输入/输出编程

                        在linux中,系统打开时,就会有三个标准文件打开
                    标准文件                文件描述符                   介绍
                    标准输入                    0                       从键盘输入
                    标准输出                    1                       从屏幕输出
                    标准错误输出                 2                       从屏幕输出(错误信息)
                
                    重定向:
                    >                   //输出重定向,新建模式            ls>1.c(会把ls输出的东西重定向输出到1.c)
                    >>                  //输出重定向,追加模式            ls>>1.c(会把ls输出的东西以追加的方式重定向输出到1.c)
                    <                   //输入重定向                 wc -w<1.c(把1.c里面的内容输入重定向到wc -w中)
                    2>                  //错误输出重定向               gcc 1.c 2> 2.c(将gcc 1.c的错误信息重定向到2.c中)

8:挂载

概念: linux中,根目录以外的文件想要被访问,需要将其"关联"到根目录下的某个目录来实现, 这种关联的操作就叫"挂载",这个被挂载的目录叫"挂载点",解除本次挂载的操作叫卸载。

                            注意:挂载点的目录需要有一下几个条件:
                                    1,目录需要事先存在,可以用mkdir命令新建目录
                                    2,挂载点目录不可以被其他进程使用
                                    3,挂载点原有的文件将会被隐藏

​ 查看ubuntu硬盘信息:cat /proc/partitions ​ 8 0 20971520 sda ​ 8 1 20969472 sda1 ​ ​ sd*X: ​ *:a b c d ​ ubuntu的第几快硬盘 ​ sda 第一块硬盘 ​ sdb 第二块硬盘 ​ sdc 第三块硬盘

​ X:1 2 3 4 ​ ubuntu的第几快硬盘的第几分区 ​ sda1 第一块硬盘的第一分区 ​ sda2 第一块硬盘的第二分区 ​ sda3 第一块硬盘的第三分区 ​ sdb1 第二块硬盘的第一分区 ​ ​ 1,cd到mnt目录下 ​ cd /mnt ​ ​ 2,创建挂载点 ​ mkdir myusb ​ ​ 3,查看ubuntu硬盘信息:cat /proc/partitions ​ 查看sda1是否在 ​ ​ 4,插上U盘,选择连接虚拟机,不要记住选择!!!!!!!!! ​ 再次执行:cat /proc/partitions ​ sdb ​ sdb1 ​ ​ 5,确认sdb是插上U盘才出现的,证明是U盘 ​ ​ 6,挂载操作,在mnt这个目录下操作 ​ sudo mount /dev/sdb1 /mnt/myusb ​ ​ 7,ls查看一下。信息是否对应

​ 卸载U盘:umount ​ 前提是在mnt目录下 ​ 卸载:sudo umount myusb

9:用户管理,进程管理

		在字符界面
			有三个用户配置清单的文件
			/etc/sudoers 	//用户的权限
			/etc/passwd 	//用户密码
			/etc/shadow 	//用户信息

1,修改用户密码-----passwd 用户名

2,新建一个用户-----adduser 用户名

3/给新建的用户添加权限 //不要乱玩,配置这个之前,最好保存一份快照

sudo vim /etc/sudoers

    在第21行添加
    XXXXXXX ALL=(ALL:ALL) ALL

4/修改用户名

tonlyfucku@ubuntu:/home/farsight$ su farsight
Password: 
farsight@ubuntu:~$ sudo vim /etc/sudoers
farsight@ubuntu:~$ sudo vim /etc/passwd        //在三个文件底行模式都执行:%s/xxx/xxxxx/g
farsight@ubuntu:~$ sudo vim /etc/shadow
farsight@ubuntu:/home$ sudo mv XXX XXXX

5/删除用户----两种方法:第一种是命令删除 ---userdel 用户名 第二种是操作删除

                    操作删除:
                        farsight@ubuntu:~$ sudo vim /etc/sudoers
                        farsight@ubuntu:~$ sudo vim /etc/passwd   //在三个文件分别清空用户信息
                        farsight@ubuntu:~$ sudo vim /etc/shadow
                        farsight@ubuntu:~$ cd ..
                        farsight@ubuntu:/home$ sudo rm -rf tonly  //删除用户目录
                        第一种是命令删除:
                        sudo userdel tonly
                        被某个进程占用 XXXX
                        先执行kill -9 XXXX

ps -aux ----查看进程(静态)

USER:进程拥有者 PID:进程的ID号 %CPU:CPU占有率 %MEM:物理内存的占有率 VSZ:虚拟内存的占有率 RSS:内存的占用大小 TTY:终端 STAT:当前进程的状态 D:不可中断的静止 R:正在执行中 S:阻塞状态 T:暂停执行 Z:不存在,但暂时无法消除 W:没有足够的内存可分配 <:高优先级的进程 N:低优先级的进程 L:有足够的内存分配并锁在内存中 I:空闲状态

10:进程

farsight@ubuntu:~$ ls -l
total 55172
-rw-r--r--              1           farsight    farsight      157   Jul 28 01:34            1.c
文件类型与权限         硬链接数    文件拥有者   文件所属组   文件的大小   文件的最近修改时间   文件名
            
            文件类型与权限
                -rw-r--r--一共10位
                10=1+3+3+3
                1:即10位的开头位,代表文件的类型
                第一个3:文件拥有者的权限
                第二个3:文件所属组的权限
                第三个3:其他用户的权限
                
        文件类型            标识
        普通文件               -    
        块设备文件             b 
        字符设备文件           c 
        目录文件              d 
        符号连接文件           l 
        命名管道              p                 ---多进程线程,网络编程
        套接字                s                 ---网络编程的域通信

​ ​ 文件权限 ​ rw- r-- r-- ​ r:读的权限 ​ w:写的权限 ​ x:执行的权限 ​ ​ 在linux当中,权限一般用八进制表示 ​ rw- r-- r-- ​ 八进制:0 6 4 4 ​ 二进制: 110 100 100 ​ ​ 修改权限的命令:chmod 权限 文件名字 ​ chmode 0777 test ​ 0 7 7 7 ​ 0 111 111 111 ​ 八进制:rwx rwx rwx

11:文件压缩与解压

                    
文件压缩和解压
            用户在进行数据备份时,需要把若干文件整合为一个文件便于保存。尽管整合为一个
            文件进行管理,但文件的大小依旧没有改变。如果需要网络传输文件,就希望将其压缩
            成更小的文件,以节省在网络传输的时间,且更好的保护文件不损坏
            
            
            归档:是将一组文件或目录保存在一个文件中。
            压缩: 也是将一组文件或目录保存一个文件中,
                    并按照某种存储 格式保存在磁盘上,所占磁盘空间比其中所有文件总和要少。
                    
            压缩工具                解压工具            文件拓展名
            gzip                    gunzip              .gz 
            bzip2                   bunzip2             .bz2 
            zip                     unzip               .zip
            
            
            tar-----考试会考
            
            压缩和解压的操作
            gzip:只能操作一个文件
            farsight@ubuntu:~/test$ gzip 1.c 2.c 3.c
            farsight@ubuntu:~/test$ ls
            1.c.gz  3.c.gz     guo_1.c  guo_3.c    guo_a.c  guo_c.c
            2.c.gz
            
            解压:
            farsight@ubuntu:~/test$ gunzip 1.c.gz 
            farsight@ubuntu:~/test$ ls
            1.c     3.c.gz  5.c  guo_123.c  guo_2.c  guo_abc.c  guo_b.c
            2.c.gz  4.c     6.c  guo_1.c    guo_3.c  guo_a.c    guo_c.c
            farsight@ubuntu:~/test$ gunzip 2.c.gz 
            
            
            bzip2:
            farsight@ubuntu:~/test$ bzip2 1.c 2.c 3.c
            farsight@ubuntu:~/test$ ls
            1.c.bz2  3.c.bz2  5.c  guo_123.c  guo_2.c  guo_abc.c  guo_b.c
            2.c.bz2  4.c      6.c  guo_1.c    guo_3.c  guo_a.c    guo_c.c
​
            bzip2解压
            farsight@ubuntu:~/test$ bunzip2 1.c.bz2 2.c.bz2 3.c.bz2 
            farsight@ubuntu:~/test$ ls
            1.c  3.c  5.c  guo_123.c  guo_2.c  guo_abc.c  guo_b.c
            2.c 
            
            zip:可以压缩多个文件,更像我们windows的压缩
            farsight@ubuntu:~/test$ zip 123.c.zip 1.c 2.c 3.c
                adding: 1.c (stored 0%)
                adding: 2.c (stored 0%)
                adding: 3.c (stored 0%)
                farsight@ubuntu:~/test$ ls
            123.c.zip  2.c  4.c  6.c        guo_1.c  guo_3.c    guo_a.c  guo_c.c
            1.c        3.c
​
            zip解压:
            farsight@ubuntu:~/test$ ls
            123.c.zip  5.c  guo_123.c  guo_2.c  guo_abc.c  guo_b.c
            4.c        6.c  guo_1.c    guo_3.c  guo_a.c    guo_c.c
            farsight@ubuntu:~/test$ unzip 123.c.zip 
            Archive:  123.c.zip
                extracting: 1.c                     
                extracting: 2.c                     
                extracting: 3.c                     
            farsight@ubuntu:~/test$ ls
            123.c.zip  2.c  4.c  6.c        guo_1.c  guo_3.c    guo_a.c  guo_c.c
            1.c        3.c 
            
            gzip/bzip2/zip:都没有办法归档
            
            tar---集归档和压缩/解压为一体
                    主要用于将若干文件或目录合并为一个文件,以便 备份和压缩
                    
            详解:
        tar         -cvjf           123.bz2             1.c 2.c 3.c 
        命令      选项          压缩后的压缩包名字   需要压缩的文件
                    -v :显示压缩和解压的过程
                    -cf:压缩文件
                    -j :以bzip2的压缩格式进行压缩(.bz2)
                    -z :以gzip的压缩格式进行压缩(.gz)
                    -xf:解压文件
                    
        tar -cvjf :以bzip2的压缩格式进行压缩
        
        tar -cvzf :以gzip的压缩格式进行压缩
        
        tar -xvf :解压命令(解压任何格式的压缩包)

12:硬链接

            什么是链接文件?(windows)
            概念:
                链接文件就类似我们windows的快捷方式,只保留本体文件的地址,而不占用存储空间
                使用链接文件与使用本体文件的效果是一样的。
                
            为什么要使用链接文件?
                1/比如说:在windows都会把文件放到一个较大的盘里,我们每次需要用的时候,就要到盘里
                    去文件夹里找,这样的操作比较麻烦
                现在使用linux的字符界面,是不是更麻烦?
                
                2/  你做某个东西的时候,需要在意文件的安全和技术要点。
                    比如做了一个app拿去销售,你同时要保护app的专利,就可以给app创建一个链接文件,
                    给这个链接文件加上权限,只执行,不可以读不可以写,就不怕用户乱更改你的app了。
                    
            linux中有两种类型的链接:
                        硬链接:利用linux中为每一个文件分配的inode码建立连接的
                                                什么是inode码?
                                                    在Linux中,每创建一个文件,系统就会给这个文件
                                                    赋予一个inode码来记录这个文件的信息。
                                                    
                                                    对于系统来说,inode码才是这个文件的真实名字
                        
                                        查看文件的inode码:ls -i
                        
                        
                        软链接:(符号链接)利用带路径的文件名来进行建立链接。
                                通常建立软链接使用的是绝对路径而不是相对路径,以最大限度提高可移植性
                                
                        绝对路径:以/根目录为开头的详细路径
                        相对路径:不以/根目录为开头的相对路径
                            两种路径是相对的,   如果不是绝对路径就是相对路径
                                                如果不是相对路径就是绝对路径
                                                
                        创建链接文件的命令:ln
                                1/硬链接:ln -v 
                                        -v是显示链接过程
                                farsight@ubuntu:~/test$ ln 1.c 1lin.c
                                786531 1.c          796555 4.c          786544 guo_1.c    786548 guo_b.c
                                786531 1lin.c
                                
                                
                                
                                2/软连接:ln -s
                                        soft(软)
                                    farsight@ubuntu:~/test$ ln -s 3.c 3lin.c            //相对路径
                                    farsight@ubuntu:~/test$ ln -s /home/farsight/test/4.c 4lin.c    //绝对路径
​
​
​
                        总结:
                                硬链接:
                                        本体文件被删除后,链接文件依旧有效,但是链接断开
                                        重新创建同名本体文件,链接文件有效,但是链接失效
                                        
                                        移动本体文件,链接文件依旧有效
                                        
                                软链接: 
                                    相对路径:本体文件被删除,链接文件失效
                                            重新创建同名本体文件后,链接文件有效,链接有效
                                            
                                            本体文件被移动后,链接文件失效
                                            重新移回本体文件,链接文件有效,链接有效
                                            
                                    绝对路径:
                                            本体文件被移动,链接文件失效
                                            
                                            链接文件被移动,链接文件依旧有效,且链接有效

二:数据结构

1:时间复杂度(硬件的提升空间复杂度不用理会)

代码运行的效率,快慢

时间复杂度是一个函数

例子
F(N) = N*N + 2*N+1
估算O(N^2)
​
F(N) = 2N+10
O(N)
​
循环100次
O(1)

数据结构按照逻辑结构分类

    * 线性结构:
        只有一个前驱节点和一个后继点(一个元素就是一个节点)
        包含:
            线性表
            栈
            队列
            
    * 非线性结构:
            不止有一个前驱节点或者后继节点
            包含:
                树
                图----不讲     

数据结构按照存储结构分类:

    * 顺序结构:
        数据紧挨保存在一块空间---数据不能间断
        
    * 链式结构:
        数据随机存放(到处存放),通过指针的方式链接起来

2:线性表:

1》概念: ​ 只有一个前驱节点和一个后继节点 ​ 2》分类: ​ *按照顺序存储的方式:(数组) ​ 顺序表 ​ *按照链式存储的方式: ​ 单向链表 双向链表 内核链表

顺序表的优缺点: 优点: 查找数据方便(通过下标) 缺点: *插入 和 删除数据,后面往后或者往前移动 -----> 麻烦 *假如数据量大,那么连续占用的空间大,不方便 例子: malloc(1000000);

3:链表:

  • 单向链表

  • 单向循环链表

  • 双向链表

  • 双向循环链表

  • 内核链表

单向链表(增删改查)

三:文件IO

1:标准(带缓冲)IO编程

  1. 格式化标准IO编程

  2. 非格式化标准IO编程

2:文件操作步骤: 第一步: 打开文件 第二步: 读写文件 第三步: 关闭文件

定义: 文件:一组相关数据的有序集合。 文件名:这个数据集合的名称。

3:linux中文件分类: (-)常规文件 ASCII码文件 二进制的文件 (d)目录 (c)字符设备 (b)块设备 (p)有名管道 (s)套接口 (l)符号连接

4,标准IO概念

不仅在UNIX系统,在很多操作系统上都实现了标准I/O库
标准I/O库由ANSI C标准说明
​
标准I/O库处理很多细节,如缓冲分配、以优化长度执行I/O等,这样使用户不必关心如何选择合适的块长度
​
标准I/O在系统调用函数基础上构造的,它便于用户使用
​
标准I/O库及其头文件stdio.h为底层I/O系统调用提供了一个通用的接口。

5,文件指针 FILE指针:每个被使用的文件都在内存中开辟一个区域,用来存放文件的有关信息,这些信息是保存在一个结构体类型的变量中, 该结构体类型是由系统定义的,取名为FILE。

struct _IO_FILE;
    typedef struct _IO_FILE FILE;
    struct _IO_FILE {
          int _flags;           /* High-order word is _IO_MAGIC; rest is flags. */
        #define _IO_file_flags _flags
​
          /* The following pointers correspond to the C++ streambuf protocol. */
          /* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
          char* _IO_read_ptr;   /* Current read pointer */
          char* _IO_read_end;   /* End of get area. */
          char* _IO_read_base;  /* Start of putback+get area. */
          char* _IO_write_base; /* Start of put area. */
          char* _IO_write_ptr;  /* Current put pointer. */
          char* _IO_write_end;  /* End of put area. */
          char* _IO_buf_base;   /* Start of reserve area. */
          char* _IO_buf_end;    /* End of reserve area. */
          /* The following fields are used to support backing up and undo. */
          char *_IO_save_base; /* Pointer to start of non-current get area. */
          char *_IO_backup_base;  /* Pointer to first valid character of backup area */
          char *_IO_save_end; /* Pointer to end of non-current get area. */
​
          struct _IO_marker *_markers;
​
          struct _IO_FILE *_chain;
​
          int _fileno;
        _IO_off_t _old_offset; /* This used to be _offset but it's too small.  */
​
        #define __HAVE_COLUMN /* temporary */
          /* 1+column number of pbase(); 0 is unknown. */
          unsigned short _cur_column;
          signed char _vtable_offset;
          char _shortbuf[1];
​
          /*  char* _save_gptr;  char* _save_egptr; */
​
          _IO_lock_t *_lock;
​
    }

标准I/O库的所有操作都是围绕流(stream)来进行的,在标准I/O中,流用FILE *来描述。

标准I/O库是由Dennis Ritchie在1975年左右编写的

6,流(stream) 定义:所有的I/O操作仅是简单的从程序移进或者移出,这种字节流,就称为流。 分类:文本流/二进制流。

7,打开文件,返回文件指针:FILE*

#include <stdio.h>
    FILE *fopen(const char *pathname, const char *mode);
    //参数1 ---- 要打开的文件 
    //参数2 ---- 打开的操作权限:
                    r      //只读,文件必须存在
                    r+     //读写,文件必须存在
                    w      //只写,文件不存在,则创建,文件存在,则把文件清空 
                    w+     //写读,文件不存在,则创建,文件存在,则把文件清空 
                    a      //只写, 文件不存在,则创建,文件存在,则在文件末尾追加
                    a+     //写读, 文件不存在,则创建,文件存在,则在文件末尾追加,如果是读,则从文件头开始读
    //返回值 ---成功:文件指针,失败:NULL

8,关闭文件: #include <stdio.h> int fclose(FILE *stream);

int main(int argc,char **argv)
        {
            FILE *fp;
​
            if(argc != 2){
                printf("Usage: %s <filename>\n",argv[0]);
                exit(1); //结束程序
            }
​
            //打开文件
            fp = fopen(argv[1],"a");
            if(fp == NULL){
                perror("fopen");
                exit(1);
            }
​
​
            //关闭文件
            fclose(fp); //刷新缓冲区,释放缓冲区
​
            return 0;
        }

9,标准IO的缓冲

当程序和硬盘上的文件进行数据存取时,由于cpu速度比硬盘速度快很多,为了提高程序的效率, 在程序和文件之间创建一个缓冲区,程序可以先将数据写到缓冲区中,然后再一点一点刷新到文件中,这样能够程序效率。

在标准IO中,缓冲分为三种:
    1》全缓冲  ---- //操作常规文件的时候
        
        全缓冲刷新的条件:
            缓冲区满时
            关闭文件时
            强制刷新缓冲区
            程序正常结束时
    
    2》行缓冲 ----- //操作标准输入和标准输出的时候
        当运行一个程序时,系统会自动默认打开三个设备文件:
                         文件指针       设备 
        标准输入            stdin       键盘 
        标准输出            stdout      屏幕
        标准错误输出          stderr      屏幕
        
        行缓冲刷新的条件: 
            缓冲区满时
            关闭文件时
            强制刷新缓冲区 ------ //fflush(FILE * )
            程序正常结束时
            当缓冲区中遇到换行符('\n')时
            例如:
            int main(int argc,char **argv)
            {
            #if 0
                while(1){
                    printf("hello world");
                    usleep(100000);
                }
            #else
                printf("hello world\n");
​
                //exit(0);
                //fflush(stdout);
                //fclose(stdout);
                while(1);
            #endif
                return 0;
            }
            3》不带缓冲 ------ //操作标准错误输出的时候
        
        没有缓冲区刷新的条件,直到往缓冲区写数据,会立即刷新。
        很多的人机交互界面要求不可全缓冲。
        标准出错决不会是全缓冲的。
    
        例如:
            int main(int argc,char **argv)
            {
                printf("helloworld");   //通过标准输出打印,行缓冲
                fprintf(stderr,"hello world"); //通过标准错误输出打印,不带缓冲
​
                while(1);
                return 0;
            }
​
            

2.格式化标准IO

1,格式化输出函数

int printf(const char *format, ...); //只能向标准输出写数据 int fprintf(FILE *stream, const char *format, ...); //向指定的文件中写数据

 
        例如: 
        int main(int argc,char **argv)
        {
        #if 0
            printf("helloworld");   //通过标准输出打印,行缓冲
            fprintf(stderr,"hello world"); //通过标准错误输出打印,不带缓冲
​
            while(1);
            /****************************************************/
        #else
            FILE * fp;
​
            if(argc != 2){
                fprintf(stderr,"Usage: %s <filename>\n",argv[0]);
                exit(1);
            }
​
            if((fp = fopen(argv[1],"w")) == NULL){
                perror("fopen");
                exit(1);
            }
​
        #if 0
            //向文件中写数据
            char buf[100];
            while(1){
                bzero(buf,sizeof(100));
                scanf("%s",buf);
                fprintf(fp,"%s\n",buf);  //将buf输出的结果写到文件中
                fflush(fp);  //确保数据能够立即写到文件中,可以使用fflush()强制刷新
            }
        #endif
            fprintf(fp,"%s\n","hello world");
            fprintf(fp,"%d\n",120);
            fprintf(fp,"%f\n",545.34534);
            fclose(fp);
        #endif
            return 0;
        }
            
  
       int sprintf(char *str, const char *format, ...);     //输出结果以字符串形式写到内存中
       例如: 
        struct student{
            int sno;
            char name[20];
            float score;
        };
​
        int main(int argc,char **argv)
        {
            int i;
            struct student st[3];
            char buf[5];
​
​
            //向文件中写数据
            for(i =0; i < 3; i++){
                printf("请输入学生信息:");
                scanf("%d%s%f",&st[i].sno,st[i].name,&st[i].score);
                sprintf(buf,"%d %s %.2f",st[i].sno,st[i].name,st[i].score);  //数组溢出
                printf("%s\n",buf);
            }
            return 0;
        }
​
       int snprintf(char *str, size_t size, const char *format, ...); //输出结果以字符串形式写到内存中,同时限制写的字符数,防止溢出
       例如: 
        struct student{
            int sno;
            char name[20];
            float score;
        };
​
        int main(int argc,char **argv)
        {
            int i;
            struct student st[3];
            char buf[5];
​
​
            //向文件中写数据
            for(i =0; i < 3; i++){
                printf("请输入学生信息:");
                scanf("%d%s%f",&st[i].sno,st[i].name,&st[i].score);
                snprintf(buf,5,"%d %s %.2f",st[i].sno,st[i].name,st[i].score);  //数组溢出
                printf("%s\n",buf);
            }
            return 0;
        }
​

2,格式化输入函数

int scanf(const char *format, ...); //从标准输入(键盘)获取数据 int fscanf(FILE *stream, const char *format, ...); //从指定文件中获取数据

    例如: 
     struct student{
        int sno;
        char name[20];
        float score;
    };
​
    int main(int argc,char **argv)
    {
        FILE * fp;
        int i;
        struct student st[3];
​
        if(argc != 2){
            fprintf(stderr,"Usage: %s <filename>\n",argv[0]);
            exit(1);
        }
​
        if((fp = fopen(argv[1],"r")) == NULL){
            perror("fopen");
            exit(1);
        }
​
        //从文件中读出数据
        for(i =0; i < 3; i++){
            printf("请输入学生信息:");
            fscanf(fp,"%d%s%f",&st[i].sno,st[i].name,&st[i].score);
            printf("%d %s %.2f\n",st[i].sno,st[i].name,st[i].score);  //将buf输出的结果写到文件中
        }
        return 0;
    }
​
    
    int sscanf(const char *str, const char *format, ...); //从内存中获取数据
    例如: 
    struct student{
        int sno;
        char name[20];
        float score;
    };
​
    int main(int argc,char **argv)
    {
    #if 0
        struct student st;
        char buf[] = "1001 jack 87.56";
​
        sscanf(buf,"%d%s%f",&st.sno,st.name,&st.score);
        printf("%d %s %.2f\n",st.sno,st.name,st.score);  //将buf输出的结果写到文件中
    #else
        char buf[100];
        int h,m;
​
        printf("请输入时间:");
        scanf("%s",buf);
        sscanf(buf,"%d:%d",&h,&m);
        printf("%s为%d小时%d分钟\n",buf,h,m);
​
        int a;
        strcpy(buf,"123");
        sscanf(buf,"%d",&a);
        printf("a = %d\n",a);
    #endif
        return 0;
    }

3:非格式化标准IO编程 -------//输入与输出没有格式,以字节流读写

1,一次一个字符的IO 输入

int fgetc(FILE *stream); //(函数)从指定的文件中获取一次字符 int getc(FILE *stream); //(宏)从指定的文件中获取一次字符 int getchar(void); //默认从标准输入(键盘)获取一次字符

例如: 
        int main(int argc,char **argv)
        {
            int a;
            char b;
​
            scanf("%d",&a);
            printf("a = %d\n",a);
            while(getchar() != '\n'); //清空输入缓冲区
            scanf("%c",&b);
            printf("b = %c\n",b);
​
            return 0;
        }

2》输出

int fputc(int c, FILE *stream); //(函数)向指定的文件中写一个字符 int putc(int c, FILE *stream); //(宏)向指定的文件中写一个字符 int putchar(int c); //向标准输出写一个字符

    例如: 
        int main(int argc,char **argv)
        {
            FILE * rfp,*wfp;
            char ch;
​
            if(argc != 3){
                fprintf(stderr,"Usage: %s <src_filename> <target_filename>\n",argv[0]);
                exit(1);
            }
​
            if((rfp = fopen(argv[1],"r")) == NULL){
                perror("fopen");
                exit(1);
            }
            if((wfp = fopen(argv[2],"w")) == NULL){
                perror("fopen");
                exit(1);
            }
​
            //从一个文件中读数据,写到另一个文件中
            while((ch = fgetc(rfp)) != EOF){
                //printf("%c",ch);
                putchar(ch);
                fputc(ch,wfp);
            }
​
            fclose(rfp);
            fclose(wfp);
            return 0;
        }
​

2,一次一行IO

//输入函数 char *fgets(char *s, int size, FILE *stream); //从指定文件中读取一行数据 //fgets()会连同一行后面的换行符一起读入 //参数1 ----- 内存地址 //参数2 ----- 内存大小 //参数3 ----- 要读取数据的文件的指针 char *gets(char *s); //默认从标准输入(键盘)获取一行数据

int main(int argc,char **argv)
    {
        char buf[100];
    #if 0
        fgets(buf,sizeof(buf),stdin);
        printf("%ld\n",strlen(buf));
        printf("%s\n",buf);
    #else
        FILE * rfp;
​
        if(!(rfp = fopen(argv[1],"r"))){
            perror("fopen");
            exit(1);
        }
​
        while(fgets(buf,sizeof(buf),rfp) != NULL)
            printf("%s",buf);
​
        fclose(rfp);
    #endif
        return 0;
    }

//输出函数 int fputs(const char *s, FILE *stream); //向指定的文件中写一行 int puts(const char *s); //默认向标准输出(屏幕)打印,在字符串后面会自动添加换行符

例如: 
        int main(int argc,char **argv)
        {
            FILE * rfp,*wfp;
            char buf[100];
​
            if(argc != 3){
                fprintf(stderr,"Usage: %s <src_filename> <target_filename>\n",argv[0]);
                exit(1);
            }
​
            if((rfp = fopen(argv[1],"r")) == NULL){
                perror("fopen");
                exit(1);
            }
            if((wfp = fopen(argv[2],"w")) == NULL){
                perror("fopen");
                exit(1);
            }
​
            //从一个文件中读数据,写到另一个文件中
            while(fgets(buf,sizeof(buf),rfp)){
                printf("%s",buf);
                fputs(buf,wfp);
            }
​
            fclose(rfp);
            fclose(wfp);
            return 0;
        }

3,直接IO -----//字节流

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); //参数1 ----- 内存地址 //参数2 ----- 对象大小 //参数3 ----- 对象个数 //参数4 ----- 文件指针

例如: 
        struct student{
            int sno;
            char name[20];
            float score;
        };
​
        int main(int argc,char **argv)
        {
            FILE * fp;
            int i;
            struct student st[3];
​
            if(argc != 2){
                fprintf(stderr,"Usage: %s <filename>\n",argv[0]);
                exit(1);
            }
​
            if((fp = fopen(argv[1],"w")) == NULL){
                perror("fopen");
                exit(1);
            }
​
            //向文件中写数据
            for(i =0; i < 3; i++){
                printf("请输入学生信息:");
                scanf("%d%s%f",&st[i].sno,st[i].name,&st[i].score);
            }
            //直接iO,向文件中写三个结构体
            fwrite(st,sizeof(struct student),3,fp);
​
            return 0;
        }
​
    例如:
        struct student{
            int sno;
            char name[20];
            float score;
        };
​
        int main(int argc,char **argv)
        {
            FILE * fp;
            int i;
            struct student st[3];
​
            if(argc != 2){
                fprintf(stderr,"Usage: %s <filename>\n",argv[0]);
                exit(1);
            }
​
            if((fp = fopen(argv[1],"r")) == NULL){
                perror("fopen");
                exit(1);
            }
​
            //直接iO,向文件中写三个结构体
            fread(st,sizeof(struct student),3,fp);
​
            //向文件中写数据
            printf("学生信息:\n");
            for(i =0; i < 3; i++){
                printf("%d  %s  %.2f\n",st[i].sno,st[i].name,st[i].score);
            }
​
            return 0;
        }
        
        例如: 
        int main(int argc,char **argv)
        {
            FILE * rfp,*wfp;
            char buf[100];
​
            if(argc != 3){
                fprintf(stderr,"Usage: %s <src_filename> <target_filename>\n",argv[0]);
                exit(1);
            }
​
            if((rfp = fopen(argv[1],"r")) == NULL){
                perror("fopen");
                exit(1);
            }
            if((wfp = fopen(argv[2],"w")) == NULL){
                perror("fopen");
                exit(1);
            }
​
            //从一个文件中读数据,写到另一个文件中
            while(!feof(rfp) && !ferror(rfp)){
                bzero(buf,sizeof(buf));
                fread(buf,sizeof(char),sizeof(buf)-1,rfp);
                printf("%s",buf);
                fwrite(buf,sizeof(char),strlen(buf),wfp);
            }
​
            fclose(rfp);
            fclose(wfp);
            return 0;
        }
​

4,定位文件指针

long ftell(FILE *stream); //参数 ----- 文件指针 //返回值 ----成功:文件指针的位移量,失败:-1

例如: 
        int main(int argc,char **argv)
        {
            FILE * fp;
            char ch;
            int i;
​
            if(argc != 2){
                fprintf(stderr,"Usage: %s <filename>\n",argv[0]);
                exit(1);
            }
​
            if((fp = fopen(argv[1],"r")) == NULL){
                perror("fopen");
                exit(1);
            }
​
            for(i = 0; i < 7; i++){
                ch = fgetc(fp);
                printf("%c",ch);
            }
​
            printf("\n%ld\n",ftell(fp));
​
​
            return 0;
        }

2》修改文件指针的位置

int fseek(FILE *stream, long offset, int whence);
//参数1 ----- 文件指针
//参数2 ----- 偏移量(相对于whence的)
//参数3 ----- 表示参数2偏移的起始位置:
        SEEK_SET  --- 文件开头
        SEEK_CUR  --- 文件指针当前位置
        SEEK_END  --- 文件末尾
        例如: 
        int main(int argc,char **argv)
        {
            FILE * fp;
            char ch;
            int i;
​
            if(argc != 2){
                fprintf(stderr,"Usage: %s <filename>\n",argv[0]);
                exit(1);
            }
​
            if((fp = fopen(argv[1],"r")) == NULL){
                perror("fopen");
                exit(1);
            }
​
        #if 0
            //修改文件指针位置,从第5个字符后开始读取数据
            fseek(fp,5,SEEK_SET);
            for(i = 0; i < 3; i++){
                ch = fgetc(fp);
                printf("%c",ch);
            }
        #else
            //计算大小
            int len;
            fseek(fp,0,SEEK_END);
            len = ftell(fp);
            printf("len = %d\n",len);
        #endif
​
​
            return 0;
        }

小总结:

1,格式化输出函数

int printf(const char *format, ...);    //只能向标准输出写数据
int fprintf(FILE *stream, const char *format, ...);   //向指定的文件中写数据
int sprintf(char *str, const char *format, ...);        //输出结果以字符串形式写到内存中
int snprintf(char *str, size_t size, const char *format, ...); //输出结果以字符串形式写到内存中,同时限制写的字符数,防止溢出
例如: 
        struct student{
            int sno;
            char name[20];
            float score;
        };
​
        int main(int argc,char **argv)
        {
            int i;
            struct student st[3];
            char buf[5];
​
​
            //向文件中写数据
            for(i =0; i < 3; i++){
                printf("请输入学生信息:");
                scanf("%d%s%f",&st[i].sno,st[i].name,&st[i].score);
                snprintf(buf,5,"%d %s %.2f",st[i].sno,st[i].name,st[i].score);  //数组溢出
                printf("%s\n",buf);
            }
            return 0;
        }

2,格式化输入函数

    int scanf(const char *format, ...);  //从标准输入(键盘)获取数据
    int fscanf(FILE *stream, const char *format, ...); //从指定文件中获取数据
    int sscanf(const char *str, const char *format, ...); //从内存中获取数据
    struct student{
        int sno;
        char name[20];
        float score;
    };
​
    int main(int argc,char **argv)
    {
    #if 0
        struct student st;
        char buf[] = "1001 jack 87.56";
​
        sscanf(buf,"%d%s%f",&st.sno,st.name,&st.score);
        printf("%d %s %.2f\n",st.sno,st.name,st.score);  //将buf输出的结果写到文件中
    #else
        char buf[100];
        int h,m;
​
        printf("请输入时间:");
        scanf("%s",buf);
        sscanf(buf,"%d:%d",&h,&m);
        printf("%s为%d小时%d分钟\n",buf,h,m);
​
        int a;
        strcpy(buf,"123");
        sscanf(buf,"%d",&a);
        printf("a = %d\n",a);
    #endif
        return 0;
    }
​

二,非格式化标准IO编程 -------//输入与输出没有格式,以字节流读写

1,一次一个字符的IO 1》输入

        int fgetc(FILE *stream);   //(函数)从指定的文件中获取一次字符
        int getc(FILE *stream);   //(宏)从指定的文件中获取一次字符
        int getchar(void);      //默认从标准输入(键盘)获取一次字符
        2》输出
        int fputc(int c, FILE *stream);  //(函数)向指定的文件中写一个字符
        int putc(int c, FILE *stream);  //(宏)向指定的文件中写一个字符
        int putchar(int c);       //向标准输出写一个字符
        

2,一次一行IO

    //输入函数
    char *fgets(char *s, int size, FILE *stream);     //从指定文件中读取一行数据
    //fgets()会连同一行后面的换行符一起读入
    //参数1 ----- 内存地址
    //参数2 ----- 内存大小
    //参数3 ----- 要读取数据的文件的指针
    char *gets(char *s);   //默认从标准输入(键盘)获取一行数据
    输出
    int fputs(const char *s, FILE *stream);  //向指定的文件中写一行
    int puts(const char *s);    //默认向标准输出(屏幕)打印,在字符串后面会自动添加换行符

例如:

例子:printf scanf

  1. 打开文件

sprint 把

sscanf 把字符串变成整数(时间:)

fgets 会读取换行符

puts 会自动加换行符

直接IO

fwrite

fread

有乱码的原因

定位文件指针

ftell(报位置)

fseek

2:非标准IO

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值