C语言要点总结

typedef

在编程中使用typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明。如
typedef int size;
void measure(size*psz);
size array[4];
size len=file.getlength();
std::vectorvs;

全局变量与局部变量

允许函数内的局部变量和全局变量重名 全局变量在函数内不起作用
全局变量的作用域为源文件(程序),即定义处到文件结束,链接属性为external。另一.c要用的话需要用extern声明。

extern修饰定义在别的文件或者本文件其他位置external属性的变量,一般修饰全局标识符(变量函数)
全局变量的链接属性为external。static后为internal。只在本文件使用​
static都不改变作用域。当用于函数内部变量时,改变的是存储类型​。外部时改变的链接属性。

结构体(数据集合)

struck student
{

}stu[5]
struct student两个词共同构成了结构体类型 ,就像int类型。student是结构体标记,后面的是变量名跟int i中的i一样

  • 结构体变量初始化的两种方式

int main ()

{
Student a={ 20,79,‘f’}; //结构体定义的第一种方式
//第二种方式
Student *pstu=malloc(sizeof(Student));
pstu->age=20;
pstu->score=79;
pstu->sex=‘f’;
printf(“年龄:%d 分数:%.2f 性别:%c\n”, a.age, a.score, a.sex );

return 0;
}

//结构体定义的第一种方式,通常的变量定义形式,但是一般情况下我们传递结构体变量都是传地址以减少赋值内存内容的开销,所以一般情况下还有一个语句:Student *pa=&a;
Student a={ 20,79,’f’};
Student *pa=&a;
我们干嘛不直接定义一个指向结构体变量的指针呢?所以体现了第二种方法的便利性。

//第二种方式
Student *pstu=malloc(sizeof(Student));
pstu->age=20;
pstu->score=79;
pstu->sex=’f’;
传递参数的时候直接把pstu传过去就over了。

  • typedef和struct结合

#include <stdio.h>
typedef struct student //student为结构体标记(可去)
{
int age; /年龄/
float score; /分数/
char sex; /性别/
}Student;
用法:如 struct student st; == Student st;
Student(结构体类型)实际上就是struct student的别名,可以用来声明结构体变量,其中st为结构体变量
注意 1.这里也可以不写student,于是上面用法变为Student st==struct{int age;…}st;
    ----因为没有了结构体标记,声明结构体时便不能省略{int age;…},不然没法确定结构体组成,不知是什么结构体
   2.如果没有typedef,则Student变为结构体变量
详细内容struct和typedef彻底搞懂

指针常量,常量指针

结论:const修饰的是p,那么p是常量(指向不可改),所以是指针常量
const修饰的是int* ,那么指向的类型是常量不可改,p可以改(即指向可以改),所以是常量(类型)指针
在这里插入图片描述

int a;
int * const p = &a //指针常量,*p可以修改*p = 8;(OK,指针类型的常量,指向不可改,指向的内容可以该)
p不可以修改 p++(ERROR)
int a,b;
const int *p = &a;//常量指针 *p不可修改 *p = 8;(ERROR,该指针指向的内容是常量)
p 可以修改 p = &b (OK)

char *name_t : //name_t是一个指针,指向了一个char类型的内存(如果该变量是静态的,则初始化为0,如果是自动的,则不会被初始化。都不会“创建”用于存储char类型的内存空间。可能地址非法,可能地址处在错误的边界,可能修改一个你不希望修改的位置处的值)

typedef char *name_t; // 可以用name_t来定义char类型的指针;比如char *p,等价于name_t p(即name_t=char *)

typedef void (*OS_TASK_PTR)(void *p_arg);//定义了一个指向函数的(由void导致)指针OS_TASK_PTR,其返回值 void 类型,参数也是后面的(void *)接下来我们就可以直接使用 T 来定义这种指针变量,比如:OS_TASK_PTR fn1; //等价于void (*fn1) (void *);

name_t abc; // 则abc 是一个指针,指向了一个char类型的内存

指针函数,函数指针

float *g() , (*h)();
()比*优先级高,float * (g()); g是函数,返回值为float指针
(*h)为函数,返回值为flaot,h为指向这个函数的指针

int f(int);
int (*pf)(int)=&f;

在这里插入图片描述
一个较难的例子:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

数组和指针

在这里插入图片描述
总结:注意数组名array是指针常量,是指向数组首元素的int型指针(当元素是int型)。而&array返回的不是指针常量的地址(即不是指向指针常量的指针),而是指向数组的指针(即指针类型是数组,在数值上等于&array[0])。
还有sizeof时,array也不是指针常量,而是数组长度。

volatile关键字

