C语言
C基础知识细节要点:
C语言有34种运算符.
目录文件(linux所有设备和目录都是文件属性)
linux权限:
数字权限: 4读 2写 1执行
属主 属组 其他人
rwx rwx rwx
421 421 421
drwxr-xr-x(rwx,r-x,r-x)三位一组
chmod u-x,g-wx,o=- xx u代表属主,g代表属组,o代表其他
命令格式:
命令动词 选项 参数(至少一个空格)
C语言简洁、紧凑,使用方便、灵活。ANSI C一共只有38个关键字:
auto :声明自动变量
short :声明短整型变量或函数
int: 声明整型变量或函数
long :声明长整型变量或函数
float:声明浮点型变量或函数
double :声明双精度变量或函数
char :声明字符型变量或函数
struct:声明结构体变量或函数
union:声明共用数据类型
enum :声明枚举类型
typedef:用以给数据类型取别名
const :声明只读变量
unsigned:声明无符号类型变量或函数
signed:声明有符号类型变量或函数
extern:声明变量是在其他文件中声明
register:声明寄存器变量
static :声明静态变量
volatile:说明变量在程序执行中可被隐含地改变
void :声明函数无返回值或无参数,声明无类型指针
if:条件语句
else :条件语句否定分支(与 if 连用)
switch :用于开关语句
case:开关语句分支
for:一种循环语句
do :循环语句的循环体
while :循环语句的循环条件
goto:无条件跳转语句
continue:结束当前循环,开始下一轮循环
break:跳出当前循环
default:开关语句中的“其他”分支
sizeof:计算数据类型长度
return :子程序返回语句(可以带参数,也可不带参数)循环条件
C语言一共有32个关键字,如下所述:
auto :声明自动变量
short :声明短整型变量或函数
int: 声明整型变量或函数
long :声明长整型变量或函数
float:声明浮点型变量或函数
double :声明双精度变量或函数
char :声明字符型变量或函数
struct:声明结构体变量或函数
union:声明共用数据类型
enum :声明枚举类型
typedef:用以给数据类型取别名
const :声明只读变量
unsigned:声明无符号类型变量或函数
signed:声明有符号类型变量或函数
extern:声明变量是在其他文件正声明
register:声明寄存器变量
static :声明静态变量
volatile:说明变量在程序执行中可被隐含地改变
void :声明函数无返回值或无参数,声明无类型指针
if:条件语句 else :条件语句否定分支(与 if 连用)
switch :用于开关语句 case:开关语句分支
for:一种循环语句
do :循环语句的循环体
while :循环语句的循环条件
goto:无条件跳转语句
continue:结束当前循环,开始下一轮循环
break:跳出当前循环
default:开关语句中的“其他”分支
sizeof:计算数据类型长度
return :子程序返回语句(可以带参数,也可不带参数)循环条件
顺序结构,选择结构,循环结构.
函数是程序的模块化单位.(功能化)
C语言允许直接访问物理地址.
C语言比汇编的可移植性好,(C语言也不必考虑寄存器等)
基本数据类型:整型,实型,字符型.
%开头的为格式控制符,与变量对应(用变量的值来替换)(%d 十进制整数的形式);
C语言中无输入输出语句(printf是函数调用).
数据结构+算法=程序
对数据的描述是数据结构
对操作的描述是算法
算法是解决问题的方法和步骤
算法分为:数值运算算法和非数值运算算法(数据库)
Sqrt 开根号
exp 平方
pow().x ()的x方 pow(y,x) y的x方法
+ 加 - 减 * 乘 / 除 % 求余数
< 小于 <= 小于或等于
> 大于 >= 大于或等于
== 等于 != 不等于
&& 且 || 或 ! 非
?: 条件
<math.h>中的函数
abs 绝对值
sqrt 平方根
pow (a,b) 求a的b次方
fabs 原型:在TC中原型是extern float fabs(float x);,在VC6.0中原型是double fabs( double x );。
用法:#include <math.h>
功能:求浮点数x的绝对值
说明:计算|x|, 当x不为负时返回x,否则返回-x
类似函数:abs
在C语言中,%只能用于整数运算的运算符
c的char数据属于基本类型,基本类型其中还包括-整型,实型,枚举类型!
数据在内存中是以二进制形式存放的。数值是以补码表示的。
整型:
一个正数的补码和其原码的形式相同。而负数的补码方式是将其绝对值的二进制形式“按位求反再加1”
实型:
在内存中占4个字节,是按照指数形式存储的,实型数据分为小数部分和指数部分,分别存放!计算机用二进制表示小数部分,用2的幂次来表示指数部分!
字符型:
在内存中字符的存储实际上是把字符相对应的ASCII代码放到存储单元中的。而这些ASCII代码值在计算机中也是以二进制形式存放的。这个与整型的存储很相似。因此这两类之间的转换也比较方便!
C语言中标识符为关键字,用户标识符,预定义标识符,
C语言中用”\”开头的字符序列为转义字符
C语言中没有用二进制表示数值的
\123 (三位八进制显示的) 是通过ASCII对应的字母(S)
\x23 (两位十六进制的) 也是ASCII对应的(#)
%(求余)的操作对象只能为整数
C语言规定两个%才是常用%号(与求余%区分)
C语言运算符的左结合性
右结合性
注意格式
++i(加后取值) i的值和表达式的值是两个概念;但对i来说都是+1;
i++(加前取值)
C=-5;a=6(a++过了)
两个a++ 有二义性(结果是10,7)
此编译器先算的两个a++ 然后5+5=10;
这样就没有二义性了
从右到左运算(编译器原因)(结果7,7,5)
从右到左 x的值是y=6表达式的值
b=7,9 不能看做7,9赋值给b的原因是因为,运算符的值优先级最低
必须括起来才能,这里不识别逗号表达式
结果是8(a=7被重新赋值了)
结果为(7,12)
因为printf是从右到左运算,而逗号是从左到右!(编译器优化不同结果不同)
float应该是输出6位小数
输出结果是:
#在这里充当于0
%X输出为大写,%x则为小写
16位最大的范围是32767,
%e为 0.000e+000的形式
y=%-8d (空格补齐在数值后面,如3*******)但是不包括已有的数.
y=%8d(空格补齐在数值前面,如*******3)
但是 当a=345,%2d 就不起作用了,应为a有3位;
%14.10lf 表示一共有14位(算小数点),小数部分为10位
再输入%d%c%d%c的时候必须注意空格和回车会占用%c字符位!
字符型变量是 单个字符用单引号括起来的或者用’\**’转义字符
C语言逻辑模块和数组:
(表达式的值给左侧变量)
表达式和赋值是两码事
putchar();函数只能输出一个字符,必须要包含一个参数
注意:字符串不行
也是一个字符
注意是地址表列,&
要注意的是%c只能接受一个字符,所以%3c没意义
注意%*是抑制字符,%*3d代表345这3位被屏蔽了!
抑制字符控制格式很严格,不会多出
只能用printf中用来控制小数点
S=√s(s-a)(s-b)(s-c) 其中s=(a+b+c)/2;(悲催的三角形通用面积公式)
Character 字符全名
float和int型之间的运算要注意 截断问题
(5/9)不能加上括号,是因为优先级问题导致5/9先计算出为0,然后0在强制转换成float就没意义了
a>b成立 表达式的值为1,1=b,成立
关系表达式有2个逻辑值,0或者1(非0);
if else 是一套
if(表达式1)
if(表达式2)
else(这样写就错了,else的依赖条件是表达式2,不是表达式1)
if语句后面没{}只绑定一条语句
else会自动绑定上面最近的if所以要注意.
没 ; 号 编译器认为语句没结束,所以报错在下一条;
比如:case Y:
case y: … 2个条件执行一条语句
注意的是 case选择后会自动继续执行case后面的语句
exit()函数可以推出
分离整型数字用的是int截断的原理,所有的个位都是对%10
goto语句明白就行,不建议使用,严重影响结构化设计
while语句没{},也只绑定一条循环语句
外层循环控制行,每行的变化由内层循环改变
一个外层循环 2个并列的内层循环
要学会对i(行)的利用
穷举法(暴力破解法)
循环中,遇到break则循环结束,遇到continue在跳过一次循环,循环继续~
Pow(2.n-1) 2的n-1次幂(math.h)
Getchar() 为回车’\n’结束
字符串””和字符’’完全不同,字符串不向下兼容
2个Scanf()和2个getchar这样使用会造成第二个输入无效(被回车操作符占用了)
这样可以解决连续输入问题,中间的getchar()吃掉了回车,但是这样就只能输入一个字符就要回车才能输入下一次;
这样解决第二个getchar()从头开始获取而不再缓存区里自动顺序排列的问题,但要注意getchar只能获取一个字符而不是字符串
利用getchar()一次只能读一个的特性结合 ++ 运算可以统计输入的字母的个数
必须注意else是和最近的if结合的!
求2个数的最大公约数:
辗转相除法: (前提 m>n,m%n!=0才可以不断细分)
如上图,求126和24的最大公约数,用126除24余6,再用除数24除余数6直到为0,那6就是最大公约数.
水仙花数的两种方法
完数位各个真约数之和等于本身(6=1+2+3);
费波拉契数列:从第三项起每一项等于前两项和的数列(1,1,2,3,5,8,13,21…)
输出结构为***,因为条件表达式里的a被赋值成0,整个表达式的值都为0.
x=2;if和else默认都只能绑定一条语句
a=1,!a的值就是0;
default也要添加break;,不然会继续执行下面的case10;
表达式1 && 表达式2:如果表达式1不成立,系统并不会继续去判断表达式2的值了,直接忽略.
数组:
数组a[10]最大的元素是a[9],不能越界,数组的第一个元素都是0,而且数组都是连续的.
整型变量都可以用scanf输入数据
5个元素
5个元素,其余2个初始化为0了
全局变量处于未初始化变量段,自动初始化为0;
数组不能整体赋值,因为数组名是个地址
-std=c99(启用新协议)
数组只能做加减运算,arr+1(加多少看类型)
*(arr+i) 取地址
a[0]中的[]为取地址含义
每5个元素换一行
冒泡法
假如有5个元素 只要确定了4个排序,那自然地顺序就确定了(10个数就找9个)
i控制着比较的范围,把已经确定的数(大)排除在外;
j控制着具体的元素用来比较,范围受i的限制.
不能对数组整体操作,只能对数组元素操作
数组是一种静态的数据结构,链表是动态的
%p为地址输出(自带0x)
(哈工大35集详细介绍冒泡法)
数组的容错性
一个大括号为1行(分行初始化),同样没初始化的就为0
初始化字符型数组要注意\0,要多设一位用来存放\0;(前提是用字符串的形式存储的,单个字符存储则不用,上图中都是单个字符,不涉及到\0);
字符型数组未完全初始化时,其他全变成\0;
字符串才有\0
%s是字符串
%c是单个字符
用串就不要循环单个输入输出了,当做整体
a就是首地址,代表着地址不用加& 取地址符
字符串输入时要保留\0的位子
%s只会输入到\0前一个的字符,%c则会输出所有字符包括\0
从第二个开始输出(只要是地址就行)
a,a[0],a[0][0]地址都一样,但a[0][0]打印要加&,同时3个代表的意义不一样,a代表整个数组的大地址,a[0]是第一行的首地址,a[0][0]才是精确到具体值.
Getpid进程号(随机种子)
Strlen(字符串);实际长度(不包括\0)
字符串处理函数:
Strcpy(a,b) 从b赋值到a;(务必注意空间大小)
Get()读取字符直到换行符结束
Gets()读取字符串直到换行符结束
break只能跳出一层循环
\b 退格符
模块化编程(设计模式):将大的功能细分化为若干个小的功能模块,然后再将小的功能在细分化为更小的功能模块,直到,每个功能由一个小程序模块完成,最后将所有的程序模块组合起来,达成完成实现所需的功能.
int mian(int argc,char * argv[])
{
函数体…
}
返回值类型 函数名(参数列表…)
函数与普通变量或数组一样,在使用前要先进行定义(实现)或声明(非实现)
参数列表由 参数类型 参数变量名组成
函数返回值时会退化成值,同时栈消失;(函数只能返回一个值)
函数可以嵌套,但不允许嵌套定义;(printf里的为函数调用(非值))
在函数调用时出现的参数是实参;
在函数被调用时,出现在参数列表中的参数叫做形参;
形参是在产生函数调用时,才会分配内存单元;
用户自定义函数要写在main函数上面就不用声明,否则要在main上面必须声明;
函数的递归:
在调用一个函数的过程中,又出现直接或者间接的调用该函数本身,称为函数的递归调用;(递归函数构成了另一种循环的结构)
函数在传递参数时分为值传递和地址传递;
作用域(函数{}或者语句体{}内):从声明开始到return函数返回或结束;
全局变量是从开始到结束;默认是启用局部变量;
使用函数的好处:
1:使程序看起来更简洁,更清晰;
2:可以有效利用代码,减少程序冗余;
函数的调用:
1:可以出现在语句当中,例show();
2:可以出现在表达式当中;max=getmax();
主调函数(例:main)以及函数嵌套关系;
窗体中的按钮为隐藏函数
C语言中有唯一一个返回2个值的函数(父进程和子进程ID)
注意调用函数的实参和定义函数的形参两个概念.
实参 ==> 形参(单项值)
函数调用的几种形式
函数原型的说明和函数的定义是两码事!
输出结果为11,10,9;因为l互相受到影响,所以编译器选着从右到左.(不影响为从左到右)
生命周期的特性就为存储类
我们普通所声明的变量也带有存储类属性,只是缺省了(auto)
静态存储类从分配开始到程序结束才会释放.
动态存储类,调用时产生,调用结束后释放.
Auto是缺省的,局部变量,调用时才分配,调用结束后释放
存放在CPU中,加快读取速度,但是寄存器不能多设,且不能取地址(因为它不在内存中!)(只能用于自动(动态)和形参)
静态局部变量的函数释放后,变量并不消失,它的值具有继承性,下一次的初值就是上一次的结束值.
静态全局变量还自动初始化为0;(static int num)
源文件都可以访问,继承性和初始化为0;(int num)
Int型函数写在main函数后面时不需要声明,默认是缺省的可以不写,但是long长整型必须要写.(有可能windows才是,mac os不是)
递归函数(自己(层层)调用自己)(哈工大46集中间开始)
一层一层返回的,先到里面去.
递归调用时不直接求值,找到最后一层在一层一层返回运算.
递归要先明确最底层的开始条件,再一层一层退回来.(必须要有终止条件)
(比如1+到n,1+2+3+4…(n-2)+(n-1)+n层层返回时正好每个数都相加了!)
system(“clear”);清屏函数
code段 代码段
data段 数据段 常量(初始化的全局变量)
bss段 全局变量(未初始化的全局变量,都是0)
堆 段 molloc 需要手动释放,动态的
栈 段 自动释放的,局部变量 auto
(栈基址 栈顶址(跑马圈地))
二维数组传递到函数的形参会退化成一维数组的指针
指向函数的指针和栈顶址有关
段错误:栈空间不够后,系统引入其他空间,进行非法写入,导致错误.
汉诺塔原理:
1:把A上的n-1个盘借助C先移到B上;
2:把A上剩下的一个移到C上;
3:讲n-1个盘借助B移到C上;
(扩展知识点)
宏展开时就是s(3*4);
宏定义是在编译的时候替换好的,和函数完全不同.
预处理命令(高效率)(条件编译)
首地址就是地址
&地址和*地址中的内容是两码事.
(逻辑地址和实际地址)
指针类型的变量
类型关键字 * 指针变量名
指针就是地址.
指针的指针(地址的地址)
指针都是8字节(64位)
P是存放地址的,对p取地址就可以读取里面的内容.
*和&作用不同;
&是取地址符(对内容) *是取内容符(对地址)
指针只能参与 赋值,算术(加和减)和关系运算 三种运算
指针算术运算是用的变量类型长度
汇编语言都是16进制显示地址
数值运算和地址运算是两码事,普通int型存放地址(数值)也行,但是只是数值运算.
内容可以用 *(指针) 来代替,完成一些强大的,方便的功能.
数组常量指针无法自增自减,因为起始地址是固定的了(上图是错的)(可以用指针来*(p++));a是常量无法变化(a是数值运算),p也没变,变的是引用(地址运算).
P一直没变
P变了(但是指针变量可以使用,也是正确的)
X就不能自增了,x是数组指针,无法更新.
这样才行
原理:要在首地址的基础上运算,首地址本身不能更新.
可以实现strcpy函数;(切记新的字符串要手动添加’\0’)
运算符优先级
(*p)++和*p++ 可能结果一样,但是前者是数值运算,地址不变,后者地址+1,意思完全不同.
指针的关键作用在于数据结构,动态的.
指针和函数之间的关系
Char str[10]
要是存放字符串 有效地就是9个;
要是存放单个字符,可以手动+’\0’(不加也行),就是10个(实际上还是9个);
Ptr指向的是这串字符串的首地址(第一个字符).
作为一个数组,str虽然也是首地址,但是是常量,有限制.
指针只是一个存放地址的变量.数组从创建开始就已经有了地址了,无法改变.
数组的首地址只能引用!!!(固定的值)
Int型存放地址会报error,只能使用指针变量.
指针变量存放函数,数组,变量的地址(类型要匹配)
指针长度都是8字节(64位)
int *p用遮掩法查看类型(非常重要)
*和++ 是右结合性
*p++(是地址运算符,但不推荐这么写)
char *p=”asdf”;
*p=”qwer”;
bus error;因为asdf存在数据段(常量区),只读,修改就段错误.
定义指针变量再引用之前要初始化.(防止野指针)
不能返回已经释放了的函数内部自定义变量的地址.(不安全)
初始化和char *prt=NULL一样,只是给定了一个首地址.
要注意ptr存放的是其他变量的地址内容,但是ptr自己也是有地址的!!(prt和*ptr的区别)
int *p1;
*p1=&a;
会报警告,截取a的地址的一部分,变成野指针.
Int a[10];
a++;
报错,因为a是首地址(跑马圈地),首地址移动的话,数组就找不到了.(a+i)的话就行,变化的是i.
二级指针**p用来指向一维指针.(int * &p)
多维指针除了第一个是数值外其他指针变量存的都是上一级的地址.
Const int b=6;(b是只读)
不能分开写,要一次性定义.
Const 无法修改,但是通过指针可以修改const保护的内容.
Const int*p=&b;
P=&a;
不能修改内容,但可以修改地址.
Int * const p=&b;
可以修改内容,但不能改地址了.
Const int * const p=&b;(const *p和const p)
这样什么都不能改了!!
Const是将变量的权限缩小了(读写变成读了);
I++和i+1的性质完全不同,I变了(自增),i没变(被引用).
用p来接管一个一维数组,a出现的地方p都可以替代,但是p++,a就不行了.
定义一个数组指针,要你过来指向一个二维数组的指针变量.
[]比*优先级要高,*和++优先级一样高.
Int *p[5]和int (*p)[5]不一样.前者先结合数组,成为一维数组指针.后者是每行有5个元素的二维指针.
*(*(p+i)+j) 二维数组引用.
先找到i的行首地址,在用偏移量j寻找.(i行的偏移量要*才能准确定位,不然还是a[0][0]的首地址);二维数组要考虑到a[0][j]的情况,才有的偏移量*,不然无法取出j的值.
指针数组(数组元素是指针变量)
函数指针变量
int (*func)(int a,int b)
指针数组的优势在于只记首地址,不用考虑下标元素个数问题.(‘\0’是结束标记)节省数据空间
括号的优先级别最高的.
函数指针变量优势在于通用性!
函数指针定义的是要在初始化时要匹配参数;
void (*fp[])()={null,add,sub,mul,div,mod};
调用语句:
fp[i]();
i=3的时候就调用mul;(p[i]相当于下标引用)
类型是函数指针.
指针函数
int * func();
返回值是指针.
数组指针和是指针数组等命名,看后两个关键字就是本质.
(*p)指向
函数指针声明;add是函数的地址赋值给funcp,以后可以直接调用funcp.
函数指针数组,每个元素都是函数地址.但是会有段错误产生(输入超出范围),可以添加if条件判断.
NULL是不允许引用的,会段错,但可以赋值.
数组传进来的不需要用*取内容,s[i]就是元素.
形参传回结果.(地址)
数组和指针要注意的是数组的首地址是不会变化的,而指针是游标卡尺一样的在指向.
Ptr的元素是地址,所以需二重指针来存放,要是值的话就只需要一维指针.
系统会自动调用的;
新的数据类型,结构体;
以后直接用student1和2的时候 里面就包含了所有的信息变量,直接使用.
结构体类型.(若干个属性)
Dt就是声明的变量;
结构体可以嵌套.
结构体变量的引用.
结构体的变量是静态的,大小是分配好的.结构体变量大小是所有成员类型大小的和.
结构体指针,用来指向这种结构体.
还是名字是地址.[student[5]是声明的数组]
P++加的是16进制的+22(地址运算),就指向了下一个结构体变量.
所以就可以使用指针来访问了.
若果用指针来访问结构体变量的成员时,就要用->来引用.
结构体指针指向的是结构体变量,再饮用变量的成员.
写入5位,输入5位.注意的是name[10]就是数组,输入的时候不需要加上&符号.
Sizeof是运算符.
这是p=student,是因为student[5]是数组.
结构体指针是根据结构体大小来偏移移动的.
动态数据结构:
之前的空间都是固定的(数组).
数据结构-链表!(思想都一样)
Malloc申请后返回的是空间的首地址.(指针来接收)
Malloc参数有1个.
申请的空间都是空白的要先类型转换(格式化)
(扩展) calloc参数有2个.(不常用)
用链表的好处就是动态的,有对象就分配,数组时提前分配好的静态的.
结构体中的下一个节点指针.而这个指针是指向同一种结构体类型(本身所在的)的,就连上了.(链表的关键)
链表的结束和字符串(‘\0’)一样用NULL表示
链表的建立和遍历输出.(遍历链表还是需要另一个指针来偏移,链表建立后,本身是不动的) 另一个指针指向head,然后p=p->next直到NULL结束.(p,next,head都是相同结构体类型)
结构体也可以写在main函数外面(美观)
哈工大55集详解 动态数据建立和添加.
Exit(0)退出程序
Head节点是标记,不能移动.
最好的程序就是代码不应该重复.
Malloc有可能申请失败,返回NULL;
Pot可以代替struct point了(小名)
Del返回是地址.
Long num是要删除的节点(内容)
共同使用同一块空间,空间以最大的类型为准.
用于多种情况.(不能同时使用,和结构体成员不同)
虽然是共用空间,但是用多少就放多少,内容并不共享.
Typedef 可以简化变量名
初始化要用{};
Free()函数释放空间;(必须的手动)
文本文件和二进制文件
文本文件每行用\n结束标志
文件指针(结构体类型)
用来存放变量的首地址.
FILE *fp(结构体类型的指针)
Fopen(文件名,打开方式);
任何文件都要进行打开和关闭操作.
文件名记得写上路径.
“rb””wb”等…
Fclose(文件指针)
返回非0就是失败的.
Fopen打开成功返回的是地址,返回NULL就是失败了.
按字符写就按字符读.
Fputc(ch,fp);失败就返回EOF(-1);
出错就返回EOF(-1);
Stdout是显示到屏幕上
二进制上用FEOF结束.
按数据块(一组数据)来读写.(结构体)
Fread(buffer,size,count,fp);
从fp读给buffer空间,size是大小,count是个数
Fwrite(buffer,size,count,fp);
写就是从buffer写到fp中.
补码取反加1就是源码
位运算都是补码运算.
相同为0,不同位1.
位运算结果开头为0是正数,是1 就要还原.
右移要注意添加符号位.
位运算要注意的是符号位永远不变.
要注意:;
不用考虑符号位.
为了完成了把b存储起来.
字符指针数组里的每一个元素都是地址.
Delay()时间延迟函数.
用/b可以刷新(类似电子钟)