C语言进阶

数据操作

1 数据修饰 auto、static、register、const、volatile

  • auto

auto关键字 修饰局部变量

auto修饰局部变量,标志这个局部变量是自动局部变量,自动局部变量分配在栈上。(也就是说如果不初始化,那么值就是随机的)

auto的局部变量就是默认定义的普通的局部变量,省略了auto关键字而已,

  • static
static关键字可以修饰:局部变量、全局变量、函数
static修饰后改变了什么?
1.改变了生存周期 
2.改变了作用域 

static修饰不同对象时的作用:

1、局部变量:
局部变量就是在函数内定义的变量,普通的局部变量,生存周期是随着函数的结束而结束,
当用static修饰后,静态局部变量的生存周期就是当程序结束才会结束。
改变其生存周期的原因是被static修饰的局部变量被存放在.bss段或者.data段,而普通的局部变量是存放在栈上的。
总结:改变了生存周期,但是没有改变其作用域。

2、全局变量:
全局变量用static修饰改变了作用域,没有改变生存周期。
普通的全局变量是可以被其他的.c文件引用的,静态全局变量就只能被定义该全局变量的.c文件引用。
这样其他的文件就不能通过extern的方式去访问,这样主要是为了数据安全。
总结:改变其作用域,没有改变生存周期。

3、函数:
函数用static修饰,改变了作用域。
和静态全局变量一致

  • register

register修饰的变量(一般是全局变量),编译器会尽量将它分配在寄存器中。(平时分配的一般是在内存中的)。
通过改善这个变量的访问效率可以极大地提升运行效率。读写效率会更高。
所以register修饰的变量用在那种变量被反复高频率的作用,

  • const

const限定一个变量不允许被改变,产生静态作用。

可以保护被修饰的东西,防止意外的修改,增强程序的健壮性。

  • volatile

volatile 关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问

2 大端模式、小端模式

大端字节存储模式 :
是指数据的低位保存在内存的高地址中,而数据的高位保存在内存的低地址中

小端字节存储模式。
是指数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中

以0x1234为例进行说明。

在这里插入图片描述

内存操作

在一个C语言程序中,能够获取的内存就是三种情况:栈(stack)、堆(heap)、静态存储区
在这里插入图片描述

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

  • 静态存储区

由 操作系统 分配 和 使用,有 .bss段 和 .data段组成,可读可写。

.data段(数据段)

已初始化的全局变量、静态变量存放在.data段。
.data段占用可执行文件空间,其内容有程序初始化。

.bss段
未初始化的全局变量、静态变量 和 初始化为0的全局变量、静态变量存放在.bss段。
.bss段不占用可执行文件空间,其内容由操作系统初始化。

  • 栈 stack

函数执行时,函数的形参以及函数内的局部变量,分配在栈区,函数运行结束后,形参和局部变量去栈(自动释放)。
编译器在需要的时候分配,不需要时自动清除

1 动态内存管理(堆区 heap) malloc、calloc、realloc、free

开辟内存的函数 

1void* malloc(size_t size); 
 如果开辟成功,返回一个指向开辟好空间的指针。
 如果开辟失败,则返回一个NULL指针,所以使用malloc,要对返回值进行检查。 

2void* calloc(size_t num,size_ size);
 函数的功能是为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0。
 与函数malloc的区别只在于calloc会在返回地址之前把申请的空间的每个字节初始化为全03、realloc 
 原本申请的空间不够,通过这个函数创建新的空间

释放动态开辟的内存

void free(void* ptr); 

2 GCC对C的扩展:内存对齐 attribute、aligned()、packed()、at

GNU使用__attribute__选项来设置我们想要的内存字节对齐大小。__attribute__选项不属于标准C语言,它是GCC对C语言的一个扩展用法。

GCC支持用__attribute__为变量、类型、函数、标签制定特殊属性。

大致有六个参数值可以被设定,即:aligned, packed, transparent_union, unused, deprecated 和 may_alias 。

  • aligned

该属性设定一个指定大小的对齐格式(以字节 为单位)

struct p
 {
  int a;
  char b;
  short c;
 }__attribute__((aligned(4))) pp;

sizeof(a)+sizeof(b)+sizeof(c)=4+1+1=6<8 所以sizeof(pp)=8

struct m
 {
  char a;
  int b;
  short c;
 }__attribute__((aligned(4))) mm;

sizeof(a)+sizeof(b)+sizeof(c)=1+4+2=7

a 后面需要用 3 个字节填充 才能和 b 是 4 个字节 一致 
所以 a 占用 4 字节, b 占用 4 个字节,而 c 又要占用 4 个字节。所以 sizeof(mm)=12

  • at

attribute( at(绝对地址) )的作用分两个,一个是绝对定位到Flash,另个一是绝对定位到RAM。

定位到flash中,一般用于固化的信息,如出厂设置的参数,上位机配置的参数,ID卡的ID号,flash标记等等

const u16 gFlashDefValue[512] __attribute__((at(0x0800F000))) = {0x1111,0x1111,0x1111,0x0111,0x0111,0x0111};
const u16 gflashdata__attribute__((at(0x0800F000))) = 0xFFFF;
定位到RAM中,一般用于数据量比较大的缓存,如串口的接收缓存,再就是某个位置的特定变量

