今天呢,想跟大家分享一下我的笔记,很明显,目前并没有完成,想要完整的完成,我还有很长一段路要走,其中也许会有一些值得大家借鉴的地方,具体还是因人而异吧,希望大家能够有所收获,后续我还会继续补充,希望大家能够关注我(虽然写的不太好),里面有的是一些基础,有的不是,有的是一些易错点,有的是一些边角料,更新频率的话最少一周一次,直到彻底补充完整,如果大家觉得对自己有所帮助的话,希望大家点一波关注和小小的赞吧,谢谢大家的支持!
目录
0、基础常识
(1)进制
(2)变量与常量
(3)内存
(4)其它零零碎碎的点
(5)运算符
1、关键字
1.switch
2.关键字总览(不需要记,认识即可)
2、分支和循环
3、函数
4、数组
5、指针
6、结构体
7、数据的存储
8、字符串
9、动态内存管理
10、文件操作
11、程序的编译(预处理)
1.\ddd表示1~3个八进制的数字,注意\071和\71表示的都是八进制的数字。
1.局部变量是指代码块内的变量,全局变量是指代码块外定义的变量。
2.定义并且初始化变量的本质是先根据变量类型所占据的内存空间大小为依据开辟空间,然后把索要存储的数据的二进制补码形式存储在内存中,就像unsigned int 类型的变量也可以存储125一样,换句话说,无论什么类型,都可以互相存储,当然,前提是开辟的内存能够放的下,因为无论存储什么数据,存放的都是二进制补码形式,只有在输出时才会进行不同形式的转换,比如-128的补码在转换为源码形式符号位不变,其它位按位取反,(假设输出类型是unsigned int),然后加1,然后转换为十进制进行输出,而如果输出类型是signed int时,就直接把补码转换为十进制即可,至于一些运算就无关紧要了,因为都是以补码形式进行计算。
3.C语言中的常量分为以下以下几种:字面常量、const 修饰的常变量(注意const修饰的最然不可被改变,但本质上仍为变量,不能在定义数组使放入[]内,因为[]内只能是常量)、#define 定义的标识符常量、枚举常量
枚举常量的定义方式
enum Sex { MALE, FEMALE, SECRET }; //括号中的MALE,FEMALE,SECRET是枚举常量(按照整数打印后数值为0 1 2)
4.当局部变量和全局变量同名的时候,局部变量优先使用,但一般在定义局部变量时不要和全局变量同名。
5.局部变量是指代码块内的变量,全局变量是指代码块外定义的变量。
6.全局变量静态变量在编译期间就创建好了。
1.int 和 long(int)一般都是八个字节,事实上对于long (int)的字节长度要求是大于或者等于int类型所占的字节数。
2.强制类型转换的格式是(类型) 变量名而不是 类型 (变量名),后者是ui变量的定义,编译器会显示对变量的重定义。
3.任何有值特性的均能用sizeof()求其所占的内存的大小,比如sizeof(10),编译器一般会把整数默认为是int 类型占据4个字节,把小数默认为是double占据8个字节。
1.C语言中是没有次方运算的,如果要进行次方运算需要运用pow()函数,^是异或运算符。
scanf格式输入要注意同步,scanf()运用时格式时非常严格的,代码格式和输入格式要严格对照。同时注意一点,如果定义普通变量之后未初始化是无法输出的,但如果在定义之后,用scanf()进行输入操作此时不初始化也没有问题,但我们通常并不建议这样操作,因为在编写程序时,我们赋的初值有时会具有某些含义,所以无论后续是否用scanf进行输入,最好都要初始化。
2.在定义变量时,因为编译器默认为我们输入的整数是int,输入的浮点数为double,所以在定义单精度浮点数时float a = 3.14f
3.浮点数在进行比较的时候,绝对不能用==进行直接比较,浮点数在存储的时候本身会有精度损失,进而导致结果可能会有各种细微的差别。
解决方案:#define EPSILON(精度的意思) 0.00001(自己设定的精度)
if ( (x - y) >-0.00001 && (x-y) < 0.00001) 或者 if(fabs(x-y)
实际上,系统已经给我们定义好了精度,即DBL_EPSILON 用法同上,判断两个数是否相等时是if(fabs(x-y)),如果判断某一个浮点数是否等于0时可用if(fabs(x)前面不能加=,即这个数不等于0)
4.区分0、\0、NULL ‘0按照整数进行输出后的结果是48(0的ascii码值)
事实上,运用printf进行输出时,格式为%d时数据都是一样的,均为0,但它们的类型是不同的。
int a = 0; int *a = NULL(类型为void *); (能够被操作符(+-*/=等)两端连接起来的数据类型必须是一样的,如果不一样会发生报错或者警告)
int *p=NULL;
推荐if(NULL == p),而不推荐if(p==0)和if(p)和if(p==NULL)
5.按%p格式进行打印是用来打印地址的。
6.如何理解强制类型转换?
在将''123456''转换为整型时需要自己写函数或者使用相应的库函数,会改变内存中的数据。(真实的转化,并不等于强制类型转换)
强制类型转换并不改变内存中任一二进制位,只改变了我们如何去解释该二进制数字,没有改变内存中的数据。(强制类型转换的本质)
7.#define _CRT_SECURE_NO_WARNINGS要放在(头)文件的最前面或者采用#pragma warning(disable:4996) (后者可以不放在最前面)
8.使用getchar()函数时要注意输入缓冲区的存在,getchar()先检索输入缓冲区中有没有字符,如果没有,才会把输入的字符加载进去,尤其注意我们通常输入的间隔符可能就会因为我们代码的不严谨而被加载到缓冲区中。
9.define不是关键字,是一个预处理指令,可以作为变量名,但关键字不可以
10.在未声明时不能用其它.c文件里的全局变量,如果要使用,需要用extern(关键字,专门用来声明外部符号的,用法为 extern 类型 变量名,全局变量的作用域是整个工程,生命周期是在程序的运行期间,而static修饰了全局变量后,就使全局变量只能在定义的文件内使用,即使在其它文件中用extern声明后也不能使用)进行声明。
一个全局变量在整个工程的其它文件内部中可以被使用是因为全局变量具有外部链接属性,当一个全局变量被static修饰后,这个变量的外部链接属性就变为了外部链接属性就变成了内部链接属性,使得这个全局变量只能在自己的源文件内使用,其它文件不能使用,给人感觉作用域变小了,生命周期没有变化,存储位置也没有发生变化。
11.内存空间的单位是字节。
12.键盘输入的内容,或者往显示器中打印的内容,全部都是字符,从printf()的返回值即可得出,因为printf的返回值就是输出字符的数目。就像getchar()输入1234,就可以通过printf()进行输出后得到1234,事实上,1234是四个字符。
13.任何C程序,在默认编译好之后,运行时,都会打开三种输入输出流:
stdin:标准输入 FILE* stdin 键盘
stdout:标准输出 FILE* stdout 显示器
stderr:标准错误 FILE*stderr 显示器
14.for(;;)将构成死循环。
15.了解编译和链接。
(1)什么是编译和链接?
编译是为了将函数变量等变成,o二进制的机器码格式,链接是为了将各个独立分开的二进制的函数链接起来形成一个整体的二进制可执行文件。
(2)编译和链接以什么为单位?
编译以文件为单位、链接以工程为单位。
编译器编译时会将所有源文件依次读出来,以每个文件为单位进行编译,因此编译不会考虑其他的文件,显然这样就简化了编译器的设计。
链接的时候实际上是把第一步编译生成的.o文件作为输入,然后将它们链接成一个一个可执行程序,第一步有多少.c文件,编译时就会有多少个.o文件,链接后多个.o文件就会变成一个可执行文件。
(3)三种链接属性:外链接、内链接、无链接
外链接:外链接就是需要的函数与变量可以在外部文件找到,通俗说就是可以被跨文件访问。
内链接:与外链接相反,需要的函数和变量在当前的文件的内部就可以找到,或者说具有内部链接属性的变量只能在文件内部被访问,static修饰全局变量和函数都是内链接的。
无链接:这个符号本身不参与链接,它跟链接没有关系,局部变量(auto、和被static修饰的局部变量)都是无链接的。
(4)函数和全局变量的命名冲突问题
extern修饰的全局函数和全局变量都是外链接的,这些函数和变量在整个程序的所有.c文件中都是可以被访问到的,因此对于外部链接的全局函数和全局变量来说,避免命名冲突是非常重要的,特别是在一个大型的工程项目中,不出现相同的名字是很难做到的。所以在C++中给出了解决方案,就是使用命名空间namespace的方式,通俗点就是给一个变量带上各个级别的前缀,不过C语言中并没有这种方法。但是C语言也有自己的解决方案,就是使用之前的外链接、内链接和无链接这三种属性的方法。
C语言的解决方法是这样的,我们将明显不会再其它C文件中引用的全局变量/函数,使用static修饰使其成为内链接,这样在将来链接时,即使2个.c文件有重名的全局函数/变量,只要其中一个或两个为内链接就不会冲突。当然这种解决方案在一定程度上解决了这个问题,但是并没有从根本上解决问题,因此留下了一些瑕疵,今后我们在用C语言写大型项目时要格外注意命名问题。
(5)运用上面的知识分析运用static修饰全局变量和全局函数
当我们使用static修饰全局变量和全局函数的时候,他们的作用范围就被锁在了本文件内,其它文件在链接时无法使用这些函数和全局变量,这就是由原来的外链接属性变成了内链接属性,同时有限避免了函数和全局变量的命名冲突问题。
16.定义域和生命周期的概念
作用域概念:指的是该变量的可以被正常访问的代码区域(区域性的概念)
生命周期的概念:变量的创建到变量的销毁之间的一个时间段(时间上的概念)
17.全局变量,是可以跨文件被访问的。
全局函数,是可以跨文件被访问的。
18.源反补的转换方法
原码:直接将二进制按照正负数的形式翻译成二进制就可以。 反码:将原码的符号位不变,其他位依次按位取反就可以得到了。 补码:反码+1就得到补码。
19.正数的源反补相同,负数的源反补按照上面步骤进行转换!
20.在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理; 同 时,加法和减法也可以统一处理(CPU只有加法器)。此外,补码与原码相互转换,其运算过程是相同的,不 需要额外的硬件电路。计算也是用补码进行计算的!
取模运算符%只能用于整数,即两侧只能是整数。
(1)switch后面跟整型变量、常量、整型表达式(只能为整型或者字符型)
(2)switch语句中,switch语句本身没有判定和分支功能,case完成的是判定功能,而break完成的则是分支功能。一定不要忘记加上default!必须要带!(代码具有更好的健壮性)。这是作为一个优秀程序员的自我修养!
(3)case后面如果想要定义变量(注意是定义而不是赋值),需要加{},可以在代码块内进行定义,当然,在case语句中变量也只能在代码块内进行定义。在case之后可以执行多条语句,不用{}也可以,但最好加上,或者直接封装为函数。
(4)default可以放在任意位置,可以放在case前,case中间,case最后都没有任何的影响,但习惯上将default放在最后。
(5)case后面不要用return。虽然说编译器不会报错,但我们要搞清楚return 的作用,return的作用是直接退出程序,返回值为0,而break的作用是退出循环或者switch语句,我们要搞清楚这一点,如果你用了return并且成功匹配,那么程序就不会执行switch后面的语句,有兴趣自己试一下。
(6)不要在switch后面的括号内出现bool值。虽然说程序也不会报错,但我们并不推荐这样,因为()里面我们通常得出的是整型数值,bool类型可以正常运行的原因是c99和c90标准下的vs 2019把true默认为是1,把false默认为是0,这些同样是作为一个优秀程序员的自我修养!
(7)case后面要跟真的常量,const修饰的常变量是无法编译通过的。
(8)建议case后面要有好的布局方式,从小到大,以及把最容易匹配到的放在最前面。
(9)switch后面{}内的语句位于case和default外面的无法进行执行,无论是定义变量的语句还是其它如printf()之类的输出语句。
auto 声明自动变量
short 声明短整型变量或函数
int 声明整型变量或函数
long 声明长整型变量或函数
float 声明浮点型变量或函数
double 声明双精度变量或函数
char 声明字符型变量或函数
struct 声明结构体变量或函数
union 声明共用数据类型
enum 声明枚举类型
typedef 用以给数据类型取别名
const 声明只读变量
unsigned 声明无符号类型变量或函数
signed 声明有符号类型变量或函数
extern 声明变量是在其他文件正声明
register 声明寄存器变量
1.什么样的变量,可以采用register呢?
(1)局部的(全局会导致CPU寄存器被长时间占用,影响程序运行效率)
(2)不会被写入的(写入就需要写回内存,后续还要读取检测的话,register的使用将没有意义)
(3)高频被读取的(提高效率)
(4)如果要使用,不要大量使用,因为寄存器数量有限,而且并非每一次声明计算机都将变量存入内存中,程序员做的只是建议
2.register修饰的变量,无法取地址,因为地址是内存上的概念,而寄存器上没有地址的概念
static 声明静态变量
作用:
1、修饰变量
(1)修饰全局变量,该全局变量只能在本文件内被使用。
(2)修饰局部变量,变量的生命周期变成全局周期(和全局变量将一样,但作用域不变)(同时存储的空间发生变化,由原来的栈区到了全局区(静态区))
volatile 说明变量在程序执行中可被隐含地改变
void 声明函数无返回值或无参数,声明无类型指针
if 条件语句
else 条件语句否定分支(与 if 连用)
switch 用于开关语句
case 开关语句分支
for 一种循环语句
do 循环语句的循环体
while 循环语句的循环条件
goto 无条件跳转语句
continue 结束当前循环,开始下一轮循环
break 跳出当前循环
default 开关语句中的“其他”分支
sizeof 计算数据类型长度
return 子程序返回语句(可以带参数,也可不带参数)
1.在使用if条件判断时需要注意一个小小的点,就是很多表达式和函数往往是由自己的返回值的,例如if(a=0)这样写的话,后面的代码块就不会执行,因为()内的返回值为0,所以不会执行。
2.if进行条件判断时,把常量放在左边
3.else匹配总是采取就近原则,与上面最近的一个if进行匹配。(所以带花括号)
if里面得到的最好是一个bool值,不要用赋值表达式之类的。
所有的else if语句最好是由else结束。
4.do while语句建议书写格式像这样:
do {
}while();这个语句的循环变量初始化在do之前!
5.while()执行的顺序和if()执行的顺序是一样的,不同的是执行完之后会返回到while()语句进行重新执行,前面的
定义并初始化是循环田间初始化,()里的内容是循环条件判定,{}里的内容是循环条件更新。for()循环使将条件初始化,条件判定,条件更新放在一个()内。,语法结构比较紧凑。
1.数组在定义的时候可以选择不初始化,或者放0也可以。
2.if后面虽然说跟单挑语句时可以不带大括号,但我们在使用的时候,必须要带大括号,因为在条件判断后只跟一条语句的情况时相当少的。
1.指针变量的名字不带*,我们通常所说的某某是一个指针,其实是一个指针本身,比如int *p =&a; p是变量名,而不是*p。
2.指针的大小跟系统有关,32位的环境下有32根地址线,指针大小为4个字节,64位的环境下有64根地址线,指针大小为8个字节。(X86:编译出来为32位程序,X64编译出来为64位程序)。
3. * 操作符也叫解引用操作符,间接访问操作符。
1.定义结构体时,结构体中[]内的数字不能省略,必须明确指定,因为初始化才不用写。
2.给结构体变量赋初值时可以直接这样struct Stu zhangsan = {0};后面可以进行再赋值,不然打印出来的数据都是0。
1.’\0'是字符串的结束标志,在用sizeof进行计算时也算一个字节,strlen()则不会把它计入(因为strlen()和printf函数在遇到'\0'就会自动停止了。(sizeof计算的的是变量所占内存空间的大小,而strlen()计算的是从变量的起始地址开始,到'\0'结束标志为止的字符的数目)。
2.字符串定义的两种方式:
char arr1[] = "bit";([]内可以加数字限定,但不要忘记'\0'也占用一个字符(’\0'编译器会自己加上) char arr2[4] = {'b', 'i', 't'}; char arr3[] = {'b', 'i', 't', '\0'};(2和3两种定义方式要么在末尾加上’\0'为结束标志,要么加上字符串长度限定符(加限定符不要忘了还有'\0'为字符串结束标志,也占用一个字节长度,这个结束标志在限定了字符串长度后编译器会自己加上)