预处理命令:gcc -E -omain.i main.c对main.c只执行预处理
相当于文本替换,只管替换,不计算
宏一律使用大写,结尾不写; 宏定义时 替换的只可以读。(左值右值)
不带参宏 #define PI开始宏定义
#under PI 结束宏定义
带参宏 #define SUM(a,b) a+b
#define DEBUG 只定义一个数
文件包含:
有根目录的路径叫绝对路径
条件编译: //主要用于编译
#if 用法:
#if 常量a
…程序段1…
#else
#if 常量b
…程序段2…
#else
…程序段3…
#endif
#endif
这里表示,如果常量a为真(非0,随便什么数字,只要不是0),就执行程序段1。当常量a为0,常量b为真时,执行程序段2;
当常量a为0,常量b为0时,执行程序段3;
#if 0 注释代码
#endif if截止
#ifdef XXX 如果定义了标识, 则加入下面的代码
#ifndef XXX 如果没有定义了标识,则加入下面的代码
#if 如果条件成立就加入下面的代码
#else 如果条件不成立就加入下面的代码
#ifndef _DEBUG_H_
#define _DEBUG_H_
#endif //防止重复定义出错。
函数的调用可以作为一个函数的实参,可以出现在执行语句/表达式中
函数的定义不可以嵌套,但函数的调用可嵌套
#include <math.h>
abs(x);
fabs(x); //求绝对值
fabs的参数为double型,返回值也是double型abs的参数为int型,返回值也是int型。
指针: //数据结构
整型是装整数的数据类型 整型变量 整型常量
指针是装地址的数据类型 指针变量 指针常量<=>地址常量
使用指针时要给出基类型 *p的类型是基类型
地址装的是j的地址,则称指向j的指针变量
*p指向i的本身,即*p=i 间接访问形式
*p的实际操作过程:1.定位
2.向后偏移sizeof基类型
3.整体的将偏移好的内存当基类型看
保存的不确定的地质的指针称为野指针
指针自加一会向后偏移sizeof基类型,即加上sizeof基类型
p++的结果调用还是p,后 p=p+1
例 int i = 100 int *p = &i *p++,(*P)++ 输出为 101 100 // (*p)++后p = 101
例 int i= 100; int*p = &i ; p = &i,*p = i
*&i = i &*p = &i
*&p = &i
使用指针时要给定一个确定的内存地址,野指针没给定确定位置存在内存的随机位置
(类型名)强制类型转换
数组中:
a[i] <=> *(a+i)
*mid = a[mid] mid = 第mid个元素
指针的减法要保证基类型一样
快排:从右边开始 只能排数组
1、从末尾开始寻找第一个比a[0]小的数。
2、从首段开始寻找第一个比a[0]大的数。
3、从该值开始首尾分为两段,递归调用自身各自进行快排。
4、递归循环终止条件为 首尾相见即begin >= end。返回值为 ;
#include <stdlib.h>
void qsort(void *base, size_t nmemb, size_t size,int(*compar)(const void *, const void *));
入口地址 元素个数 一个元素所占的字节 比较方法
const 1、给定的变量。只可以定义,不能赋值。 制度变量 //但是又是一个左值
2、定义指针变量时 const char *q,无法通过指针变量修改指针变量所指向的值 //常指针
只能读,不能写 针对不需要改写的可以加一哈
char *const q = s;只读的指针变量
strncpy(char *dest ,const char *pstr,n)只capy n个
万能指针 void *p;大多数用于强制转换 可以用任意一种指针对其进行赋值
int *p =(int *)p 强制转换为int型
a[]={1.23} &a指向数组的指针
数组指针: 指向整个数组的指针 int (*p)[10] 指向一个有10个元素的数组指针 ,即p为指向一维数组的指针
*p 即为数组数组名。*(*p) = a[0]
*(*a(a+1)+2) < = > a[1][2]
a 即为二维数组第一行的首地址 *a是指向列的首地址 &a,二维数组的地址 &a+1即加了sizeof基类型个字节
a[4][3]
char *型 a[0] <=> *(a+0) a[0][1] <=> *(a+0)+1 // 地址 *(*(a+0)+1)//值
指针数组:指针数组的元素只能存放地址
int i; 定义整型变量i
int *p; p为指向整型数据的指针变量
int a[n]; 定义整型数组a,它有n个元素
int *p[n]; 定义指针数组p,它由n个指向整型数据的指针元素组成
int (*p)[n]; p为指向含n个元素的一维数组的指针变量
int f(); f为带回整型函数值的函数
int *p(); p为带回一个指针的函数,该指针指向整型数据
int (*p)(); p为指向函数的指针,该函数返回一个整型值 ,
int **p; p是一个指针变量,它指向一个指向整型数据的指针变量
返回值为指针的函数为指针函数。可以返回全局/静态局部变量地址/主函数传入地址
//在函数中不能返回静态局部变量的指针
动态内存分配:
#include <stdlib.h> //标准库
void *malloc(size_t size); 将内存空间分配到堆区 。
void *calloc(size_t nmemb, size_t size); 连续分配n个,size的内存
void *realloc(void *ptr, size_t size); 重复申请内存空间。 将前申请的空间销毁
q = realloc(p,sizeof(int));
p =q;
p = calloc(n ,sizeof(int)); 1.只要动态申请后才有。
char *p; 2.空间大小是连续的
p =malloc(1024*1024*2); 没有申请成功则返回NULL
if( p != NULL) 3.不能将对p写入 p=&a;(error)
{
。。。。。
free(p) ; 归还内存空间 不归还内存会造成内存泄漏
p = NULL;
}
else
puts(alloc error);
window和linux 中有垃圾回收器,可以自动回收没有归还的内存
函数指针:指向函数的指针。函数的函数名即函数的第一条指令的地址
int (*p)(int ,int);描述p的数据类型是指针,可以指向一类函数,函数有两个int型,且返回值为int型
(*p)指向函数的指针。
int sub(int n,int a)
{
......
}
int (*p)(int , int ); 类型名:int (*) (int ,int);
p = sub;
printf("%d\n",p(3, 4)); //调用函数sub
(*p)( 3 ,4) < = > p (3 ,4). 调用函数
回调函数 由另一个函数间接去调用。
指针的指针 char **p 修改主调函数中的指针需要传递指针的地址
指针数组作为函数参数,形参是指针的指针
二维数组作为函数参数,形参是一维数组的指针
结构体 struct student{。。}; 结构体定义后需要在}后加;
调用时 struct student s; struct student即为类型名
结构体允许整体赋值。 keil 默认一个字节
可以在定义结构体的同时定义结构体变量
struct stu{
char *name; //姓名
int num; //学号
char sex; //性别
float score; //成绩
} stu1, stu2;
将变量放在结构体定义的最后即可。
定义结构体数组和定义结构体变量的方式类似
struct stu{
char *name;
int num;
char sex;
float score;
}class[5];
指针也可以指向一个结构体变量。定义的一般形式为:
struct 结构体名 *变量名
一个结构体指针变量虽然可以用来访问结构体变量或结构体数组元素的成员,
但是,不能使它指向一个成员。也就是说不允许取一个成员的地址来赋予它
内存对齐: 默认四字节对齐,没有按两字节对齐
最终大小可以被四整除 被二整除
如果偏移量可以整除基类型,则可以存放
结构体运算符
. -> 两种方式都可以找到结构体中成员 (*pstu).num pstu->num
使用 -> 与该成员类型一致,可以修改主函数的参数
float 占 4个字节。 double 占8个字节
#pragma pack( 1) 此后程序按1个字节对齐;
const + ->可以实现 . 的稳定功能
头文件中只放声明#include"b.h"
写一个电子时钟,可在控制台运行
%02d,不够2位的补零
#include <unistd.h>
unsigned int sleep(unsigned int seconds);
sleep(1)延时1秒 延时程序
fflush(stdin) 功能是清空输入缓冲区,通常是为了确保不影响后面的数据读取 。
清除文件缓冲区,文件以写方式打开时将缓冲区内容写入文件
地址:区分内存中不同空间的编号
指针:地址就是指针。指针就是地址(指针比地址多了指向的概念)
指针变量:存放指针(地址)的变量,后来也被称为指针
访问内存空间的方式 直接访问:使用变量名访问。
间接访问:使用指针间接访问。
指针的运用:& 获得一个变量崽内存空间的首地址 int *p指向四个字节的内存 char *p指向一个字节的内存
*:取指针所对应的空间
*p:作为左值,取指针p指向的空间
*p:作为右值,取指针p指向空间对应的值
指针变量的定义:
int *p;
char *p;
int (*p)[5]; //定义指向整型五个元素数组的指针
int (*p)(void) //定义一个指向int类型返回值,缺省函数参数类型参数的函数指针
typedef
指针初始化: int *p = NULL;
int *p;
int a = 100;
int *p = &a;
指针的算数运算:
+ p+2
- p-2
++ p++ 向内存高地址偏移指向数据类型大小个字节
-- p-- 向内存低地址偏移指向数据类型大小个字节
指针用法: void * 0x2000只是存放了一个地址是0x2000
int * 0x2000指向0x2000往后的四个字节
const int *p p可以改变,但是*p不可以变
int *const p p不可以变,*p可以变
int const *const p 都不可以变 // 一定要初始化
1、函数体内部想改变函数体外部指针变量的值,传二级指针
2、指针数组:传参
char *str[5];
fun(str ,5);
void fun(char **Str ,int len)
指针和数组的关系:(数组的数组名是指向数组第一个元素的指针)
指针和一维数组的关系:
注:1、数组的数组名是指向数组第一个元素的指针
2、a[n] == *(a+n) == *(p+n) ==p[n]
3、a:int * 有两种情况式子不成立:1、sizeof时
2、&时
4、数组的本质
*(a+n) ==a[n]
&a 指向数组的指针。
指针和二维数组的关系:二维数组的数组名是指向第一行元素的一个数组指针
和二维数组的本质:由一维数组构成的一维数组
函数指针:指向函数的指针。函数的函数名即函数的第一条指令的地址
用指针来处理链表:有头链表 指针域 可以指向自身
无头链表
最后一个节点的指针域部分是啥一个NULL
公用体:所有元素公用一段内存空间,地址都一样,依然有内存对齐 判断计算机大小端存储
可以作为参数传递,可以用指向共用体变量的指针
起作用的成员是最后一次存放的成员,存入新的成员后原有成员就失去了作用
不能对共用体变量名赋值,定义共用体变量时不能对它初始化,
枚举 enum 变量名{ 枚举值列表 }; 具体实现是int型
结构体一样,枚举变量可以先定义后说明,也可以在定义的同时说明
enum week a, b, c;
enum week{sun, mon, tue, wed, thu, fri, sat} a, b, c;
枚举值为常量,不是变量,不能赋值。枚举值默认从0开始,逐个加1。
如果一定要使用数值,必须使用强制类型转换:
a = (enum week)1;
b = (enum week)6;
位运算符 只能是整型和字符型,尽量用usigned
& 按位与 双目运算 作用:指定位置清零
| 按位或 双目运算 作用:指定位置置一
^ 按位异或 双目运算 作用:指定位置取反
~ 取反 单目运算
<< 左移 双目运算 | 加 <<实现打开指定位置
>> 右移 双目运算 1、算数右移,取决操作数正负,正补0,负补1
2、逻辑右移, 无论是正负,一律补0
某一个变量连续对同一个数做异或运算,结果还是本身
位段/位域:依旧遵从内存对齐
struct Date
{
int year :16; 此后year所占的字节为16
int month :8; 此后month所占的字节为8
int day :8; 此后day所占的字节为8
}
存储类型:auto static extern register
生存周期:
变量从分配空间到空间被回收的整个过程
1.auto(默认) 自动变量
auto类型将变量存放到栈区
栈区:
1.未经初始化前为随机值
2.执行到变量定义时,为变量分配空间
3.超过变量的作用域,空间被回收
作用域:离该定义变量最近的大括号内
2.register
register寄存器变量 register int a;
将变量存放到CPU内部的寄存器里面
如果寄存器存满等价于auto类型
3.extern
extern声明外部变量
定义:需要分配空间
声明:该变量存在,并且声明其类型
初始化:定义变量时为其赋值
赋值:将值赋给变量空间
4.static
static静态变量
存放到数据区:
1.未经初始化前为0值
2.程序编译时分配空间并初始化一次
3.程序结束回收空间
作用:
1.修饰静态变量
2.限定变量/函数作用域
3.防止多文件编程时变量冲突
linux 下串口编程VTIME和VMIN的设置
VTIME定义要求等待的时间量(取值不能大于cc_t)。
VMIN定义了要求等待的最小字节数。
c_cc[VTIME] = X; //定义等待的时间
c_cc[VMIN] = Y; //定义了要求等待的最小字节数,这个字节数可能是0
如果VTIME取0,VMIN定义了要求等待读取的最小字节数。函数read()只有在读取了VMIN个字节的数据或者收到一个信号的时候才返回。
如果VMIN取0,VTIME定义了即使没有数据可以读取,read()函数返回前也要等待几百毫秒的时间量。这时,read()函数不需要像其通常情况那样要遇到一个文件结束标志才返回0。
如果VTIME和VMIN都不取0,VTIME定义的是当接收到第一个字节的数据后开始计算等待的时间量。如果当调用read函数时可以得到数据,计时器马上开始计时。如果当调用read函数时还没有任何数据可读,则等接收到第一个字节的数据后,计时器开始计时。函数read可能会在读取到VMIN个字节的数据后返回,也可能在计时完毕后返回,这主要取决于哪个条件首先实现。
如果VTIME和VMIN都取0,即使读取不到任何数据,函数read也会立即返回
tcgetattr是一个函数,用来获取终端参数,成功返回零;失败返回非零, 用非规范模式
int tcgetattr(int fd, struct termios *termios_p);
参数fd为终端的文件描述符,返回的结果保存在termios 结构体中,该结构体一般包括如下的成员:
tcflag_t c_iflag;
tcflag_t c_oflag;
tcflag_t c_cflag;
tcflag_t c_lflag;
cc_t c_cc[NCCS];
tcsetattr是用于设置终端参数的函数
int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);
TCSANOW:不等数据传输完毕就立即改变属性。
TCSADRAIN:等待所有数据传输结束才改变属性。
TCSAFLUSH:等待所有数据传输结束,清空输入输出缓冲区才改变属性。
错误信息:
EBADF:非法的文件描述符。
EINTR:tcsetattr函数调用被信号中断。
EINVAL:参数optional_actions使用了非法值,或参数termios中使用了非法值。
ENOTTY:非终端的文件描述符。
exit(1)表示异常退出,在退出前可以给出一些提示信息
exit(0)表示正常退出。
return是返回函数调用,如果返回的是main函数,则为退出程序
exit是在调用处强行退出程序,运行一次程序就结束
随机数:库函数中系统提供了两个函数用于产生随机数:srand()和rand()。 原型为:
函数一:int rand(void);
返回一个[0,RAND_MAX]间的随机整数。
10.0*rand()
函数二:void srand(unsigned seed);
参数seed是srand()的种子,用来初始化srand()的起始值。 播种只用播一次
srand((int)time(0));
reset 终端的恢复
1:文件路径 文件描述符应是唯一的。文件指针(值)不是唯一的,但指向的对象也应该是唯一的。
2: FILE中包含fd的信息,而且还包含IO缓冲,所以可以理解为FILE是对fd的封装,是C语言的标准形式 可以看做一个结构体
seq [OPTION]... LAST //依次产生从1到 LAST的数
seq [OPTION]... FIRST LAST //依次产生从FIRST 到 LAST的数
seq [OPTION]... FIRST INCREMENT LAST // 每次加INCREMENT 产生从FIRST 到 LAST的数
sprintf(buf,“%d”,a);
把任意基础类型转换成字符串 写到buf中
可以实现:数字转字符串
fprintf(fp,“%d”,a);
把任意基础类型转换成字符串 写到文件中
strtok(buf,“:”) 把buf 按: 分开
字符串分割,会对原串进行修改
charname = strtok(buf,"?;
char age = strtok(NULL,"?;从name后面开始找
char* addr=strtok(NULL,"?;从age后面找
char* phone=strtok(NULL,"?;从addr后面找
strstr (buf,":")在总串中找字串
char*name = strstr(buf,":");
char* age = strstr(name+1,":"); 从name+1后面开始找
char* addr=strstr(age+1,":");
char* phone=strstr(addr+1,"\n");
*name='\0';
*age='\0';
*addr='\0';
*phone='\0';
memmem(buf,":",len)在二进制文件中查找字符串
len是二进制文件的长度
清屏命令
第一种clear命令
清空屏幕
相当于向后翻了一页
之前的内容,依然保存在屏幕上
clear
第二种res命令
完全清空屏幕
之前的内容会同时清空
屏幕内容较多时,速度较慢
reset
第三种printf命令
完全清空屏幕
类似reset ,但速度稍快
printf “\033c”
第四种命令
快捷键 Ctrl + L
功能与clear命令相同
宏替换时 ##和#的区别
#是替换字符串
##是链接函数
(待定…)