C语言精简笔记.docx_第1页
C语言精简笔记.docx_第2页
C语言精简笔记.docx_第3页
C语言精简笔记.docx_第4页
C语言精简笔记.docx_第5页
已阅读5页,还剩29页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

C语言详细内容观看博客(/mjios/category/459066.html)1、 概述C语言的数据类型有:整型(int)、实型(float)、字符型(char)、数组类型(int )、指针类型(void *)、结构体类型(struct)、共用体类型(union)等C语言允许直接访问物理地址,可以直接对硬件进行操作,能够像汇编语言一样对位(bit)、字节和地址进行操作2、第一个函数分析 #include 是C语言的预处理指令之一,所谓预处理,就是在编译之前做的处理,预处理指令一般以 # 开头 #include 指令后面会跟着一个文件名,预处理器发现 #include 指令后,就会根据文件名去查找文件,并把这个文件的内容包含到当前文件中。被包含文件中的文本将替换源文件中的 #include 指令,就像你把被包含文件中的全部内容拷贝到这个 #include 指令所在的位置一样一个C程序中一定会有一个main函数,也只能有一个main函数。main函数是整个C程序的入口。1.在编译前先执行#include指令,拷贝stdio.h的内容到源程序中2.编译源程序,生成目标文件3.链接C语言函数库,生成可执行文件4.运行可执行文件,在屏幕上输出Hello, World!3、函数C语言的函数可以大概分为3类:1. 主函数,也就是main函数。每个程序中只能有一个、也必须有一个主函数。2. 2.开发人员自定义的函数,可有可无,数目不限3. 3.C语言提供的库函数在标准C语言中,函数的定义顺序是有讲究的,默认情况下,只有后面定义的函数才可以调用前面定义过的函数如果调换下sum函数和main的顺序,在标准的C编译器环境下是不合法的如果想把其他函数的定义写在main函数后面,而且main函数能正常调用这些函数,那就必须在main函数前面作一下函数的声明可以省略参数名称,如在大型的C程序中,为了分模块进行开发,一般会将函数的声明和定义(即实现)分别放在2个文件中,函数声明放在.h头文件中,函数定义放在.c源文件中运行步骤分析:1 在编译之前,预编译器会将sum.h文件中的内容拷贝到main.c中2 接着编译main.c和sum.c两个源文件,生成目标文件main.obj和sum.obj,这2个文件是不能被单独执行的,原因很简单:*sum.obj中不存在main函数,肯定不可以被执行* main.obj中虽然有main函数,但是它在main函数中调用了一个sum函数,而sum函数的定义却存在于sum.obj中,因此main.obj依赖于sum.obj3 把main.obj、sum.obj链接在一起,生成可执行文件4 运行程序在定义函数时,函数名后面的()中定义的变量称为形式参数(形参);在调用函数时传入的值称为实际参数(实参)。4、 printf函数scanf函数 3个%d之间是用空格隔开的,我们在每输入一个整数后必须输入一个分隔符,分隔符可以是空格、tab、回车5、 基本数据类型在Java中,你声明了一个局部变量后,如果没有经过初始化赋值就使用该变量,编译器直接报错在C语言中,你声明看一个局部变量后,没有经过初始化赋值是可以使用的但这是很危险的,不建议这样做,b可能会是任意大整数如果是全局的int类型变量,系统会默认赋值为0char的取值范围是:ASCII码字符 或者 -128127的整数下面的写法都是错误的:有以下4种类型修饰符: short 短型 long 长型 signed 有符号型 unsigned 无符号型红色的代表常用的数据类型6、 基本语句C语言的基本语句跟Java中的差不多,所以,这里只是简单地提一下 循环语句(do while、while、for) 条件语句(if 、if-else、switch) goto语句7、 基本运算1、 算术运算符 + 加法运算符 - 减法运算符,或负值运算符 * 乘法运算符 / 除法运算符 % 模运算符,或称取余运算符,要求%两侧均为整型2、 关系运算符 小于运算符 大于运算符 = 大于等于运算符 = 等于运算符 != 不等于运算符3、 逻辑运算符 & 逻辑与运算符 | 逻辑或运算符 ! 逻辑非运算符4、 赋值运算符1.简单的赋值运算符= :int a = 5;2.复合赋值运算符 += 加赋值运算符。如a += 3+1,等价于 a = a +(3+1) -= 减赋值运算符。如a -= 3+1,等价于 a = a -(3+1) *= 乘赋值运算符。如a *= 3+1,等价于 a = a *(3+1) /= 除赋值运算符。如a /= 3+1,等价于 a = a /(3+1) %= 取余赋值运算符。如a %= 3+1,等价于 a = a %(3+1)5、 自增自减运算符 + 自增运算符。如a+,+a,都等价于a = a+1 - 自减运算符。如a-,-a,都等价于a = a-16、 逗号运算符,逗号表达式 用逗号运算符连接起来的表达式称为逗号表达式,它的一般形式为: 表达式1, 表达式2, , 表达式n 逗号表达式的运算过程是:从左到右的顺序,先计算表达式1,接着计算表达式2,.,最后计算表达式n * 整个逗号表达式的值是最后一个表达式的值+a的结果为3,a *= 2的结果为6,b = a * 5的结果为30。因此,输出结果为:7、条件运算符条件表达式其实就是三目运算符,一般形式为:表达式1 ? 表达式2 : 表达式38、Sizeofsizeof可以用来计算一个变量或者一个常量、一种数据类型所占的内存字节数。sizeof一共有3种形式 sizeof( 变量常量 ) sizeof 变量常量 sizeof( 数据类型 )注意,不可以写成sizeof float;8、 数组一维数组定义的形式为:类型 数组名元素个数 int a5;只能放在数组名的后面,下面的都是错误写法:里面的个数必须是一个固定值,可以是常量(比如6、8)、常量表达式(比如3+4、5*7)。绝对不能使用变量或者变量表达式来表示元素个数,大多数情况下不要省略元素个数(当数组作为函数的形参和数组初始化时除外)下面的都是正确写法:int a5; / 整型常量int bA; / 字符常量,其实就是65int c3*4; / 整型常量表达式下面的都是错误写法:int a; / 没有指定元素个数,错误int i = 9;int ai; / 用变量做元素个数,错误定义数组时,系统将按照数组类型和个数分配一段连续的存储空间来存储数组元素,数组名代表着整个数组的地址,也就是数组的起始地址。如int a3 其实a不算是变量,是个常量,它代表着数组的地址。a = &a0初始化的一般形式是:类型 数组名元素个数 = 元素1, 元素2, .;int a2 = 8, 10;其实相当于:int a2;a0 = 8;a1 = 10;注意的是:C语言中编译器是不会对数组下标越界进行检查的,所以自己访问数组元素时要小心元素值列表可以是数组所有元素的初值,也可以是前面部分元素的初值int a4 = 2, 5;当数组为整型时,初始化未确定初值的元素,默认为0,所以上面的a2、a3都为0当对全部数组元素都赋初值时,可以省略元素个数int a = 2, 5, 7;说明数组a的元素个数是3数组初始化时的赋值方式只能用于数组的定义,定义之后只能一个元素一个元素地赋值下面的写法是错误的:int a3;a3 = 1, 2, 3; / 错误a = 1, 2, 3; / 错误其实为什么是错误的写法呢?我们可以简要分析一下。1 第2行的a3代表着访问数组的第4个元素,首先这里已经是数组下标越界了;就算没有越界,给a3赋值时也应该赋一个int类型的整数,不应该是。2 第3行的a是数组名,代表着数组的地址,它是个常量!给常量赋值,那肯定错了!一维数组的元素作为函数实参,与同类型的简单变量作为实参一样,是单向的值传递,即数组元素的值传给形参,形参的改变不影响实参 如果一维数组的名字作为函数实参,传递的是整个数组,这样形参数组修改时,实参数组也同时被修改了。形参数组的元素个数可以省略。二维数组定义形式:类型 数组名行数列数int a23; / 共2行3列,6个元素a0、a1也是数组,是一维数组,而且a0、a1就是数组名,因此a0、a1就代表着这个一维数组的地址按行进行初始化int a23 = 2, 2, 3, 3, 4, 5 ;按存储顺序进行初始化(先存放第1行,再存放第2行)int a23 = 2, 2, 3, 3, 4, 5;对部分元素进行初始化int a23 = 2, 3, 4 ;int b33 = , , , 2, 1, 2, 3;如果只初始化了部分元素,可以省略行数,但是不可以省略列数int a3 = 1, 2, 3, 4, 5, 6;int a3 = 1, 2, 3, 3, 5, ;int a2 = 1, 2, 3, 4, 5, 6; / 错误写法二维数组会先存放第1行的元素,由于不确定列数,也就是不确定第1行要存放多少个元素,所以这里会产生很多种情况,所以只指定行数是错误的9、 字符串在C语言中,我们可以用字符数组来存储字符串。*字符串可以看做是一个特殊的字符数组,为了跟普通的字符数组区分开来,应该在字符串的尾部添加了一个结束标志0。0是一个ASCII码值为0的字符,是一个空操作符,表示什么也不干。所以采用字符数组存放字符串,赋值时应包含结束标志0。* 字符串mj的存储情况如下(假设用字符数组char a来存储):注意了,尾部有个0,如果没有这个结束标记,说明这个字符数组存储的并不是字符串字符串的初始化:当我们使用类似第3个的初始化方式时,系统会自动在字符串尾部加上一个0结束符字符串的输出我们可以使用stdio.h中两个函数来输出字符串,分别是printf和puts函数%s表示期望输出一个字符串,因此printf函数会从b的首地址开始按顺序输出字符,一直到0字符为止,因为0是字符串的结束标记。同样puts函数会从a的首地址开始输出字符,一直到0字符为止。所以,如果想要创建一个字符串,记得加上结束符0,不然后果很严重,会访问到一些垃圾数据。puts函数输出一个字符串后会自动换行,puts函数一次只能输出一个字符串,printf函数则可以同时输出多个字符串字符串的输入我们可以使用stdio.h中有2个函数可以用来接收用户输入的字符串,分别是scanf和gets gets跟scanf一样,会从a的首地址开始存放用户输入的字符,存放完毕后,系统会自动在尾部加上一个结束标记0。* gets一次只能读取一个字符串,scanf则可以同时读取多个字符串* gets可以读入包含空格、tab的字符串,直到遇到回车为止;scanf不能用来读取空格、tab字符串数组:10、 字符与字符串常用处理函数字符处理函数字符输出putchar():putchar(65); / Aputchar(A); / Aint a = 65;putchar(a); / A上面的3种用法,输出的都是大写字母A。* putchar一次只能输出一个字符,而printf可以同时输出多个字符printf(%c %c %c, A, B, a);字符输入getchar():char c;c = getchar();getchar会将用户输入的字符赋值给变量c。* getchar函数可以读入空格、TAB,直到遇到回车为止。scanf则不能读入空格和TAB。* getchar一次只能读入一个字符。scanf则可以同时接收多个字符。* getchar还能读入回车换行符,这时候你要敲2次回车键。第1次敲的回车换行符被getchar读入,第2次敲的回车键代表输入结束。字符串处理函数:1.strlen函数strlen函数会从s2的首地址开始计算字符串的字符个数,直到遇到空字符0为止,不包括02.strcpy函数strcpy函数会将右边的lmj字符串拷贝到字符数组s中。从s的首地址开始,逐个字符拷贝,直到拷贝到0为止。当然,在s的尾部肯定会保留一个0。* 假设右边的字符串中有好几个0,strcpy函数只会拷贝第1个0之前的内容,后面的内容不拷贝最后字符串s中的内容为:mj3.strcat函数char s130 = LOVE;strcat(s1, OC);strcat函数会将右边的OC字符串拼接到s1的尾部,最后s1的内容就变成了LOVEOCstrcat函数会从s1的第1个0字符开始连接字符串,s1的第1个0字符会被右边的字符串覆盖,连接完毕后在s1的尾部保留一个0* 注意下面的情况char s130 = L, m, j, 0, L, o, v, e, 0;strcat(s1, OC);printf(%s, s1);第1行初始化的s1有2个0,经过第2行的strcat函数后,输出结果:4.strcmp函数* 这个函数可以用来比较2个字符串的大小* 调用形式为:strcmp(字符串1, 字符串2)* 两个字符串从左至右逐个字符比较(按照字符的ASCII码值的大小),直到字符不相同或者遇见0为止。如果全部字符都相同,则返回值为0。如果不相同,则返回两个字符串中第一个不相同的字符ASCII码值的差。即字符串1大于字符串2时函数返回值为正,否则为负。char s1 = abc;char s2 = abc;char s3 = aBc;char s4 = ccb;printf(%d, %d, %d, strcmp(s1, s2), strcmp(s1, s3), strcmp(s1, s4);输出结果: s1和s2相同,所以返回0 s1和s3是第2个字符不相同,b的ASCII码值是98,B的ASCII码值是66,b - B = 32,所以返回32 s1和s4是第1个字符就不相同,a的ASCII码值是97,c的ASCII码值是99,a - c = -2,所以返回-211、 指针通过变量名来直接引用变量,然后进行赋值:char a;a = 10;通过变量名引用变量,由系统自动完成变量名和其存储地址之间的转换,称为变量的直接引用方式直接引用是直接通过变量名来读写变量C语言中还有一种间接引用的方式(以变量a为例):首先将变量a的地址存放在另一个变量中,比如存放在变量b中,然后通过变量b来间接引用变量a,间接读写变量a的值。这就是间接引用。如果程序通过间接引用的方式来修改a的值,可以这样做:先根据 变量名b 获取 变量b 的地址ffc2,取出变量b中存储的内容ffc1,也就是变量a的地址,再根据变量a的地址ffc1找到a的存储空间,然后修改里面的数据。总结一句:用来存放变量地址的变量,就称为指针变量。在上面的情况下,变量b就是个指针变量,我们可以说指针变量b指向变量a。指针的定义 一般形式:类名标识符*指针变量名;int *p;float *q;指针的初始化 1. 先定义后初始化 2. 定义时同时初始化指针变量是用来存放变量地址的,不要给它随意赋值一个常数。下面的写法是错误的3. 指针运算符 *1. 给指针指向的变量赋值2. 取出指针所指向变量的值4. 指针用途写一个函数swap,接收2个整型参数,功能是互换两个实参的值。默认情况下,一个函数只能有一个返回值,有了指针,我们可以实现函数有多返回值每个指针变量所占用的内存空间是一样的,而且存储的都是地址,为何指针变量还要分类型?因为当利用*p获取变量c的值时,由于指针p会根据变量c的类型来取多少字节的数据。5. 指针与数组p是指针,a是一个数组1 如果p指向了一个数组元素,则p+1表示指向数组该元素的下一个元素。比如,假设p = &a0,则p+1表示a1的地址2 对于不同类型的数组元素,p值的改变是不同的。如果数组元素为int类型,p+1代表着p的值加上2(16位编译器环境下)3 如果p的初值是&a0,那么 p+i和a+i都可以表示元素ai的地址,它们都指向数组的第i个元素。a代表数组首地址,a+i也是地址,它的计算方法与p+i相同 *(p+i)和*(a+i)都表示数组元素ai 虽然p+i和a+i都指向数组的第i个元素,但二者使用时还是有区别的。因为作为指针变量的p可以改变自身值,如p+,使p的值自增。而数组名a是一个代表数组首地址的常量,它的值是不能改变的,即a+是不合法的,数组名a是个常量!不能进行赋值运算!4 引用一个数组元素可以有两种方法: 下标法: 如ai 指针法: 如*(p+i) 或 *(a+i)6. 指针、数组和函数 函数的实参和形参都可以分别使用数组或指针。这样就有4种情况:也就是说,如果一个函数的形参类型是一个数组,调用函数时,你可以传入数组名或者指针变量;如果一个函数的形参类型是一个指针变量,调用函数时,你可以传入数组名或者指针变量。7. 指针与字符串用指针遍历字符串的所有字符字符串处理函数的参数都是指向字符变量的指针类型,因此可以传入指针变量或者数组名。我们也可以直接用指针指向一个字符串,省略定义字符数组这个步骤 但是要注意: chara =lmj;定义的是一个字符串变量! char*p2 =lmj;定义的是一个字符串常量!下面的做法也是错误的:因为s指向的一个常量字符串,不允许修改它内部的字符字符数组也是不允许这样做,下面的做法是错误的:因为s是数组第一个元素的地址,但是可以*s = L;修改字符串变量内部字符8.函数指针函数作为一段程序,在内存中也要占据部分存储空间,它也有一个起始地址,即函数的入口地址。其中,函数名就代表着函数的地址。指向函数的指针的定义的一般形式:函数的返回值类型 (*指针变量名)(形式参数1, 形式参数2, .);注意:形式参数的变量名可以省略,甚至整个形式参数列表都可以省略返回指针的函数的定义char*upper(char*str) 和 指向函数的指针的定义int(*p)(inta,intb)非常相似,使用时特别注意区分指向函数的指针变量主要有两个用途: 调用函数 将函数作为参数在函数间传递。如果以后想再增加一种乘法运算,非常简单,根本不用修改counting函数的代码,只需要再增加一个乘法运算的函数.12、 预处理指令1.C语言在对源程序进行编译之前,会先对一些特殊的预处理指令作解释(比如之前使用的#include文件包含指令),产生一个新的源程序(这个过程称为编译预处理),之后再进行通常的编译2.为了区分预处理指令和一般的C语句,所有预处理指令都以符号#开头,并且结尾不用分号3.预处理指令可以出现在程序的任何位置,它的作用范围是从它出现的位置到文件尾。习惯上我们尽可能将预处理指令写在源程序开头,这种情况下,它的作用范围就是整个源程序文件4.C语言提供的预处理指令主要有:宏定义、条件编译、文件包含宏定义可以分为2种:不带参数的宏定义 和 带参数的宏定义。不带参数的宏定义:#define宏名字符串 比如#define ABC 10它的作用是在编译预处理时,将源程序中所有宏名替换成右边的字符串,常用来定义常量。1 宏名一般用大写字母,以便与变量名区别开来,但用小写也没有语法错误2 对程序中用双引号扩起来的字符串内的字符,不进行宏的替换操作。3 在编译预处理用字符串替换宏名时,不作语法检查,只是简单的字符串替换。只有在编译的时候才对已经展开宏名的源程序进行语法检查4 宏名的有效范围是从定义位置到文件结束。如果需要终止宏定义的作用域,可用#undef命令 PI这个宏在第1行到第8行之间是有效的,第8行后就无效了5 定义一个宏时可以引用已经定义的宏名带参数的宏定义#define宏名(参数列表) 字符串在编译预处理时,将源程序中所有宏名替换成字符串,并且将 字符串中的参数 用 宏名右边参数列表 中的参数替换输出结果为:注意:1 宏名和参数列表之间不能有空格,否则空格后面的所有字符串都作为替换的字符串如2带参数的宏在展开时,只作简单的字符和参数的替换,不进行任何计算操作。所以在定义宏时,一般用一个小括号括住字符串的参数。将被替换成intb = 2*3+4;,输出结果:应该将被替换成intb = 2*(3+4);,输出结果:3 计算结果最好也用括号括起来所以最好是#define Pow(a) ( (a) * (a) )从整个使用过程可以发现,带参数的宏定义,在源程序中出现的形式与函数很像。但是两者是有本质区别的:1宏定义不涉及存储空间的分配、参数类型匹配、参数传递、返回值问题2函数调用在程序运行时执行,而宏替换只在编译预处理阶段进行。所以带参数的宏比函数具有更高的执行效率条件编译:在很多情况下,我们希望程序的其中一部分代码只有在满足一定条件时才进行编译,否则不参与编译(只有参与编译的代码最终才能被执行),这就是条件编译。#if和#elif后面的条件一般是判断宏定义而不是判断变量,因为条件编译是在编译之前做的判断,宏定义也是编译之前定义的,而变量是在运行时才产生的、才有使用的意义#if和#elif后面的条件不仅仅可以用来判断宏的值,还可以判断是否定义过某个宏。比如: #ifdef的使用和#ifdefined()的用法基本一致#ifndef又和#if!defined()的用法基本一致文件包含:#include 直接到C语言库函数头文件所在的目录中寻找文件#include文件名 系统会先在源程序当前目录下寻找,若找不到,再到操作系统的path路径中查找,最后才到C语言库函数头文件所在目录中查找注意:1.#include指令允许嵌套包含,比如a.h包含b.h,b.h包含c.h,但是不允许递归包含,比如 a.h 包含 b.h,b.h 包含 a.h。2.使用#include指令可能导致多次包含同一个头文件,降低编译效率为了解决这种重复包含同一个头文件的问题,一般我们会这样写头文件内容:13、 变量类型不同类型的变量有不同的作用域、不同的存储类型、不同的生命周期,C语言也提供了一些关键字来设置变量的属性(比如设置存储类型、生命周期)。C语言根据变量作用域的不同,将变量分为局部变量和全局变量。1.局部变量1 定义:在函数内部定义的变量,称为局部变量。形式参数也属于局部变量。2 作用域:局部变量只在定义它的函数内部有效,只有在定义它的函数内部使用,其它函数不能使用它。2.全局变量1 定义:在所有函数外部定义的变量,称为全局变量。2 作用域:全局变量的作用范围是从定义变量的位置开始到源程序结束,即全局变量可以被在其定义位置之后的其它函数所共享。* 变量的存储类型就是指变量存储在什么地方。有3个地方可以用于存储变量:运行时堆栈、普通内存、硬件寄存器。变量的存储类型决定了变量何时创建、何时销毁以及它的值能保持多久,也就是决定了变量的生命周期。C语言根据变量的存储类型的不同,可以把变量分为:自动变量、静态变量、寄存器变量。1.自动变量1定义:自动变量是存储在堆栈中的。2 哪些是自动变量:被关键字auto修饰的局部变量都是自动变量,但是极少使用这个关键字,基本上是废的,不用的省略的,因为所有的局部变量在默认情况下都是自动变量。3 生命周期:在程序执行到声明自动变量的代码块(函数)时,自动变量才被创建;当自动变量所在的代码块(函数)执行完毕后,这些自动变量就会自行销毁。如果一个函数被重复调用,这些自动变量每次都会重新创建。变量a、b,变量c、变量d都是自动变量。2.静态变量1 定义:静态变量是存储在静态内存中的,也就是不属于堆栈。2 哪些是静态变量: 所有的全局变量都是静态变量 被关键字static修饰的局部变量也是静态变量3 生命周期:静态变量在程序运行之前创建,在程序的整个运行期间始终存在,直到程序结束。变量a、变量b都是静态变量变量b是静态变量,所以它只会被创建一次,而且生命周期会延续到程序结束。因为它只会创建一次,所以第6行代码只会执行一次,下次再调用test函数时,变量b的值不会被重新初始化为0。注意:虽然第6行的变量b是静态变量,但是只改变了它的存储类型(即生命周期),并没有改变它的作用域,变量b还是只能在test函数内部使用。3.寄存器变量1 定义:存储在硬件寄存器中的变量,称为寄存器变量。寄存器变量比存储在内存中的变量访问效率更高(默认情况下,自动变量和静态变量都是放在内存中的)2 哪些变量是寄存器变量: 被关键字register修饰的自动变量都是寄存器变量 只有自动变量才可以是寄存器变量,全局变量和静态局部变量不行 寄存器变量只限于int、char和指针类型变量使用3 生命周期:因为寄存器变量本身就是自动变量,所以函数中的寄存器变量在调用该函数时占用寄存器中存放的值,当函数结束时释放寄存器,变量消失。4 使用注意: 由于计算机中寄存器数目有限,不能使用太多的寄存器变量。如果寄存器使用饱和时,程序将寄存器变量自动转换为自动变量处理 为了提高运算速度,一般会将一些频繁使用的自动变量定义为寄存器变量,这样程序尽可能地为它分配寄存器存放,而不用内存变量a是个寄存器变量。14、 static和extern关键字对函数作用static和extern不仅可以用在变量上,还可以用在函数上 外部函数:如果在当前文件中定义的函数允许其他文件访问、调用,就称为外部函数。C语言规定,不允许有同名的外部函数。 内部函数:如果在当前文件中定义的函数不允许其他文件访问、调用,只能在内部使用,就称为内部函数。C语言规定不同的源文件可以有同名的内部函数,并且互不干扰。外部函数extern接下来就演示在一个源文件中调用另外一个源文件定义的函数,比如在main.c中调用one.c中定义的one函数。这个extern跟auto关键字一样废,完全可以省略,因为默认情况下,所有的函数就是外部函数extern关键字对函数的作用:用来定义和声明一个外部函数。其实extern又跟auto一样废,完全可以省略为了模块化地开发,在正规的项目里面,我们会把one函数的声明写到另一个头文件中,当然,这个头文件的命名最好有意义、规范一点,比如叫one.h。以后,谁想调用这个one函数,包含one.h这个头文件就行了。于是最后的代码结构是这样的:内部函数static有时候,我们可能想定义一个内部函数,也就是不想让其他文件访问本文件中定义的函数。这个非常简单,你只需要在定义函数的时候加个static关键字即可。发现程序运行不起来了,在链接的时候就报错了。报错的原因很简单:我们在main.c中调用了one.c中定义的one函数,但是现在one.c的one函数是个内部函数,不允许其他文件访问第1个红框中的Undefined symbols.意思是one这个标识符没有被定义,也就是找不到one;第2个红框的linker表明是链接器报错了。但这个程序是可以编译成功的,因为我们在main函数前面声明了one函数(函数的声明和定义是两码事)所谓编译,就是单独检查每个源文件的语法是否合理,并不会检查每个源文件之间的关联关系,一个源文件编译成功就生成一个目标文件。所谓链接,就是检查目标文件的关联关系,将相关联的目标文件组合在一起,生成可执行文件。static、extern与函数的总结1.static在定义函数时,在函数的最左边加上static可以把该函数声明为内部函数(又叫静态函数),这样该函数就只能在其定义所在的文件中使用。如果在不同的文件中有同名的内部函数,则互不干扰。static也可以用来声明一个内部函数2.extern* 在定义函数时,如果在函数的最左边加上关键字extern,则表示此函数是外部函数,可供其他文件调用。C语言规定,如果在定义函数时省略extern,则隐含为外部函数。* 在一个文件中要调用其他文件中的外部函数,则需要在当前文件中用extern声明该外部函数,然后就可以使用,这里的extern也可以省略。15、 static和extern关键字对变量作用static用来定义一个内部函数,不允许其他文件访问;extern用来定义和声明一个外部函数,允许其他文件访问。static和extern对变量也有作用,不过跟函数有点差别。1.多个源文件共享的全局变量 extern:在C语言中,全局变量定义的位置是有限制的默认情况下,一个函数不可以访问在它后面定义的全局变量在第4行定义的main函数中尝试访问第9行定义的变量a,编译器直接报错了。解决这个错误的话,有2种办法:第1种办法:将变量a定义在main函数的前面这样做编译器就不会找你麻烦了。第2种办法:在main函数前面对变量a进行提前声明也就是让main函数知道变量a的存在就行了,至于变量a定义在哪个位置,main函数不用管。* 完整的变量声明需要用extern关键字第3行是对变量a进行声明,第10行是定义变量a,再次强调,声明和定义是两码事。注意:你不能省略第10行的定义,只留下第3行的声明,因为extern是用来声明一个已经定义过的变量。*其实,你也可以直接在main函数前面再定义一次a看到这一幕,你可能很惊讶,但编译器是不会报错的。在这种情况下,第3行和第10行的变量a代表着同一个变量。以此类推,如果我们写了无数遍全局变量int a;,它们代表的都是同一个变量。第2、第10行代表着同一个全局变量,而第5、第6行则是一个局部变量,跟外面的那个全局变量没有半毛钱的关系你在一个源文件中无论写多少遍全局变量int a;,它们代表的都是同一个变量。还有一个事实,假如在另一个源文件中也有全局变量int a;,那么这两个源文件的所有全局变量int a;都代表着同一个变量。extern是用来声明一个已经定义过的变量和或者是:和上面的两种情况下,test.c和main.c中使用的全局变量a都还是代表着同一个变量注意了,不可以两个文件的所有全部变量a都用extern,下面的做法是错误的:和因为extern是用来声明一个已经定义过的变量,这两个文件都是在声明变量,没有人定义变量,在链接的时候肯定报错:大致错误意思是:标示符a未定义2.单个源文件私有的全局变量 static:输出结果:当static用来修饰全局变量时变为该源文件的私有全局变量当static用来修饰局部变量时变为静态变量3.static和extern的总结1.extern可以用来声明一个全局变量,但是不能用来定义变量2.默认情况下,一个全局变量是可以供多个源文件共享的,也就说,多个源文件中同名的全局变量都代表着同一个变量3.如果在定义全局变量的时候加上static关键字,此时static的作用在于限制该全局变量的作用域,只能在定义该全局变量的文件中才能使用,跟其他源文件中的同名变量互不干扰16、 结构体struct是关键字,是结构体类型的标志。结构体的定义:结构体变量的定义:1. 先定义结构体类型,再定义变量2. 定义结构体类型的同时定义变量3.直接定义结构体类型变量,省略类型名结构体的注意点:1.不允许对结构体本身递归定义如下做法是错误的2.结构体内可以包含别的结构体3.定义结构体类型,只是说明了该类型的组成情况,并没有给它分配存储空间,就像系统不为int类型本身分配空间一样。只有当定义属于结构体类型的变量时,系统才会分配存储空间给该变量4.结构体变量占用的内存空间是其成员所占内存之和,而且各成员在内存中按定义的顺序依次排列结构体的初始化将各成员的初值,按顺序地放在一对大括号中,并用逗号分隔,一一对应赋值。只能在定义变量的同时进行初始化赋值,初始化赋值和变量的定义不能分开,下面的做法是错误的:结构体的使用一般对结

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论