C语言笔记(1.1版本,目前12000字)----未完待续

x33g5p2x  于2021-11-19 转载在 其他  
字(12.6k)|赞(0)|评价(0)|浏览(312)

前言

0、基础常识

(1)进制

(2)变量与常量

(3)内存

(4)其它零零碎碎的点

(5)运算符

1、关键字

1.switch

2.关键字总览(不需要记,认识即可)

3、函数

4、数组

5、指针

6、结构体

7、数据的存储

8、字符串

9、动态内存管理

10、文件操作

11、程序的编译(预处理)

前言

今天的笔记是1.1版本,想跟大家分享一下我的笔记,很明显,目前并没有完成,想要完整的完成,我还有很长一段路要走,其中也许会有一些值得大家借鉴的地方,具体还是因人而异吧,希望大家能够有所收获,后续我还会继续补充,这次呢,重点内容我将不作标注,因为针对每个人的的个人情况不同,所以重点内容也不尽相同,希望大家能够关注我(虽然写的不太好),里面有的是一些基础,有的不是,有的是一些易错点,有的是一些边角料,更新频率的话最少一周一次,直到彻底补充完整,如果大家觉得对自己有所帮助的话,希望大家点一波关注和小小的赞吧,谢谢大家的支持!

0、基础常识

(1)进制

1.\ddd表示1~3个八进制的数字,注意\071和\71表示的都是八进制的数字。

(2)变量与常量

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.全局变量静态变量在编译期间就创建好了。

7.诸如strlen等的函数名可以作为变量名,但我们并不推荐!

8.define不是关键字,是一个预处理指令,由编译器实现,可以作为变量名,但关键字不可以。

9.变量的访问原则:

(1)不允许在同一个作用域中定义多个相同名称的变量,编译器会报错,显示变量重定义。

(2)允许在不同的作用域中定义多个不同名称的变量。

(3)不同作用域中定义的变量,在访问时采用就近原则。即局部优先原则。

(3)内存

1.int 和 long(int)一般都是八个字节,事实上对于long (int)的字节长度要求是大于或者等于int类型所占的字节数。

2.强制类型转换的格式是(类型) 变量名而不是 类型 (变量名),后者是ui变量的定义,编译器会显示对变量的重定义。

3.任何有值特性的均能用sizeof()求其所占的内存的大小,比如sizeof(10),编译器一般会把整数默认为是int 类型占据4个字节,把小数默认为是double占据8个字节。

(4)其它零零碎碎的点

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.正数的源反补相同,负数的源反补按照上面步骤进行转换!

10.在未声明时不能用其它.c文件里的全局变量,如果要使用,需要用extern(关键字,专门用来声明外部符号的,用法为 extern 类型 变量名,全局变量的作用域是整个工程,生命周期是在程序的运行期间,而static修饰了全局变量后,就使全局变量只能在定义的文件内使用,即使在其它文件中用extern声明后也不能使用)进行声明。

一个全局变量在整个工程的其它文件内部中可以被使用是因为全局变量具有外部链接属性,当一个全局变量被static修饰后,这个变量的外部链接属性就变为了外部链接属性就变成了内部链接属性,使得这个全局变量只能在自己的源文件内使用,其它文件不能使用,给人感觉作用域变小了,生命周期没有变化,存储位置也没有发生变化。

11.内存空间的单位是字节。

12.键盘输入的内容,或者往显示器中打印的内容,全部都是字符,从printf()的返回值即可得出,因为printf的返回值就是输出字符的数目。就像getchar()输入1234,就可以通过printf()进行输出后得到1234,事实上,1234是四个字符。

无论是scanf还是getchar,输入都是以字符形式进行的,不同的是,scanf会进行格式化存储。所以我们把显示器或者键盘叫做字符设备,因为输入的是字符,输出的还是字符。

13.任何C程序,在默认编译好之后,运行时,都会打开三种输入输出流:

stdin:标准输入 FILE* stdin 键盘

stdout:标准输出 FILE* stdout 显示器

stderr:标准错误 FILE*stderr 显示器

14.计算机中的释放空间到底是指的什么?

首先,删除数据并不是把所有的数据清0/1。因为无论是清0还是清1,都是一个写入的过程,如果是清0/1的话,写入和清空应该花费同样多的时间。

计算机中清空数据,只要设置该数据无效即可

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.定义域和生命周期的概念

作用域概念:指的是该变量的可以被正常访问的代码区域(区域性的概念)

生命周期的概念:变量的创建到变量的销毁之间的一个时间段(时间上的概念)

    1. 全局变量,是可以跨文件,被访问的。 2. 全局函数,是可以跨文件,被访问的。

