硬件要通电
电有正电和负电
正电表示1负电表示0
.c的文件是源文件
.h的文件是头文件
\' 表示单引号
\" 表示双引号
\\表示一个反斜杠
\n表示换行
\r表示回车
\t表示水平制表符,相当于按下tab键,还有一些转义字符没有提及,因为刚开始用的很少
写C语言
第一步 新建项目
visual c++中的空项目
第二步 新建项
源文件右键 c++文件(.cpp)
第三步 写代码
主函数
int main()
{
函数体
return 0;
}
打印前先引用
#include <stdio.h>
打印printf("要打印的内容");
打印是用十六进制打印的
%c输出字符
%d输出整数
例%2d,输出的数占两位,不足两位的时候用空格补齐1的话输出就是空格1
同样可以3d,4d这样
%f输出单精度小数
%lf输出双精度小数
%s输出字符串
%p输出地址
例
printf(“%c”,要打印的字符变量名字);
存字符串用字符数组
char 数组名字[]="字符串";
或者
char 数组名字[]={'例a','例b','例c','\0'}
因为字符串都是以\0结尾的
\0是字符串的结束标志
在查看数组中字符长度的时候\0不算进去,但是\0在数组中占一个位置(字符空间)
所以算数组长度的时候算进去
ASCII编码
引用放在主函数外(上面)
查看其所占内存量
sizeof(要查看的名字);
要查看的名字为类型如int等不能省略括号
为变量的时候可以省略括号
例:int double 变量名
计算的是变量或类型所占空间的大小,单位是字节
要想打印控制台能显示后能看到
右键项目名称,属性,配置属性,链接器,系统,子系统,控制台
输入函数
scanf("%数据类型例d",&要输入的变量名);
要输入复数个数据时
sancf(".....%例d",&变量名,&另一变量名,....);
getchar()输入字符
是只能输入字符的sancf版本
只能输入一个字符
对应有putchar(要打印的字符)只能输出字符
是printf的只打印字符版本
注意getchar()得到的不是字符而是对应的ASCLL码所以应该存在整形中
打印的时候可以直接把整形变量放入putchar或者用printf来打印整形但是打印出来仍是那个字符
&取地址符号
*取内容符号(解引用符)
sancf函数可能会被报错
报错就在代码第一行!第一行!
#define _CRT_SECURE_NO_WARNINGS 1
全局变量就是在哪个函数都可以用的变量
在主函数外的变量是全局变量
如果全局变量和函数内的局部变量重名则全局变量会被覆盖
局部变量的生命周期是从创建到销毁
静态的局部变量只能初始化一次生命周期是整个程序
静态的全局变量不能被别的类使用
静态的生命周期是程序运行到结束
常量是静态的
被const修饰的变量会成为常变量
常变量的值不能被修改
常量名字要求大写
C语言定义变量要求放在函数体的最前面
否则会报错
创建数组
数组类型例int 数组名字[数组长度]={数组内容};
----------------------------------------
定义标识符常量
#define 标识符 常量
例 AAA 10
枚举常量
创建
enum 枚举名字{枚举常量1,2,....};
定义
enum 枚举名字 对象名字=枚举常量n;
----------------------------------------
中括号[]可以放标识符常量
查看字符串长度
strlen(要查看的字符串名字);
用strlen需要引用#include <string.h>
\x\xx\xxx都是系统规定默认的八进制转义字符
\后跟数字会当成八进制数字转义成字符,转后跟的数字最多三个数字
\x跟数字是十六进制数字,转X后的最多两个数字
ifelse语句和C#一样
定义数组
例int a[3];
例int a[]={1,2,3};
移位操作,移动的是二进制位,二进制的数串整体移动
左移《 右移》
例int a=1;int b=a《2;
代表把a 左移两位后赋值给b
好比b=a+2 a本身是不变的
二进制位操作
&按位与(并)
|按位或(或)
^按位异或(对应二进制位不同为真,相同为假)
位操作是是把符号拆成二进制数字串
然后对每一个bit进行位操作
真是非0假是0
!反逻辑符
会把后面接的第一个字符反逻辑
例!10
因为1是非零为真,反逻辑后假为0
所以结果是00即0
例!0结果为1
特殊!=是不等于
~二进制按位取反
会把后面紧跟的第一个字符的二进制串的0变1,1变0
二进制数串
对于分正负的数据类型时
最高位0表示是正数1表示是负数
对非正数
原码,符号位不变其他位置按位取反得到反码
补码=反码+1
正数在内存存储的时候存储的是二进制的原码
正数的原码反码补码相同
负数在内存存储的时候存储的是二进制的补码
原码是直接按照正负写出的二进制序列
只要是整数,内存中存储的都是二进制的补码
使用的打印的是这个数的原码
&&逻辑与(并)
||逻辑或(或)
判断表达式?表达式1:表达式2;
判断表达式真时读表达式一
假时读表达式2
是
if(判断表达式){表达式1}
else{表达式2}
的缩写
----------------------------------------
常见关键字
auto自动
局部变量也是自动变量,每个局部变量都有auto但是可以省略不写
continue跳过本次循环
struct结构体
strict 结构体名字 对象名字={各个结构体内容}
void无,空
EOF值是-1
signed有正负的
unsigned无负的
int平时前面都有signed只不过省略了
typedef类型定义(给类型起外号)
类型重命名,例typedef int asd;
此时int类型被重命名为abc但是int仍可以用abc可以用
union联合体,共用体
extern引入外部符号
同一项目下的不同类中一个类要用另一个类的某一变量或某一函数时使用
例一个类有变量int a;
另一类引用时extern int a;
例一个类有函数,int b(int x,int y){函数体}
另一类引用时extern int b(int,int);
static静态的
被static修饰的函数和全局变量不能被其他类使用
被修饰的函数会从外部链接属性变成内部链接属性
register寄存器
例register int a=10;
这样会告诉编译器建议把该变量放入寄存器
最终放不放由编译器对寄存器的剩余和是否必要来决定
当某个变量使用特别频繁或者特别重要时可以建议放入寄存器
计算机存储
寄存器
高速缓存
内存
硬盘
由上至下,存储空间越来越大,数据传输速度越来越慢
所以寄存器空间有限,优先重要的频繁的而且大小不大的放入,大的也放不进去
volatile
注,变量名不能和关键字相同
---------------------------------------
定义宏
#define 名字(形参)(方法体)
使用宏
例
#define a(x,y)(x>y?x:y)
int main()
{
int m=1,n=2;
int s=a(m,n);
}
读到宏的时候会把宏的mn当实参传到方法体里然后把宏的名字和实参替换成方法体
相较函数运算更快省代码量
----------------------------------------
32/64位电脑指的是有多少根地址线/数据线
每根线都可以通电,通电就有正负电就有电脑的0或1
例三十二位电脑
00000000000000000000000000000000
到11111111111111111111111111111111
有二的三十二次方种不同的组合
每种组合对应一个地址
所以内存就有2的三十二次方个内存空间
每个内存空间容量有一个字节的大小
所以三十二位电脑内存为4G
存放地址的变量叫指针变量
例
int a=10;
int* m=&a;
printf("%p\n",m)
根据地址找到对应的变量
*m,此时*m就是a
*m=20;(a由10变成了20)
每个指针变量所占内存大小是该电脑位数的大小
例32位电脑,那每个指针变量占32个比特位也就是4个字节
----------------------------------------
字符串拷贝方法
给存字符串的字符数组更换存的字符串
strcpy(字符数组名字,新的字符串);
拷贝完会把新字符串结尾自动加\0
所以打印的时候哪怕后面有旧的字符也不会打印出来只会打印新字符串
这是库函数要先引用string.h
else会和离他最近的为匹配的一个if配对
switch语句中()里的变量只能说整形变量
刷新缓冲区fflush(stdin);
线程暂停sleep(暂停的时间);
时间单位是毫秒,一秒等于一千毫秒
需要引用#include <windows.h>
执行系统命令函数system("要执行的命令");
例cls清空屏幕
需要引用#include <stdlin.h>
判断两个字符串a与b是否相等strcmp(a,b);
注==不能判断两个字符串是否相等
若相等则返回0不相等返回1
define不是关键字是预处理指令
字符类型char等存的字符的ASCLL码所以实际也是整形
求两数之间最大公约数算法\(◎o◎)/!
m,n(m大于n)
while(tenp=m%n){m=n;n=temp;}
循环结束时n存的值就是最大公约数
若一个数可以写成i=a*b(i,a,b均为正整数)
那么a,b中至少一个数是小于等于i的平方根的
偶数里没有素数
sqrt(要开方的数);开平方函数
需要引用math.h
在for(定义变量,条件判断,变量改变)中
break 出循环时不会执行变量改变,continue和正常循环完都会执行一次变量改变
需要引用stdlib.h
伪随机数rand();随机生成一个0到32767之间的正整数但是随机性低明显,需要放int里
如要产生[m,n]范围内的随机数num,可用:\n\nint num=rand()%(n-m+1)+m;
真随机数srand(种子值);不给种子值会默认为1,种子值不同生成随机数的算法不同,使种子值变化会是随机数生成真正的随机
srand函数是根据种子值改变随机算法的初始值
所以要跟rand一起用
srand改变算法rand根据算法生成值
srand不要频繁调用,否则会不随机
调用一次就可以了
要使种子值不断的自动变化,可以使用时间戳
使用时间戳需要引用time.h
时间戳的类型是time_t
时间戳函数time(指针);
指针有两种方式int a=0;
NULL或0
a=time(0);
变量地址
time(&a);
第一种是空指针过去然后返回值赋值给变量
第二种会自动赋值给变量
----------------------------------------
系统命令函数system("要运行的函数");
设置关机
shutdown -s -t 关键倒计时单位为秒;
shutdown -a;取消关机
memset数组中部分数值的替换
memset(数组名字,'要替换成的字符',替换的长度)
set设置
memory内存
例int a[]="hello world";
memory(a,'*',5);
然后数组a会变成"***** world"
打印出来也是这样,这个函数不会给他添\0
函数的调用有两种
传值调用
把值当实际参数传过去
形式参数是实际参数的临时拷贝,会新建一块空间给形式参数存,所以形式参数实际参数地址不同,改变形式参数实际参数不改变的!
传址调用
把地址当成实际参数传过去
因为传过去的是实际参数的地址,所以解引用后形式参数就相当于实际参数了,实际参数随形式参数一起改变
注意,数组本身就是地址,传数组就是传址
函数间可以相互调用
传给函数的实际参数可以是函数的返回值
函数的规范声明和定义
声明要新建一个头文件放函数声明
声明例
#ifndef __ADD_H__
#define __ADD_H__
int add(int,int);
#endif
#pragma once
等效ifndef的防止头文件多次引用
但是新版才能用,较老版本编译器不行
三句#号是为了防止重复引用,养成习惯每个头文件都要有
定义要新建一个源文件放定义
定义例
int add (int x,int y){int z=x+y;return z;}
主函数那里要使用时引用一下存这个函数的头文件就可以了
引入自己的头文件用“”引入库函数的头文件用<>
printf的返回值是int类型,具体返回数值是返回打印了几个字符,打印一个叫返回1打印一百个就返回100
栈里放局部变量和函数形式参数和函数的调用
堆里放动态开辟的内存,malloc,calloc
静态里放全局变量和static修饰的变量
栈溢出stack overflow
数组名是首元素地址(放在sizeof函数中时除外)
&数组名,取出的是整个数组的地址
sizeof求整个数组占内存大小,单位是地址
数组传参传过去的不是整个数组,是第一个元素的地址
数组名字++;
代表现在是第二个元素的地址了
取到的地址是首元素的或者数组开始的地址
加加得到的不一样
首元素地址加加得到下一个元素的地址
数组的地址加加得到下一个其他变量什么的地址
所以直接数组名字的地址和取数组名字地址后的地址虽然地址显示一样但是涉及到的范围不同,第一个是涉及整个数组第二个是涉及整个元素
strlen遇到\0才停不然随机数
sizeof要算上\0
二维数组例
数组名字[有几行][有几列];
定义声明使用和普通数组相同。
小写字母和大写字母的ASCLL码值差32
for循环在只有一句的时候{}可以省略
移位分为算数移位和逻辑移位
算数移位左边补符号位右边丢弃
逻辑移位左边补0右边丢弃
数组是传地址,指针学的时候教接收地址的是指针变量,是指针。所以函数调用传数组的时候结束数组的其实是一个指针
&&一旦遇到为假右边便不在判断和执行
||一旦遇到真就停止
&&一旦遇到假就停止
整型提升
例字符变量打印的时候用%d
这时候会隐式类型转换
把变量原码拿出来然后因为int占四个字节char一个字节在高位补齐,补的是char原码的符号位,使用完转换回去的时候从低位数数八位
正数的原码反码补码相同
负数的反码是原码的按位取反,补码是反码加一
操作符具有结合性和是否控制求值顺序
结构体委托类等其实是自己定义一个类型,对象就是这个类型定义一个变量
指针的int double等类型不是代表只能指向int double什么的,而是代表这个指针指向的那个变量被解引用时给指针开放的内存大小
如果是double指针指一个int变量那么解引用时int变量只会开放一个字节的空间给指针访问
所以指针类型的意义是控制指针“每次”改变多大内存的数据
指针类型的意义还有
决定了地址加一,指针走一步地址走多远(指针的步长),int指针一步地址后走四步,因为int占四个字节
没有初始化的指针是野指针,指超出范围的地方是野指针,严防野指针
将指针指向的空间释放需要让指针指向空,即NULL
指针加减整数是指针移动
指针加减指针是指针间的元素个数(只能同类型指针且指同一数组)
指针指数组元素时可以向后越界一个位置但是不能向前越界一个位置
取地址的数组名和sizeof函数中数组名代表的不是首元素地址,而是整个数组的地址
其他情况的数组名都是代表首元素地址
指向指针的指针变量是二级指针
二级指针定义需要指针类型**
所以类型后面跟几个*就是几级指针
指针数组是数组,存着指针
数组指针是指针,指着数组
函数的形式参数保存在栈中,栈存局部变量
全局变量不初始化默认为0
局部变量不初始化会随机值
算数转换
一个有符号数和一个无符号数运算会把有符号转换为无符号
符号位变有效位
赋值的=优先级高于逗号表达式的,
system库函数pause暂停
效果是C#的console.readkey()一样
n=n&(n-1)可以把二进制位的最右边的一消失且其他位置不变
&换成|则最右边0换成1
结构体关键字struct
结构体类型struct 结构体名字
结构体对象struct 结构体名字 对象名字
struct 结构体名字{结构体内容}全局结构体变量名字;
全局的可以不创建}后直接;
全局变量要尽可能少创建
结构体类型名字重命名
typedef struct 结构体名字{结构体内容}结构体类型小名;
结构体当函数参数是传值,传址需要指针
传址比传值效率高
结构体指针可以用->指结构体内容
函数传参要压栈
栈
局部变量 函数形式参数 函数调用
先进后出后进先出
堆
动态内存分配 malloc/free realloc calloc
静态
全局变量 静态变量
函数传参是从右向左传
线形数据结构
顺序表 链表 栈 队列
树形数据结构
二叉树 图
结构体传参一定要传址不然库库压栈
栈的使用是由高地址往低地址
数组元素放栈里面也是从右往左放
C的for(i=0;判断条件;条件改变)
变量初始化这里不能定义,定义变量要放在代码最前面
用assert函数需要引用assert.h
断言assert(条件判断);
触发条件时程序会报错
const修饰指针
放在*前面,修饰的是“*指针变量名”,即修饰的指针指的对象,所以指针指的对象在操作这个指针的时候是常变量不能被修改,即该指针不能修改被指对象
放在*后面,修饰的是“指针变量名”,即指针变量本身不能被改变,不能再选择指的对象但是可以改变所指对象的值
*前解引用后所指对象暂时当常变量来操作
*后指针所指对象固定不能再指其他变量
一个指针可以被两个const修饰一个*前一个*后
----------------------------------------
内存中存一个整数的时候是把一个整数的二进制位补码换算成十六进制的存起来
内存存储有两种模式
大端存储模式
正着进制存
进制低地址存内存高地址
进制高地址存内存低地址
小端存储模式
倒着进制存
进制低地址存内存低地址
进制高地址存内存高地址
进制串左边高地址右边低地址
内存串左边低地址右边高地址
大端模式给人看
小端模式给电脑看
计算机是字节为单位,一字节八比特位
所有二进制转十六进制后是每两个十六进制位放一起分四个放即四个字节为内存串
若64位系统则八个字节内存串
\0的ASCLL码为48,字符0也是48
浮点型的二进制读取
可以用-1的S次方×M×2的E次方得出原数
其中对于32位的浮点数
从左往右第一位表示S
第二位到第九位表示E
再往后表示M
64位系统时浮点类型换算
第一位S二到十二位E其余M
M取值范围大于等于1且小于2
因为M整数部分一定为1所以二进制中存M的那些位其实存的小数点后
E是无符号整数
存时要加上E最大值的一半再往里面存
例32位系统
E占8位,所以E最大值2的8次方-1为255
中间值为127
浮点数转二进制存储
先写出它的二进制数串,小数点后转二进制是×2取整数部分。(知道×后小数部分为0或者位数超过M的存放位数)
然后整数部分二进制右移成1.XXXXXX格式成M
右移几位E就是几
M存二进制里取小数点后数字串不足的位在后面补0
E+最大数的一半然后正常数转二进制后放进去
----------------------------------------
指针的进阶
字符指针
给一个字符指针指向一个为定义的字符或者字符串,则会自动创建一个该字符且为常量字符或常量字符串
指针数组
是个数组,存指针
例 int* 数组名字[数组容量];
定义了一个可以存int*类型的指针的数组
初始化的话直接把地址放进去,直接指变量
数组指针
指向数组的指针
例 int (*指针名字)[指向数组的容量];
类型要和所指数组类型相同
[]优先级大于*不用括号扩起来的话名字先和[]结合成数组然后和int*结合成存int*的数组
所以要加()让名字先和*结合成指针
数组指针指向一个数组后解引用相当于这个数组的名字即打印所指数组的某个元素可以
(*指针名字)[i]==数组名字[i]
==*(*指针名字+i)
把指针名字解引用成数组名字即首元素地址
加i使地址后移i
再解引用把移动后所到地址解成元素
二维数组名字也是代表首元素地址,但是要把二维数组当成一维数组来看首元素,例a[3][3]的a代表的是首元素地址而他的首元素是整个a[0]这一整行
数组传参和指针传参
数组名字就是地址,可以用数组接收,可以用数组指针接收
指针传参可以用同级别指针接收,也可以高一级指针接收那个指针的地址
函数指针
例int(*a)(int x,int y);
指针a指的函数的形参为int x,int y返回类型为int
括号里是类型代表强制类型转换
例
(*(void (*)())0)();
void(*)()是函数指针类型,指的函数返回类型是void且形式参数为空的函数
所以是把0整型转换成函数指针类型即函数地址
然后*把这个地址解引用成函数再最后一个小括号给实参调用在0地址的void空形参的函数
一个函数把函数名字和形参去掉,剩下的是函数返回类型
例
void(*signal(int,void(*)(int)))(int);
()比*优先级高所以声明一个名为signal的函数形式参数为int和一个函数指针返回类型是函数名和形参意外所以去掉后得
void(*函数名 形式参数)(int)
所以返回类型是一个函数指针
对于繁琐复杂的类型可以用typedef给他定义一个简单的小名
例void(*)(int)
typedef void(*小名)(int)
此时上面例可以这么写
小名 signal(int,小名)来声明函数
通过函数指针调用所指函数可以不解引用直接
函数指针名字(实参)来调用
(*加函数指针名字)对函数指针是摆设完全没有作用
没有()的时候*加函数指针除外
函数指针数组
函数返回类型(*数组名字[数组容量])(函数形式参数类型)
操作数输出相似的函数可以放同一数组中
然后数组名[变量]来一句话省去switch来选择调用
指向函数指针数组的指针
回调函数
通过函数指针调用的函数
回调函数是不自己调用而是被其他函数调用
一个函数的形式参数中包含函数指针
可以把多个相似函数有共同的操作放进来可以省几遍一样的代码
指针和数面试题的解析
----------------------------------------
对应一个函数如果他的功能只对int可以使用对lang,char等使用不了那么这个函数非常有局限性
void的指针是无类型指针,可以指任何类型
void的指针不能进行解引用操作因为不知道你指向的类型不知道所指类型的大小
无类型指针也不能进行加减乘除等操作
只能用来接收后进行判断然后回调适合类型的对应函数
用来临时存东西或者来打破函数局限性
打破函数局限性例
无类型指针接收某类型参数后选择对应的函数传无类型指针过去
过去后强制转换类型然后解引用进行操作
任意类型排序函数qsort
数组名字 数组长度 数组第一个元素的大小 调用函数
需要引用头文件stdlib.h
sizeof(数组名),整个数组计算整个数组大小
&数组名,整个数组的地址
除此之外都是首元素地址
gets(存放的变量)读取一行放变量里
pow(数,N)返回的是数的N次方,但是double类型
十六进制小的放哪就是什么端
放低地址就是小端,高地址就是大端
strcat(字符串1,字符串2)把2接到1的后面
需要引用stdio.h
不能自己追加自己后面,但是可以追加和被追加的两个不同!的字符串变量里面的字符串相同。
即不能strcat(字符串1,字符串1)
原理,字符串1的\0开始2的\0结束
strncat(字符串1,字符串2,接的长度l)
把2的前l个字符接1后面
strstr(字符串1,字符串2)在1里找是否哪个子串与2相等
如果存在会返回字符串2首元素在1里的地址
%o八进制数
%mXo,X表示数字,输出数不足X位在前面自动补字符m
同理%oXm在后面补齐
在%后面跟个#就表示输出数前先输出一个0
sizeof是关键字不是函数,所以说编译时确定的,即使里面有表达式也是编译阶段运行完成
关键字会在编译阶段运行
0后面跟数字表示八进制数
0x开头16进制数
1到9开头十进制数
e后面必须有整数
当数据传输类型不一样时,会发生数据类型转换。低精度转高精度是隐式转换,高精度转低精度是强制转换但是会数据丢失
static_cast<>()来转换才不丢失数据
真1假0
例int a=5,b=4,c=3,d;
d=(a>b>c);
a>b真返回1,1<c假返回0
所以d=0;
getline函数,读取一行字符直到换行符,不读取换行符
----------------------------------------
结构体
关键字 标签
struct XXX
{
成员变量
}该结构体对象(全局变量);
在main里定义的变量是局部变量
关键字前面如果跟typedef 那定义全局对象的位置就变成起小名的位置。
链表是
每个链表结构体都是节点
结构体里面有指向自己同类型的结构体指针指向下一个节点
结构体内存对齐
第一个成员变量放在与结构体变量偏移量为零的地址处
其他成员变量要对齐到对齐数的整数倍的地址处
(对齐数等于编译器默认的一个对齐数与该成员大小选出较小值,vs默认8,gcc无默认)
结构体总大小为成员变量中最大对齐数的整数倍(每个成员变量都有自己的对齐数)
如果嵌套了结构体,嵌套的结构体的对齐数按他自己的成员变量中最大的那个对齐数处理
#pragma pack(对齐数);设置默认对齐数
#pragma pack(); 取消默认对齐数
位段 结构体的一种类型
成员必须是整型且类型一致
位,是二进制位,是bit位
位段开辟空间是一次开辟一个字节(char)或者四个字节(int)
位段不能跨平台
例: int a :2;(数字不能超过类型本身大小)
不同编译器不同版本,位段开辟空间和存储方式可能不同
分配内存从左向右从右向左不确定,多余位利用还是舍弃不确定
int位段是有符号数还是无符号数是不确定的
----------------------------------------
枚举的优点enum
增加代码的可读性和可维护性
和#define定义的标识符比较枚举有类型检查,更加严谨
防止了命名污染(封装)
便于调试
使用方便,一次可以定义多个常量
----------------------------------------
联合(联合体,共用体)union
成员公用同一块空间,联合体大小至少是最大成员的大小,如果小于最大对齐数,就自动补成最大对齐数的整数倍
同一联合体的不同成员不能同时使用
----------------------------------------
柔性数组
结构体最后一个成员如果是数组的话可以不给长度初始化即为柔性数组,柔性数组减少了出错,加快了系统速度。柔性数组是可以控制大小的数组(利用malloc)
柔性数组前至少有一个成员
----------------------------------------
文件操作
打开文件fopen
关闭文件fclose
"r"只输入 为了输入数据,打开一个已经存在的文本文件
"w"只输出 为了输出数据,打开一个文本文件
"a"追加 向文本文件末尾添加数据
"rb"只输入 为了输入数据,打开一个二进制文件
"wb"只输出 为了输出数据,打开一个二进制文件
"r+" 输入输出 为了输入和输出打开一个文本文件
"w+" 输入输出 为了输入输出建立一个新的文件
"a+" 输入输出 为了在文件末尾输入输出打开一个文件
"ab+"输入输出 为了在文件末尾输入输出打开一个二进制文件
有w的在没有指定文件时会新建指定文件,其他会出错a+,ab+也很新建指定文件
..上一级路径
.当前路径
键盘,标准输入设备,stdin
屏幕,标准输出设备,stdout
perror("错误提示");
出现错误时收错误码然后打印出
错误提示:错误码转换出的错误信息
源文件.c 目标文件.obj 可执行程序.exe
源文件逐个被编译器编译成目标文件
随后各个源文件对应的目标文件以及被用到的库函数所在的链接库被链接器链接,形成单一而完整的可执行程序
源文件是文本文件
可执行程序是二进制文件
将源文件转换成可执行程序需要经过编译和链接
源文件在翻译环境下变成可执行程序
可执行程序在运行环境下运行
翻译环境=编译(编译器)+链接(链接器)
编译(编译器)=预编译+编译+汇编
预编译进行的是文件操作
例(把引用的库函数加入进来并删除注释,)
预编译完.c变.i文件
预处理指令#开头
#define定义标识符,定义宏
#include
编译是把c语言代码翻译成汇编代码
语法分析,阅法分析,语义分析,符号汇总
编译再.i变.s文件
.s文件是汇编文件
汇编是形成符号表然后把汇编代码转换成二进制代码.o
汇编完成目标文件(二进制文件).obj
链接
合并段表
符号表的合并和符号表的重定位
如果从表去找找不到对应函数则链接器报错
预定义符号
读代码当时日期__DATE__
读代码当时时间__TIME__
代码所在行__LINE__
读代码所在文件__FILE__
当前所在函数的名字__FUNCTION__
宏
#define 宏的名字(宏的参数) 宏的表达式
将宏的参数代入表达式把宏替换掉
例
#define add(X) X+X
int main()
{
int a=add(3);
return 0;
}
结果为a=6
#变量
此时会把变量转换成字符串
例int a=100;
#a则表示“a”,而不是100
变量##变量
把左右两边的变量合成一个标识符
int a=1;int b=2;
a##b得出标识符ab
#under 标识符或者宏
移出标识符或者宏
宏不能递归,不能调试
条件编译
#ifdef 标识符例DEBUG
例:printf("123456");
#endif
若前面没有定义标识符DEBUG
则预编译阶段会把这些代码删去。
反之会进行运算
ifdef 是if defined 简写
#ifndef 标识符
语句
#endif
当没有定义该标识符时运行,定义时反而不运行
#if 常量表达式
语句
#endif
#if 常量表达式
语句
#elif 常量表达式
语句
....
#else 常量表达式
语句
#endif
允许嵌套
预处理指令
#define
#include
条件编译指令
#pragma
#error
#line
......