//接收缓冲,最大USART_REC_LEN个字节,起始地址为0X20001000.
u8 USART2_RX_BUF[USART2_REC_LEN] __attribute__ ((at(0X20001000)));

1、绝对定位不能在函数中定义,局部变量是定义在栈区的,栈区由MDK自动分配、释放,不能定义为绝对地址,只能放在函数外定义。
2、定义的长度不能超过栈或Flash的大小,否则,造成栈、Flash溢出。

文件操作

1 文件 I/O 库

文件 I/O 指的是对文件的输入 / 输出操作,说白了就是对文件的读写操作
文件 I/O 是系统调用

1.1 文件描述符

文件描述符:是一个非负整数,是一个文件句柄,是与对应的文件绑定

文件描述符的分配:为没有被使用的且最小的非负整数

系统定义的文件描述符:0 1 2( 标准输入、标准输出、标准错位 )

1.2 读写操作(open 、close 、write 、read)
1open("路径""flag方式")
   open("路径""flag方式""权限")

flag方式

O_RDONLY:  只读方式
O_WRONLY:  只写方式
O_RDWR:    可读可写方式
O_CREAT:   不存在就创建
O_EXCL:    和 O_CREAT 搭配使用

权限

如果使用 O_CREAT,则需要加入第三个参数,设置文件的权限
 
2、close(文件描述符) 

3、write(文件描述符,"写入的数据""数据大小"4、read(文件描述符,"存储读取数据的缓冲区""数据大小"
1.3 文件 I/O 内核缓冲

调用 write() 只是将 字节数据拷贝到了 内核空间的缓冲区 ,拷贝完成之后函数就返回了, 在后面的某个时刻,内核会将其缓冲区中的数据写入(刷新)到磁盘设备中,所以由此可知,系统调用 write() 与磁盘操作并不是同步的,write()函数并不会等待数据真正写入到磁盘之后再返回。

  • 控制文件 I/O 内核缓冲的系统调用
1fsync()函数  文件的内容数据和元数据写入磁盘,只有在对磁盘设备的写入操作完成之后,fsync()函数才会返回

2fdatasync()函数 并不包括文件的元数据	

3sync()函数 将所有文件 I/O 内核缓冲区中的文件内容数据和元数据全部更新到磁盘设备中

2 标准I/O 库

标准C库中,用于文件I/O操作的库函数,叫做 标准I/O库

在这里插入图片描述

2.1 FILE 指针

FILE 指针的作用相当于文件描述符,只不过 FILE 指针用于标准 I/O 库函数中、而文件描述符则用于文件I/O 系统调用中

2.2 读写操作(fopen 、fclose 、fwrite 、fread)
  • fopen()
1fopen("路径""flag方式")

r  只读方式打开   O_RDONLY
r+ 可读可写方式   O_RDWR
w  只写方式打开(无文件则创建)  
w+ 可读可写方式打开(无文件则创建)
a  只写方式打开(在文件尾写入,无文件则创建)  
a+ 可读可写方式打开(在文件尾写入,无文件则创建)
 
2、fclose(FILE 指针) 

3、fwrite(”写入数据“,每个字节大小,总大小,FILE 指针)
  返回读取到的数据项的数目

4、fread(读出存储的数据缓冲区,单字节大小,总字节大小,FILE 指针)
  返回写入的数据项的数目

5、fseek(FILE 指针,偏移量,SEEK_SET)
  SEEK_SET 文件开头处
  SEEK_END 文件末尾处
2.3 标准 I/O 的 stdio 缓冲

标准 I/O 在应用层维护了自己的缓冲,称为stdio 缓冲

  • 控制 stdio 缓冲
1setvbuf(FILE 指针,buf,缓冲类型,大小) 
int setvbuf(FILE *stream, char *buf, int mode, size_t size);

缓冲类型:
_IONBF 无缓冲,将立即调用文件 I/O 操作 write()或者 read()

_IOLBF 行缓冲,遇到换行符"\n"时,标准 I/O 才会执行文件
       标准输入和标准输出默认采用的就是行缓冲模式 

_IOFBF 全缓冲 在填满 stdio 缓冲区后才进行文件 I/O 操作(read、write)。
       普通磁盘上的常规文件默认全缓冲模式。

2fflush(stdout)
	强制刷新将输出到 stdio 缓冲区中的数据写入到内核缓冲区
	

不常见的函数

终止进程 exit()、_exit()

这个参数用来表示进程终止时的状态,0表示正常终止,其余 表示非正常终止,

1void exit(int status)
exit()是C语言库函数 头文件 <stdlib.h>

2void _exit(int status)
 _exit()**系统调用函数**

在这里插入图片描述

  • exit()、return()的区别

exit()是一个库函数, return()是C语言的语句
exit() 函数最终会进入到内核,把控制权交给为内核,最终由内核去终止进程。
return并不会进入到内核,它只是一个main函数返回,返回到它的上层调用把控制权交给他的上层调用,最终由上层调用终止进程。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值