18.源反补的转换方法

原码:直接将二进制按照正负数的形式翻译成二进制就可以。 反码:将原码的符号位不变,其他位依次按位取反就可以得到了。 补码:反码+1就得到补码。

19.在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理; 同 时,加法和减法也可以统一处理(CPU只有加法器)。此外,补码与原码相互转换,其运算过程是相同的,不 需要额外的硬件电路。计算也是用补码进行计算的!

20.printf()和scanf()在底层上也是由getchar和putchar实现的!

getchar函数是逐个字符进行读入,逐个字符进行输出的!如果进行相应的判定,自然也是逐个字符进行判定!下面例子即可证明。

21.在 16 位环境下,short 的长度为 2 个字节,int 也为 2 个字节,long 为 4 个字节。 16 位环境多用于单片机和低级嵌入式系统,在PC和服务器上已经见不到了。 对于 32 位的 Windows、Linux 和 Mac OS,short 的长度为 2 个字节,int 为 4 个字节,long 也为 4 个字节。

无论在32位还是64位的操作系统下。int 始终占据4个字节。

22.C语言语句可分为以下五类:

(1)表达式语句(例如 y=x+3;假设变量y和x均已定义)

(2)函数调用语句(MAX(x,y);假设函数MAX()已经定义)

(3)控制语句(if 、switch等等)

(4)复合语句(把多种语句复合在一起形成的语句)

(5)空语句(分号本身就可以作为一条语句,称为空语句 )

无论上述哪一种语句,都必须以分号结束。

23.有关于getchar和putchar

getchar的作用:

从一个流里读取一个字符,或者从标准输入(键盘)里面获取一个字符。

getchar的返回类型:int(存储文件结束标志-1)

putchar的作用:

写一个字符到流里面或者到标准输出(显示器)中。

putchar的输出类型:字符型

注意:在使用getchar时,如果前面输入了空格到了缓冲区中,不要忘了用getchar吸收缓冲区中的数据!

​​​​​​

ctrl+z就是文件结束标志,使getchar终止

24.C程序地址空间:

程序在编译之前,main()就已经先预定好了分配的空间,即先开辟了空间,在main函数中定义的变量,都是在这段空间中重新申请空间来存放的,即二次开辟。我们把给函数预先分配的这块空间叫做栈帧。

这些函数开辟的那块空间也叫栈帧。在其它局部函数中定义变量也是在相应的栈帧上开辟的。

注意:调用函数时相应的栈帧开辟,调用结束后函数返回,释放栈帧。

#include <stdio.h>
#include <windows.h>
char* show()
{
	char str[] = "hello bit";
	return str;
}
int main()
{
	char* s = show();
	printf("%s\n", s);
	system("pause");
	return 0;
}

释放栈帧之后,里面保存的数据并没有被清除掉,因为计算机意义上的删除只是使数据无效化,无效化指的是这段内存空间是可被覆盖的,换言之,栈帧释放掉后,内存中仍然存储着之前释放栈帧中相应的数据,数据并没有随着栈帧的释放而被清空,在这个函数中show()函数返回的时候,str仍然指向"hello world"这个字符串。

(5)运算符

1.取模运算符%只能用于整数,即两侧只能是整数。

2.运算符优先级

​​

1、关键字

1.switch