它所声明的变量随时可能发生变化:1.告诉compile对访问该变量的代码不做任何优化,从而提供对特殊地址的稳定访问。2.变量会在程序外被改变,每次必须从内存地址重新读取,而不能重复使用cache或寄存器中的备份。
例子:

  1. 并行设备的硬件寄存器(如状态寄存器)。

  2. 一个中断服务子程序中会访问到的非自动变量。

  自动的意思就是在一定的作用域内会自动消亡局部变量,如不专做说明为静态变量,都是动态分配存储空间,存储在动态存储区中)
  非自动就是不会自己释放内存,每次访问改变值后,值会一直保存在那里(静态变量(静态变量并不像自动变量那样使用堆栈机制来使用内存。而是为静态变量分配固定的内存,在程序运行的整个过程中,它都会被保持,而不会被销毁。有作用域:文件或函数),全局变量:只能定义一次,其他的文件要想使用在其它文件定义的这个变量,必须用extern来声明这个变量。有时候会经常用到一个全局变量,如果能够把它作为寄存器变量来使用,显然可以提高程序的性能)

  • 多线程应用中被几个任务共享的变量。

中断服务函数尽可能短

为的是不影响其他中断的到来,也不影响当前中断的再次进入。
1.比如STM32的PVD函数就要短,因为有时候电压不稳,掉电很快就又上电了,这时候又会触发一次PVD中断。如果前一个PVD中断很长的话,这一个PVD中断就会检测不到。
2.中断服务函数长的话,如果有其它低级中断了,就会延误响应中断了。
这些情况有时将导致重大错误!

  • 解决方案

如果中断一次有很多任务需要执行完全(如用PIT定时中断管理多个耗时长的任务,短的话中断里完成没关系),可以在中断子程序中设置一个标志位,在主程序中查这个标志位,当标志为1时,就在主程序中完成这些任务,这样就不会影响其它中断源的中断,也不会使中断产生混乱。
但是此时(前后台系统),中断响应得到标志位时的PC为1,但可能该标志位在PC=6才得到检测if(标志位)
而多任务系统中,低优先级任务可以被高优先级任务打断
(实时性!即不止能配置中断优先级(如通过NVIC),还能配置任务优先级(任务调度)),所以就算这样效率还是不如多任务系统。

static关键字

在这里插入图片描述

清中断标志位作用

响应中断条件是:中断使能和中断标志同时成立时.
一般来讲,响应中断后,有硬件清标志软件清标志两种.(如果硬件不能清标志,说明书会说明).

单片机要靠查询中断标志来判断是否要进入中断,如果你不清除中断标志,本次中断退出,单片机又会检测到中断标志,因此重复进入中断。

ifndefine

  • 在多个c源文件包含对相同(变量,函数)的定义时,在编译阶段是不会报错的,因为该c文件只对自己可见.而在链接阶段,当链接器发现相同的定义,则会报错(重复定义,全局变量链接属性默认为外部链接属性).
  • 多个c源文件则可以包含对相同(变量,函数)的声明.在编译阶段,将声明的(变量,函数)放入该c源文件的符号表中,在链接阶段的重定位阶段,填充(引用其他c文件变量)该文件的符号表.
  • 因此,在头文件中,尽量不要包含定义,只包含变量,函数的声明因为你不能保证,同一个头文件不会被多个c源文件引用.

goto语句

是一种危险的语句,可能会导致很难理清结构。但它可以跳出多层嵌套的循环,而break只能跳出内层循环。

浮点数大小比较

绝对值做差是否小于某个精度的数

零散的注意点

**

rtt:单片机制定内存地址, 上位机访问地址并由DAP以rtt方式传输信息

上位机是指可以直接发出操控命令的计算机

PID:粗调,微调(有滞后),除滞后(增加稳定)。

puts 输出后自动换行 gets换行时候的回车不会进入缓存区

对于scanf()函数中的带有转义字符的格式字符串,C 语言编译并不将其视做转移字符,而是当做普通的字符,所以输入时同样需要原样输入该字符。例如:
scanf("%d,%d\n",&a,&b);正确的输入应当为:
18,33\n

scanf("\n%c %f", &c, &height);中和掉缓存区的回车

volatile int *output=(volatile unsigned int*)0xff800000;定义一个IO端口
(注意 int *a=&b可以 而int*a=0xff800000不规范)
#define CCM_CCGR0 *((volatile unsigned int *)0X020C4068);指向寄存器地址的内容(指针为0x020C4064,被强制修改为指针类型了)

由数组和指针的关系知道,a数组名代表这个地址数值,它相当于一个指针,指向第一个元素(&a[0])。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值