查缺补漏
C语言的祖先是ALGOL60,是1960年由国际计算机委员会设计的一种面向过程的结构化程序设计语言,用它编写的程序具有可读性和可移植性好的特点。但是,它不能直接对硬件进行操作,不宜用来编写系统程序。系统程序用汇编编写,是面向机器的,所以可移植性与可读性比较差。
1983年,美国国家标准局ANSI制定了C语言标准,称为ANSI C或现代C。后来,ANSI C标准被ISO(国际标准化组织)采纳成为ISO9899,简称C89,其第一个版本文件在1990年出版。
到了20世纪90年代,C++标准在不断更新,C语言标准又得到改进,就是ISO/IEC9899:1999(1999年出版),俗称C99,该标准于2000年3月被ANSI采用。
但是各个公司对C99的支持不同,GCC和其他一些商业编译器支持C99的大部分特性,微软和Borland不感兴趣,如vc++6.0不完全支持C99.
C语言是面向过程的语言,按照算法的实现过程逐条语句编写,通知计算机一步一步怎么做。面向对象程序设计(OOP)方向:C++语言,也叫“带类的C”。
由于设计Internet上的Web浏览器的需要,1994年出现了Java语言,不仅支持OOP,而且具有软硬件平台无关性的特点,适合进行网络开发。Java脱胎于C++,被称为C++的衍生语言。2000年,微软公司推出Microsoft Visual Studio.Net,是一个具有公共语言子集的开发平台,实现了多种语言及其类库的无缝集成,使应用程序开发更容易、更简单。C#是专为这一平台推出的全新语言,它也派生于C和C++,并具有语言简洁、面向对象、与Web紧密结合、卓越的安全性能、灵活性和兼容性俱佳等特点,称为NET平台一流的网络编程工具。
格式输出函数:
printf("格式控制字符串",输出项目清单);
%d用来输出十进制整数;
%f用来输出实数;以小数形式输出单、双精度实数;
x,X以十六进制形式输出无符号整数,不输出前缀0x;
u以十进制形式输出无符号整数;
e,E以指数形式输出单、双精度实数;
g,G以%f或%e中较短的输出宽度输出单、双精度实数;
c输出单个字符;
s输出字符串。
输出项目清单中包含0个或多个输出项,可以是常数、变量或表达式,当有多个输出项时,相互之间用逗号隔开。
注意“格式控制符必须与对应输出项的数据类型严格一致。
格式输入函数:
scanf(”格式控制字符串“,输入项目清单);
输入项目清单中至少包含一个输入项,且必须是变量的地址(表示形式是在变量名前加&),当有多个输入项时,相互之间用逗号隔开。
printf()与scanf()是C编译系统提供的库函数,不是C语言的组成部分,而是由C编译系统提供的一些非常有用的功能函数,例如各种输入输出函数、数学函数、字符串处理函数等,可供用户在自己的程序中直接调用。stdio.h
long long int 64位长度,其取值范围是-2^63-1~2^63-1;
unsigned long long int取值范围是0~2^64-1.
unsigned char 1字节 0~255
signed char -128~127
short [int] 2字节 -32768~32767
unsigned short [int] 0~65535
[signed] long [int] 4字节 -2147483648~2147483647
unsigned long [int] 4字节 0~4294967295
float 4字节
double 8字节
标识符:
C语言规定,标识符只能由字母、数字和下划线三种字符组成,且第一个字符必须是字母或下划线。建议变量名的长度最好不要超过8个字符。
符号常量:
在C语言中,可以用一个标识符来表示一个常量,称之为符号常量。
符号常量在使用前必须先定义,有以下两种定义形式:
1、#define 标识符 字符串
#define也是一条预处理命令(预处理命令都以#开头),称为宏定义命令,其功能是把该标志符定义为其后的字符串。
2、const 类型 标识符 = 常数
标识符是只读常量,并且其值在程序运行的过程中不可以改变,习惯上符号常量的标识符用大写字母,变量标志符用小写字母,以示区别。
符号常量与变量不同,值在其作用域内不能改变,也不能再被赋值。使用符号常量的好处是含义清楚,能做到”一改全改“。
八进制整常数必须以0开头,基数取值是0~7.
0x或0X是十六进制前缀,基数取值0~9,A~F或a~f。
1表达式计算中的自动类型转换
1)若参与运算的量的类型不同,则先转换成统一类型,再进行计算;
2)转换按数据长度增加的方向进行,以保证精度不降低,如Int和long型运算时,先把int转换成long型后再进行运算;
3)所有浮点运算都是以双精度进行的,即使只含有float单精度度量运算的表达式,也要先转换成double型,再做运算;
4)char型和short型参与计算时,必须先转换成Int型。
在赋值运算中,等号两变量数据类型不同时,等号右边的类型将转换为左边类型。如果右边数据类型比左边长时,将丢失一部分数据,会降低精度,丢失的部分按四舍五入向前舍入。
2强制类型转换
(类型说明符) (表达式)
必须都加括号,单个变量可以不加。
无论是强制转换还是自动转换,都只是为了本次计算的需要而对变量的数据长度进行临时的转换,不改变数据说明时对该变量定义的类型。
sizeof运算符来测试不同数据类型的长度(字节数)。
%5.4lf 小数形式:指输出宽度为5,精度为4,若实际长度超过5,则按照实际位数输出,小数位数超过4位部分被截去。
VC++6.0的printf函数输出列表中求值顺序是从右向左,再依次输出。
scanf函数格式串如%d%d%d,输入时要用一个以上的空格或者ENTER作为每两个输入数之间的间隔。
可以指定数据宽度,但没有精度控制。
在输入字符数据时,如果格式控制串中没有非格式字符,则认为所有的输入字符均为有效字符,%c%c%c不可以输入空格,不然会把空格当成有效字符。
而%c %c %c 由于中间有空格,输入时必须输入空格。
switch花括号中表达式的值可以是整型或者字符型,如果是实型数据,系统会自动将其转换成整型或者字符型,同一个switch语句中,每个case后面的常量或者常量表达式的值必须互相不同,不然会出现二义性。
顺序、选择和循环三种程序控制结构,是结构化程序设计思想的精髓。利用这三种程序控制结构能够较为清晰的描述一些常用的算法。
一维数组的存储结构与其逻辑结构相同,都是顺序排列的;二维数组采用”按行存放“的方式,即先存放第0行的各元素,再存放第1行的各个元素,。。。。三维数组也采用”按行存放“的方式,先存放第1页二维数组,再存放第2页二维数组。。。。。
对任何多维数组而言,”按行存放“指的是:第一维下标的变化速率最慢,第二维下标的变化速率快于第一维,最后一维下标的变化速率最快。
对于数组的下标,编译系统不检查下标是否越界,因此使用了越界的下标后,可能破坏其他数据甚至程序代码。
排序:选择排序:总是选出最小的;
冒泡:相邻进行交换;。
检索/查找:顺序查找、二分查找、斐波那契检索、跳步检索等。
函数:
模块化程序设计采用自顶而下(或自底而上)、逐步求精的方法,将一个复杂问题分解成多个功能较单一、相对独立的子问题,每个子问题对应一个功能独立的程序模块。
调用函数将二维数组整体传递给被调用函数,也是将实参数组的首地址(数组名)传递给形参数组名。实际上,形参数组名被自动转换成指向实参数组的指针,实际操作的是实参数组。
在定义函数中指定的形参,在未出现函数调用时,并不占内存中的存储单元,只有在发生函数调用时,形参才被分配内存单元。在调用结束后,形参所占用的内存单元也被释放。
变量和函数的存储类型
代码区:用于存放程序的可执行代码;
全局和静态变量存储区(又称静态变量存储区):用于存放程序中定义的全局变量和静态局部变量;
堆:自由存储区,程序可通过调用动态内存分配函数使用它;
堆栈:用于存放自动变量、函数调用时现场保护信息和返回地址等。
全局变量的存储类型:static和extern
static:表明该全局变量的作用域限制在它所在的程序文件内,当一个程序包含多个文件时,在一个程序文件中定义的全局静态变量,只被所在文件引用,不能被其他文件引用,实现了数据隐藏。
extern:可以扩大全局变量的作用域。在同一个程序文件中,全局变量的作用域从定义位置开始,到所在文件结束。因此,定义在全局变量之前的函数不能引用它。若用extern说明该变量,那么它前面的函数可以引用它。若一个程序由多个文件组成,在其中一个文件内定义了全局变量,在其他文件中对该全局变量使用extern说明,不必重新定义,因为变量只定义一次,可以多次使用extern说明,扩大全局变量的引用范围,实现数据共享。
函数的存储类型:
内部函数:static,只能被它所在的文件中的函数调用;
外部函数:extern,可以被程序的其他文件中的函数调用;
C语言规定,如果定义函数时省略存储类型说明,则默认值为extern类型。
若在同一程序的不同文件中调用该函数,使用extern声明该该函数为外部函数。
在函数调用过程中,又调用了另外一个函数,称为函数的嵌套调用。
构造数据类型:一批数据;
指针是C语言的一种派生数据类型,也是C语言最具代表性的特点之一。unsigned int来存储地址。
程序内存映像:
不同的变量,对应不同的程序实体,它们在内存中的位置并不是任意的,这种分配方式的差别也正是不同类型变量性质差异的根本原因。
源程序编译链接后,生成一个可执行文件,这个二进制文件包含了把程序装入内存以及运行时需要的各种信息。操作系统装载运行该文件时,程序实体按某种布局呈现在内存中,这种布局也称程序的内存映像(memory image)。
栈由高地址向低地址方向增长。
指针变量的定义形式:目标数据对象类型 * 指针变量名称
定义后,就会在内存中为其分配存储空间:32位,未初始化,值不确定。
不管该指针指向的目标数据对象是什么类型,指针本身所占的存储空间一样大,通常用一个机器字(如32位机,指针变量占4个字节内存空间)。可用sizeof运算符获取指针变量所占内存大小(字节单位)。
指针变量在定以后,需要初始化或赋值,否则其中存放的地址值可能不是确定的,通常在定义指针变量的同时对其初始化。
初始化本身就是一个赋值的过程,可将指针变量初始化为某变量的地址,将指针同该数据对象联系起来。
使用取址操作符&:int *p=&a;
更常见的初始化方法是将指针变量赋值为0,即该指针为空指针,表示指针当前并未指向任何有意义的数据。为了提高程序的可读性,标准库定义了一个与空指针0等价的符号常量NULL。#define NULL 0
头文件stdio.h
当把一个指针声明为float类型时,系统会认为该指针保存的地址时指向浮点型数据的,如果试图把一个整形变量的地址赋值给它时,编译器会给出警告。
如果P初值为1000,则P+1表示的地址值,需要根据指针所指数据类型占的字节数来决定。如果p为整型指针,p+1为1004;如果p为字符指针,p+1为1001。
*间接运算符或反引用运算符。
对于指向同一类型的两个指针,可以进行关系运算> 、<、 >=、 <=、==、!=比较结果表示地址间的前后关系,常用于条件语句或者循环语句的控制条件。
通用指针:
C语言提供一种通用指针,允许该指针指向任何类型的数据对象,通过void*定义的通用指针,也叫无指定类型指针,因为它没有指向具体的数据对象,仅指向某个内存地址,而该地址所存储的数据对象的类型也尚未确定。
int a =10;
void *p = &a;//合法
printf("%d",*p);//错误
尽管可以把任意类型变量的地址值或任意类型的指针值赋给通用指针,但不允许直接在void*类型的指针上使用间接引用操作符来访问它所指向的目标数据对象的内容。因为指针类型通常通过类型信息来解析所指向的存储区,而对于通用指针,编译器不知道目标数据的类型,当然不知道如何去解析该存储区的内容。
指针类型强制转换
如果在间接引用运算之前,强制给指针指定一种类型,恢复指针的类型信息,就可以按照相应的数据类型来解释数据了,这就是显式类型转换。上面的语句改为:printf("%d",*(int*)p);//先强制转换p类型,再间接引用指针
就可以正确访问变量a的值了。
通用指针的显式类型转换常用在动态内存管理中。
使用指针运算时要注意以下几点:
1、指针变量使用前,应初始化为有意义的值,或置为null,这样,在做间接运算前,通过If语句即可确定该指针值是否有意义;
2、使用强制转换时,一定要和内存中数据的实际类型相匹配;否则,尽管语法上没有问题,但会产生程序逻辑错误;
3、C语言并不对指针进行越界检查,应避免由此产生的程序错误。
*p++表示先取*P值,再将指针p的值加1;
(*p)++表示先取指针p所指向数据对象的值,然后将该值加1.
尽管数组名与指针有太多相似,数组名可以作为指针使用,但指针和数组名还是有区别的。
1、数组名可以理解为指向数组首地址的指针。但它包含关于数组尺寸的信息,因为数组的大小是可在编译时确定的,在程序中可以利用这部分信息;
2、数组名是一个地址常量,不能利用赋值语句改变它的值,也不能对数组名作自加、自减等运算。
有名字符串和无名字符串:
char str[] ="hello";//用字符型数组存放字符串;
char *string = "hello";//用字符型指针指向字符串;
前者称为有名字符串,数组名str就是该字符串的名字;后者称为无名字符串,它是一个字符串常量,保存在永久存储区中,string只存放了该字符串常数的首地址。
当使用字符数组定义字符串时,将
char str[] ="hello";
分开写成:char str[6];
str = "hello";是错误的。
因为执行完第一个语句后,数组名str代表数组空间的首地址,而字符串常量"hello"则占有另外的存储空间,不允许用另一个地址数组名赋值,故上述会导致编译错误。如果使用字符型指针创建一个字符串,字符指针定义和赋值是可以分开的。char *string;
string ="hello";
赋值语句把字符串的首地址赋值给指针string,并不是字符串的复制。
用字符型数组存放字符串时,可以对字符串进行修改,而用字符指针指向的字符串是不能修改的。
在实际编程中,尽量使用库函数。
指针和函数
用变量名作参数是通过值传递,被调用函数使用传递给它的参数副本,而不是参数本身,因此并不能改变实参的值;如果采用指针作参数,可以传递参数的地址,实现由形参修改实参的效果。
用指针作参数传递一维数组
在C语言中,指针和数组关系密切,很多时候可以互换,指针和数组作为函数参数时,传递的都是地址值。作为形参的数组名会被自动的转换成指向数组的指针。如果将数组名改换成指针,可以得到相同的运行效果。
函数返回指针:指针型函数;在返回指针值时,注意不能返回被调用函数中局部变量的地址,这样会产生错误。因为该变量的存储空间被分配在栈区,即函数的局部调用环境中,当函数返回时,其空间就不再有效。通常返回指针的函数都必须或返回全局对象的地址,或者返回函数内部动态分配的内存。
定义指针保存函数入口地址,该指针称为函数指针。
动态内存分配
系统为每个C语言程序专门划分出堆heap,用作动态内存分配的缓冲池。关键技术是指针。在动态内存区域的数据对象是匿名对象,没有和具体的变量名关联起来,也就无法使用变量的方式访问数据,指针称为操纵它的唯一选择。
C标准库提供了用于动态存储管理的函数,用来在程序运行时分配和释放内存,
malloc:分配所需的字节大小,返回指向所分配空间的第一个字节的指针;
calloc:分配空间,并初始化内存为0,返回所分配空间的内存首地址;
free:释放已分配的空间;
realloc:修改分配空间的大小。
其中,最主要的函数是malloc和free。函数的原型声明在stdlib.h和malloc.h中。
malloc函数原型返回void类型的指针,表示可以将其赋值给任意类型的指针,函数调用成功会返回由堆中分配内存区的首地址。调用失败时,返回null指针。尽管堆栈区空间很大,但也是有限的,也是可能耗尽的。
需要将返回指针强制类型转换。需要测试返回指针是否是空指针NULL来确定内存分配是否成功。同时为了使程序有更好的移植性,使用sizeof运算符。
释放动态内存的工作是由free函数完成的。需要注意,free函数释放的不是指针p本身的内存,而是p所指向的目标数据对象的内存。指针p所占的内存由系统负责,程序员不用关心。
指针p释放后,p值还保持不变,但其指向的内存已不再有效,该指针成为悬空指针。如果在程序中仍使用P操纵内存数据,就会导致程序逻辑错误和系统崩溃。因此,为避免这种情况发生,可在释放内存后,将p赋值为NULL。
动态存储管理需要注意的问题:
1、悬空指针:如果两个指针指向同一块动态内存空间,当对一个指针调用free()释放目标数据对象,另一个指针指向的就不再有意义。
2、内存泄露:动态分配内存在使用完后要及时释放,如果在一个函数中调用malloc()分配内存,并将其首地址赋值给局部指针变量p,在函数中并未调用free()释放该块内存。当函数退出后,局部变量随函数调用栈一起消失。但该匿名对象所对应的在存储空间依然保持有效状态,不能用作他用,这种现象称为内存泄露。
动态分配空间一旦不再使用,就应该及时释放它。
复合数据类型和类型定义
保存用户预先设定好的一组不同类型的数据,C语言称此类自定义类型为结构类型。
struct 结构名
{
成员列表
};
成员名可以与程序中其他变量或其他结构类型中的成员同名,编译器将视其为不同的对象互不干扰。
同类型的成员变量也可以在一条语句中说明,中间用逗号间隔。
最后花括号外的分毫表明定义语句结束。
仅当用该类型定义了变量,系统才会为变量分配内存。
结构可以嵌套定义。不同结构。
无名结构类型,不能唯一标识该结构类型,故此种类型只能在类型定义的同时定义变量、数组及指针,而不能在程序任意位置使用该类型进行变量、数组及指针的定义。
递归结构
如果一个结构的某些成员与该结构属于同一类型,称为递归结构,也叫自嵌套结构。最常用的递归结构是:结构的一个成员是指向本结构类型的指针。例如,存储学生信息可定义递归结构如下:
struct student
{char name[20],sex,addr[30];
char tel[11];
struct student *next;//记录下一个同类型数据的地址
}
在该结构类型定义中,结构指针Next即属于指向本结构类型的指针。可以定义多个。
链表
链表属于数据结构的一种,与数组类似,也是对同类型数据的一种组织形式。但区别于数组的是,链表是通过在记录数据元素同时记录其后续元素地址的办法,确定各元素间的先后顺序,因而链表支持动态存储器分配。并且由于链表中每个元素都只与前一个元素相连,所以得名链表。
每个元素称为一个节点,每个节点均属于同一种递归结构。节点中用于存储数据元素信息的域称为数据域,存储其他节点的地址信息的域称为指针域。
带头节点的空链表就可以表示为头节点的指针域为空NULL。
对链表的主要操作有以下几种:
链表的创建;
链表的输出;
删除一个元素(节点);
插入一个元素(节点)。
一、由于链表的头指针非常重要,所以链表创建成功后,一定要返回头指针。
二、对链表操作无论是删除指定节点还是在指定位置前插入节点,必须有以下4个步骤:
1、判断链表是否为空链表,非空则继续;
2、初始化指针。一般至少需要对两个指针进行初始化:一个指向待查节点,另一个指向待查节点的前驱节点;
3、寻找指定节点。查询应同时满足两个条件:第一,未找到指定节点;第二,链表中还有节点待查。
4、判断是否找到,并做相应处理。由于查询条件是两个,因此终止查找后必须要判断是哪个条件未满足而导致跳出循环,从而判定指定节点是否已找到。
三、被删除的节点要及时释放,避免内存泄露。
联合类型
联合类型也是一种自定义数据类型。允许不同数据类型的数据共享同一片内存空间。前提是每一瞬间只有其中一个类型对应的数据有效。
union 联合名
{数据类型 成员名;
数据类型 成员名;
。。。
}联合变量名;
或者分开写 union share data;//定义union share 类型变量data
编译器在为变量data分配空间时,是按照该联合类型中占用字节最多的成员对应的长度分配的空间。
所谓“共用”同一内存位置,是指联合变量的地址就是其各成员变量的地址。
联合变量中,各成员在存放时会彼此覆盖的,因此,联合类型只能作为不同时存在的各成员的共有类型标识,进而对应于每一个瞬间,只有最后一个存入的成员才是联合变量的当前有效成员。
联合类型允许与结构类型互相嵌套定义。
但结构变量所占用内存长度是各成员所占内存长度之和,每个成员均有属于自己的内存单元,所有成员是可以同时存在的。
联合类型:任一瞬间只有最后一个被赋值的成员有效。
位运算
指的是参与运算的量,是二进制的位。常用于设备驱动程序中屏蔽某些位,实现奇偶校验,或在磁盘文件管理中通过将文件中的字符逐位取反实现文件不可读,以及一些C编译系统通过左移和右移实现乘2和除2运算。
按位与:清零或者取一个数的指定位或屏蔽某一位。
按位或:将某个数的指定位置置1.
按位求反:
按位异或:指定位翻转:中间数,与1异或,翻转;与0异或,相异或保留。
不借助临时变量,交换两个值:
a= a^b
b=b^a
a=a^b
<<左移
data<<n 将data中所有二进制位顺序向左移动n位。
data>>n右移n位
枚举类型
enum 枚举名{枚举列表值} 枚举变量;
枚举值之间用逗号间隔。
每个枚举元素本身都是一个常量,又称为枚举常量。编译时,由系统为每一个枚举常量定义一个表示序号的数值,从0开始顺序定义为0,1,2.。。。
输出枚举元素的值printf("%d",MONDAY)输出整型数。
由于枚举元素是常量,所以在程序中不能对其赋值。
C语言处理枚举类型值和其他整数一样,不提供边界检查来确定存储在枚举类型变量中值得有效性。
类型定义
typedef int INT16指定别名
主要是利于程序的通用与移植。特别是当处理依赖硬件特性的程序时。
定义结构类型的同时定义其别名:
typedef struct结构名
{数据类型 成员名;
数据类型 成员名;
。。。
}定义名;
定义名是struct 结构名的别名。
typedef只是为已有数据类型定义新名字,不是定义新的数据类型。
#define INTERGER int;是预编译命令,会在编译前进行简单字符替换;
typedef int INTERGER;是在编译时处理的语句,处理的方法也不是简单字符替换,而是声明一个类型,并且用该类型定义的变量,此时,编译器一样会对其做类型检查。
编译预处理
#开头,ENTER结束,一行写不下,用\和ENTER结束,然后下一行继续写。
预处理不是C语句,所以末尾无分号。
#define 宏名 字符串
#define 宏名(参数表) 宏体
#define MAX 5
#undef MAX
文件包含
#Include"文件名”
#include<文件名>系统指定的目录
条件编译
形式1
#ifdef 标识符
程序段1
#else
程序段2
#endif
标识符之前用#define命令定义过
形式2
#ifndef 标识符
程序段1
#else
程序段2
#endif
也可以是:
#ifndef 标识符
程序段
#endif
形式3
#if 常量表达式1
程序段1
#elif 常量表达式2
程序段2
#elif 常量表达式3
程序段3
。。。
#else
程序段n+1
#endif
以上黑色的部分可以没有。
本文介绍了C语言的起源、发展及其标准,包括ANSI C、C99等。C语言起源于ALGOL60,以其可读性和可移植性著称,但不适用于直接操作硬件。随着技术进步,C语言经历了多次标准更新,如C99,而不同的编译器对这些标准的支持程度各异。此外,文章还提到了C++、Java等语言的出现及其特点。C语言是面向过程的,而C++支持面向对象。文章还详细讲解了C语言中的格式输出和输入函数,以及数据类型、变量、函数、指针、数组、字符串、内存管理等方面的知识,强调了指针在C语言中的重要性,同时介绍了动态内存分配、链表、结构体、联合体和枚举类型。最后,文章提到了预处理指令,如#define和#include。
275

被折叠的 条评论
为什么被折叠?