(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()之类的输出语句。

(10)用在switch中的关键字有default、case、break,但要记住continue永远都不能用在switch中,因为continue是用来结束本次循环然后进入下一次循环的,换言之,continue只能用在循环语句中。

2.关键字总览(不需要记,认识即可)

关键字 说明

auto 声明自动变量

short 声明短整型变量或函数

int 声明整型变量或函数

long 声明长整型变量或函数

float 声明浮点型变量或函数

double 声明双精度变量或函数

char 声明字符型变量或函数

(上述6个用黄色标注的为C语言内置的数据类型,除此之外,一律不是)

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 声明函数无返回值或无参数,声明无类型指针

(1)void类型

void无法用来定义变量,因为不确定该开辟多少空间。在vs中,sizeof(void)的结果是0.在linux中用sizeof(void)结果是1,void无法用来定义变量的原因除了内存空间大小不确定之外,更重要的原因就是vlid类型本身就被编译器解释为空类型,所以编译器就强制的不允许用其来定义变量。

定义变量的本质:开辟空间 而void作为空类型,理论上是不应该开辟空间的,即使开了空间,也仅仅作为一个占位符看待,所以,既然无法开辟空间,那么也就无法作为正常变量使用,既然无法使用,编译器干脆不让他定义变量。同样的,也无法通过void进行强制类型转换。

void修饰函数返回值:1、占位符,让用户明确不需要返回值 2、告知编译器返回值无法接int

void充当函数的形参列表:告知用户or编译器,该函数不需要传参。

结论:如果一个函数没有参数,就在()内加上void;如果一个函数不需要返回值或者没有返回值,就在返回值类型处加上void。

int test1()
{
    return 1;
}
int test2()
{
    return 2;
}
int main()
{
    test1(1,2,3,4);//编译器在vs上不会报错且不会有警告可以正常运行,在linux中一样可以正常编译。
    test2(1,2,3,4);//编译器在vs上不会报错但会有警告可以正常运行,但是在linux中无法正常编译。
    //上述两种情况对于传参时在栈区中开辟内存都没有任何的影响(在vs中)
}

(2)void指针类型(作用:通常用来设计通用接口)

void*是可以定义变量的,因为指针占据的内存大小是明确的。

void可以被任何类型的指针接收,同时void可以接收任意指针类型(常用),在库函数中,系统的接口设计上 ,尽量设计成通用接口,从而就收各种类型的数据,例如memset()函数。

#include<stdio.h>
int main()
{
    void *p =NULL;
    p++;
    p--;
    //上面这两种写法,编译器均会报错,因为不明确++或者--应该跨越的步长,换言之,如果想进行类似的++或者--操作
    //就需要明确指针指向的变量类型所占据的内存空间的大小,之前已经指出,void在vs中所占据的内存空间大小为0.且
    //无法被用来定义变量,所以自然无法用来进行++--的操作,但是在linux中是可以运行的,因为linux中明确了void
    //类型的大小为1。
}

注意:C语言中无法对void *类型指针变量进行解引用,例如下面这段代码就会报错:

int main()
{
    int a = 10;
    void * p = &a;//因为void类型的指针可以接收任意类型的数据的指针(地址)
    
    *p=20;
    printf("%d",*p);
    //这两段代码都会报错,p是void*类型的,对p解引用,*p就是指向指针所指向的目标或者说类型,对p解引用
    //*p的类型就是void,而编译器无法通过void类型来解析其对应的空间及类型以及里面的数据,所以编译器会
    //报错,在Linux中同样无法正常的运行。因为在vs和Linux中均无法用void来定义变量
    return 0;
}

if 条件语句

else 条件语句否定分支(与 if 连用)

switch 用于开关语句

case 开关语句分支

for 一种循环语句

do 循环语句的循环体

while 循环语句的循环条件

goto 无条件跳转语句

goto语句的用法:

(1)goto 语句可用于跳出深嵌套循环goto语句可以往后跳,也可以往前跳,且一直往前执行

(2)goto只能在函数体内(代码块)跳转,不能跳到函数体外的函数,更不可能跳转到其它的文件内。即goto有局部作用域,需要在同一个栈内。 

(3)goto 语句标号由一个有效地标识符和符号";"组成,其中,标识符的命名规则与变量名称相同,即由字母、数字和下划线组成,且第一个字符必须是字母或下划线。执行goto语句后,程序就会跳转到语句标号处,并执行其后的语句。通常goto语句与if条件语句连用,但是,goto语句在给程序带来灵活性的同时,也会使得使程序结构层次不清,而且不易读,所以要合理运用该语句。

continue 结束当前循环,开始下一轮循环

结束本次循环之后将进入到条件判定,而不是跳入到循环体的开头,例如while循环体中有一个continue,将跳到()中进行下一次的判定;do while语句中如果do后面的{}中有一个continue,将进入到后面的while()语句的()中进行下一次的判断;for(int i =0;i

break无论是用在switch还是循环中,均只能跳出一层switch或者一层循环。 default 开关语句中的“其他”分支 sizeof 计算数据类型长度 return 子程序返回语句(可以带参数,也可不带参数)

#include <stdio.h>
#include <windows.h>
char* show()
{
	char str[] = "hello bit";
	return str;
}
int main()
{
	char* s = show();
	printf("%s\n", s);
	system("pause");
	return 0;
}

2、分支和循环

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()循环使将条件初始化,条件判定,条件更新放在一个()内,语法结构比较紧凑。

6、在循环语句中最好是将循环次数较多的,放在最内层,循环次数最少的,放在最外层。这样可以使代码执行效率更高,就是使编译器跳转循环的次数尽可能地少。计算机在执行程序的时候,计算机内部,CPU中有很多数据使会缓存起来的,如果跨代码跳转过多的话(即外循环多,内循环少的情况下,会频繁的进行循环之间的跳转),就会导致缓存的数据不停的处于缓存和过期两个过程之中,这样会使计算机在存储数据的过程之中花费很多的时间。一般来说程序代码执行越密集,执行效率就越高,即计算机在加载程序的时候,附近的代码也会一块加载,所以内层循环次数高比外层循环次数多执行效率更高。

7.for循环区间最好是左闭右开,一般我们也会这样写,原因就是能很好的看出循环次数,例如for(int i = 0; i < 10;i++)能够很好的看出循环次数为10次,而如果写成for( int i = 0; i

11.for语句的控制表达式不能包含任何浮点类型的对象。浮点数存在精度损失,在判定时可能会出现一些问题。

12.在C语言中,不能采用数学中的表达式作为条件判断式,例如(18=18,得出来的逻辑值再跟60比较大小,与我们的初衷不符。

13、计算机把{}内的语句当作一条语句,所以我们如果if后面想跟多条语句的话,就要用{}括起来形成一个语句块。当然,即使是单条语句,也建议加上。

14.else总是跟最近的未配对的If进行匹配。

15.在写表达式进行判断的时候,特别是==,最好把变量写在右边,常量写在左边。

16.continue 结束当前循环,开始下一轮循环

结束本次循环之后将进入到条件判定,而不是跳入到循环体的开头,例如while循环体中有一个continue,将跳到()中进行下一次的判定;do while语句中如果do后面的{}中有一个continue,将进入到后面的while()语句的()中进行下一次的判断;for(int i =0;i

17.不要在for循环体内修改循环变量,以防程序失去控制。

18.for(;;)将构成死循环,条件判断恒为真,所以程序不会终止,但最好不要这样写!

19.goto语句:

for(...)
    for(...)
   {
        for(...)
       {
            if(disaster)
                goto error;
       }
   }
    …
error:
 if(disaster)
         // 处理错误情况

在跳出多层循环时使用,事实上,,return 0;语句也能退出多层循环,但如果在主函数中直接执行return 语句则会直接结束程序。

3、函数

1.strlen()的返回类型最好是size_t,这是C语言自己设定的,打印的时候最好用%u,即无符号整型。这样写是最标准的!

2.在定义没有返回值的参数时,可以用void进行定义,甚至在函数中你也可以写入return 返回某一个值,此时编译器并不会报错,甚至调用函数也不会报错,但是只要你在主函数中用某一个变量来接收返回值,必定报错。

如果没有写确定的返回某一个值,只在函数中写了一个return,在return 这条语句之前的语句如果有返回值,就返回这个返回值,如果没有,就会返回随机数,这个结论只适用于局部函数的return,不适用于主函数中的return。

3.在函数调用进行值传递的时候,实参传递给形参,形参其实是实参的一份临时拷贝,所以对形参的修改,不会影响实参,如果想影响的话,可以通过址传递的方式。

4.函数的实参,可以是常量、变量、表达式、函数,但前提是这些要有确定的值。

5.c语言中可以不带返回类型,默认类型为int。但是我们最好要写上!对别人来讲,别人可能会认为我们忘了。

4、数组

1.数组在定义的时候可以选择不初始化,或者放0也可以。

2.if后面虽然说跟单挑语句时可以不带大括号,但我们在使用的时候,必须要带大括号,因为在条件判断后只跟一条语句的情况时相当少的。

3、数组的大小必须是常量,const修饰的常变量不行,因为其本质上还是变量。

5、指针

1.指针变量的名字不带*,我们通常所说的某某是一个指针,其实是一个指针本身,比如int p =&a; p是变量名,而不是p。

2.指针的大小跟系统有关,32位的环境下有32根地址线,指针大小为4个字节,64位的环境下有64根地址线,指针大小为8个字节。(X86:编译出来为32位程序,X64编译出来为64位程序)。不是64位系统的情况下指针一定占8个字 节,关键是要按照64位方式编译

    • 操作符也叫解引用操作符,间接访问操作符。

4、指针可以认为是一种数据类型,也可以认为是定义出来的指针变量。

6、结构体

1.定义结构体时,结构体中[]内的数字不能省略,必须明确指定,因为初始化才不用写。

2.给结构体变量赋初值时可以直接这样struct Stu zhangsan = {0};后面可以进行再赋值,不然打印出来的数据都是0。

7、数据的存储

8、字符串

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'为字符串结束标志,也占用一个字节长度,这个结束标志在限定了字符串长度后编译器会自己加上)

3.C语言有字符串。但是C语言没有字符串类型。

9、动态内存管理

10、文件操作

11、程序的编译(预处理)

相关文章