易忽略的C语言细节(下)
(最终解释权归原作者所有,侵权必究)
57、include时,文件名可以用””或<>括起,二者基本作用基本相同,唯一区别是,用双引号时,系统先在引用被包含文件的源文件所在目录中寻找要包含的文件,若找不到,再按系统指定的标准方式检索其他目录;而用尖括号时,不检查源文件所在目录,直接按系统标准方式检索文件目录。一般来说,包含系统库文件用尖括号,包含用户自编写文件用双引号。
58、条件编译的常用形式
#ifdef标识符 程序段1 #else 程序段2 #endif
如果标识符已被定义过(一般用#define),则对程序段1进行编译,否则对程序2进行编译,
#ifndef标识符 程序段1 #else 程序段2 #endif
如果标识符未被定义过,则对程序段1进行编译,否则对程序段2进行编译。
#if 表达式 程序段1 #else 程序段2 #endif
如果指定的表达式为真(非0),编译程序段1,否则编译程序段2.
条件编译可减少目标程序的长度,提高源程序的通用性。
59、C语言把文件看作是字符(字节)的序列,即由一个一个的字符(字节)数据顺序组成。当从文件中读数据时,可以看作字节从磁盘文件流入内存指定的地址空间;当向文件中写数据时,可以看作将内存中的指定字节流向磁盘文件,因此,通常称C语言中的文件是字节流文件。
60、按数据组织形式,可分为ASCII码文件(文本文件)和二进制文件。ASCII码文件存储的是字符对应的ASCII 码值,二进制文件存储的是数据在内存中存放的字节形式。
ASCII码文件占用字节多,把内存中的数据写入ASCII码文件或者从ASCII码文件读数据存放在内存中,需要转换,存取速度相对较慢。但ASCII码文件是可读文件,用文本编辑器可看到文件的具体内容。
二进制文件占用字节少,把内存中的数据写入二进制文件或者从二进制文件读数据存放在内存中,不需要转换,存取速度相对较快。但二进制文件是不可读文件,不能用文本编辑器查看文件的具体内容。
一般原始数据和最终运行结果用ASCII码文件,以便查看修改;中间的运行结果多用于作为另一程序的输入数据,为了提高存储速度,一般用二进制文件。
61、由于磁盘的读写速度比内存的读写速度慢很多,为了减少等待的时间,在文件的读写过程中,采用内存缓冲区技术。如果内存缓冲区是由用户自己申请的,则称为“非缓冲型文件系统”;如果缓冲区是由系统自动建立的,则称为“缓冲型文件系统”。Visual C++使用的是缓冲型文件系统。
缓冲型文件系统读文件是往内存缓冲区一次读入一批数据,待缓冲区装满后或数据已经读完,再从缓冲区逐个将数据读入内存的数据区。缓冲型文件系统写文件是将内存数据区中的数据一次写入一批数据,待缓冲区装满后或数据已经写完,再将内存缓冲区中的数据一次性写入磁盘。内存缓冲区的大小由系统自动分配,一般为512KB。
62、文件指针是指向与文件参数有关的结构体类型的指针变量,与文件参数有关的结构体类型,在头文件stdio.h中已经定义:
typedefstruct {
short level;//文件状态
unsignedflags; //文件状态的标识
char fd; //文件描述
unsignedchat hold; //缓冲区剩下的字符
short bsize;//缓冲区的大小
unsignedchar *buffer; //文件缓冲区的位置
unsignedchar *curp; //文件当前的读写位置
unsigned istemp;//临时文件标识
short token;//有效性检查
}FILE;
FILE*fp; fp是一个文件指针。
63、文件位置指针
在FILE结构体中,有一个成员变量curp为文件的位置指针。文件一旦打开,文件的位置指针便指向文件的开始处。在读写文件的过程中,文件的位置指针随之顺序后移,总是指在刚刚读过或者刚刚写过的字节的下一个字节处。即对于读,文件位置指针所指的字节是没有读过的;对于写,文件位置指针所指的字节是没有写过的。一个正在被使用的文件,文件指针始终指向该文件,文件位置指针随着读写操作不断改变。
64、文件结束标志
1)ASCII码文件的结束标志用-1表示,因为ASCII码文件中存放的是字符的ASCII码值,ASCII码不可能是-1。C系统在头文件stdio.h中用符号常量EOF代表-1,EOF可用来判断文本文件是否结束。
2)函数feof(fp)用来测试与文件指针fp有联系的文件当前状态是否是文件结束,如果返回值是非0,则表示文件结束。与fp有联系的文件既可以是ASCII码文件又可以使二进制文件。
65、打开文件函数:文件指针=fopen(文件名,文件的使用方式);
文件名必须指出文件所在的盘符、路径、文件名,默认当前盘、当前路径。文件的使用方式为一个字符串:
r(只读):对已经存在的ASCII码文件进行读操作。
w(只写):对ASCII码文件进行写操作。如果该文件不存在,系统自动建立新文件;如果该文件已经存在,其内容被清除。
a(追加):文本文件 rb(只读):二进制文件 wb(只写):二进制文件
ab(追加):二进制文件 r+(读写):文本文件 w+(读写):文本文件
a+(读写):文本文件 rb+(读写):二进制文件 wb+(读写):二进制文件
ab+(读写):二进制文件
注意:文件名字符串中用\\代替\;exit(0)可直接终止程序运行;要检查fopen()是否成功。
66、fclose(fp)关闭文件;
文本文件的读写:
rewind(fp) 把当前文件的位置指针移到文件的开始处;fgetc(fp) 从文件中读出单个字符,若文件结束或读取失败,则返回EOF; fputc(ch,fp) 向文件中写一个字符;fgets(str,n,fp) 从文件中读n-1个字符,存入str;fputs(str,fp)向文件中写一串字符;fscanf(fp,格式字符串,地址表列) 从文件中,按格式读出数据,存放在相应的变量里,返回值时正确读取的数据个数;fprintf(fp,格式字符串,表达式表列) 按格式写入文件。
二进制文件的读写(数据量较大,采用数据块处理方法):
fread(stu,size,n,fp) 从文件中一次性读取n个大小为size个字节的数据块,存放在由stu开始的存储单元中。
fwrite(stu,size,n,fp) 从str开始的存储单元,向与fp有联系的文件中,一次写入n个大小为size个字节的数据块。
fseek(fp,offset,star) 将文件的位置指针定位到文件的任何一个字节处,让读写操作从确定的字节位置开始,offset是位移量,star为起始点。star的值可以用0、1、2或者符号常量SEEK_SET、SEEK_CUR、SEEK_END表示,分别表示从文件的开始点、文件的当前位置和文件的末尾为起点。offset的值是从起始点开始偏移的字节数,如果offset的值为正,向前移;否则向后退。其也适用于二进制文件,多数情况下与fwrite()和fread()配合使用。
67、在最新的C标准中,main函数前的类型为int而不是void,尤其是Linux。
68、C语言适合做什么:Linux、Unix嵌入式,适合做小工具;硬件编程;有高性能要求的应用程序(如NGinX服务器速度可达到Aparch的十倍以上,Aparch是用C++写的)。
69、C语言是随着Unix发展起来的,Mac电脑用的就是Unix内核,Windows就只能用和Unix完全兼容的系统Linux(ubuntu,centos)。
amd率先推出的64位cpu ubuntu版本:desktop,LTS(长时间,新的可迁移)。
同时兼容Linux和Windows,需要划分磁盘,比较麻烦,可以安装虚拟机(Mac:VMware Funsion,Windows:VMware workstation)
70、Linux终端下最好用最常见的编辑器是emacs和vim。ubuntu麒麟版默认就带有vi编辑器,其余需要自己安装。更新命令sudo apt-get update,安装命令sudo apt-get install+软件名,sudo可以加在很多命令前面,表示管理员权限,一般默认为普通用户。检查编译器cc –v,cc编辑器最早是在unix里使用的一种C语言编译器,后来又出现了很多兼容cc的编译器的版本,最著名的就是gcc,完全开源。gcc –v也可检查编译器。clear清屏命令。cd ~进入当前用户的家目录。pwd查看当前位置。ls查看当前位置下包含的文件夹或文件。ls –l显示当前位置下包含的文件夹和文件及其各属性(每行首字母为d则表示其为一个文件夹,若为-则表示其为一个普通文件,r表示可读,w表示可写,x表示可执行,第一组rwx表示文件的创建者权限,第二组表示和该用户位于同一个用户组的用户权限,第三组表示用户组外的用户权限)。touch +文件路径,创建一个字符文件。删除一个文件rm+文件路径,删除一个文件。mkdir+目录路径,创建一个目录。vi+文件路径 用vi编辑器打开一个文件,打开时编辑器为命令模式,无法输入内容,输入i表示在当前光标的前面插入字符,输入a表示在当前光标的后面插入字符,shift+a将当前光标跳到行尾,shift+i将当前光标跳到行首,o表示换行输入,shift+o表示当前行的上面插入一行,x表示删除光标所在字符,dd表示删除光标所在行。按esc可再次返回命令模式,命令模式下按shift+ :可编辑命令,w表示保存,q表示退出,a表示all。cc+文件路径,编译C程序,生成可执行文件后,直接执行即可。
71、linux多文本编辑:命令模式下shift+: 后sp+文件路径,即可打卡第二个文件;ctrl+w+下箭头,会跳转到下面的文件。命令模式下shift+: 后set nu会显示行号。按数字+dd就会剪切从当前光标往下的对应数字行,按p为粘贴。多文件编译:gcc 文件路径 文件路径 –o 输出可执行文件路径。(-c只编译不链接,产生.o文件,不产生执行文件,-o控制输出文件的名称,-E只进行预处理)cp+源文件路径+目的文件路径:拷贝文件;rm+文件名:删除文件
72、可以提前将不会修改的函数,公共框架,公共类先全部提前编译成静态库,打包放在一起,下一次修改业务逻辑,只用重新编译那一部分。
73、make工具可以将大型的开发项目分成若干个模块,可以很清晰很快捷地整理源文件。make内部使用的也是gcc。Makefile文件:#表示注释;编译指令前一定要是Tab键;第一个文件为可执行文件,自顶向下递归。编译时直接输入make即自动根据Makefile文件编译。Makefile编译时对于未修改的文件编译时不重新编译,会节约大量的编译时间。
74、C语言在运行时是可以和操作系统进行交互的(Linux称为Unix like,C语言最早是由Unix作者发明的,所以其与Unix应该是完美兼容的),main函数的完整形式是intmain(int argc,char* argv[]);命令连接符&&:先执行前一条指令,如果成功,再执行第二条指令,判断成功的条件是前一条指令的返回值是0。echo $?:输出上一条指令返回值。因此C程序正常时要return 0,错误时return一个非零值。main函数的两个参数:argc是命令行总的参数的个数,argv[]用来存放每一个字符串参数的指针数组,每一个元素指向一个参数。
75、stdio.h文件包含进来,程序启动之后,会提供一系列指向资源的指针,linux会把所有外设当成文件来处理。stdio.h代入时,会启动三个文件:stdin:标准输入流(默认键盘,通过流的重定向,可设置其他),stdout:标准输出流(默认显示器),stderr:标准错误流。默认情况下,printf(“”)是对fprintf(stdout,””)的封装;scanf(“”, )是对fscanf(stdin,””, )的封装,即从文件读,写入文件。错误流即当程序错误(默认返回至不为0时)起作用。cat:读取一个文件的内容,显示到终端
76、标准输入流是0,标准输出流是1,标准错误流是2,对于输出流>>重定向后是附加在输出流文件之后,不会覆盖原本的文件,需要覆盖,则重定向时使用>。对于输入流重定向<。错误流导出:2〉〉
例:./a.out 1>t.txt 2>f.txt <input.txt
77、管道:|,将前一个命令的输出流作为后一个命令的输入流,将多个两个命令结合使用,自动完成一系列的事情。grep:搜索,ps –e查看进程,ssh通过终端远程连接操作系统。
78、gdb工具:调试工具,能够打断点,进行断点调试,单步调试,查看内存中的变量等等。编译时gcc –g就会编译成可调试文件。注意调试时不能删除源代码,即.c文件,删除了依然不支持显示源代码调试。调试过程:gdb+文件路径àlist或者l:列出当前软件项目的源代码àstart:开始单步调试àp+变量:显示变量的值àn:单步跳到下一条às:进入函数内部àbt:查看函数堆栈àf+函数堆栈中的函数编号:回到对应函数的执行位置àq:退出调试。
79、计算机内存最小的单位是字节,一个字节有8位,计算机用二进制表示数据的原因:电子计算机由电路元件组成,电流状态只有两种状态高电位(1)和低电位(0)。计算机中插两个2G内存条和插一个4G内存条是等价的,计算机把内存看成一个整体来计算内存大小,但内存条不是想插就插多少,比如32位操作系统最大使用4G内存,因为地址总线是32位,也就是寻址空间只能是32位,32位指的是给内存编号只能编到32个二进制位。地址总线可以存在多种状态。操作系统会给内存进行编号,编号就是唯一的内存字节的地址。所有的内存都交给操作系统管理,因为一个计算机中可能同时要运行多个程序,要由程序员来对内存直接进行管理是不太合理的。应用程序是由操作系统来调用的。操作系统除了能给内存做编号以外,还可以给内存做一定的规划。在64位操作系统中,程序员使用的内存只有前面的48位。最高位的12位内存空间是给操作系统内核使用的。用户内存与操作系统内核隔离开的好处:操作系统的内存不会被大量占用,避免机器卡住、卡死、死机等状态,可通过操作系统把应用程序关闭,使得操作系统更安全。
80、计算机内存分布:(高位到低位)系统内核à栈(当前函数,当前函数运行到多少行,函数中的变量及变量的值)à自由可分配内存à堆à数据段(全局变量,常量)à代码段(代码编译之后存在磁盘,运行时编译后的二进制数据加载到内存中)。C语言语法不允许我们直接操作代码段。变量名只是代号,变量的本质就是内存。C语言不可以直接对某一内存地址操作使用。C语言中所有变量都有类型。gcc编译器会对代码进行优化,把同类型的变量放一起声明,不一定按代码顺序。32位操作系统的指针占4个字节,64位占8个字节。
81、代码段和数据段的存储顺序是低位到高位存储,栈则是从高位到低位存储。静态变量也是存放在数据段的,注意区分静态变量和全局变量。
82、当对一个代码段地址取值时,会默认是对函数地址取值,即取函数(代码块)。
83、函数调用,主调函数传给被调函数的有函数的参数和被调函数运行结束返回的地址。递归是由高规模到低规模,递推是由低规模到高规模。