1、数据类型转换:C 语言中如果一个表达式中含有不同类型的常量和变量,在计算时,会将它们自动转换为同一种类型;在 C 语言中也可以对数据类型进行强制转换;
2、自动转换规则:
a)浮点数赋给整型,该浮点数小数被舍去;
b)整数赋给浮点型,数值不变,但是被存储到相应的浮点型变量中;
1、关键字:
2、数据类型:
2.1、基本类型:
2.1.1、整数类型
注意,各种类型的存储大小与系统位数有关,但目前通用的以64位系统为主。
以下列出了32位系统与64位系统的存储大小的差别(windows 相同):
2.1.2、浮点类型
常用基本数据类型占用空间(64位机器为例)
- char : 1个字节
- int :4个字节
- float:4个字节
- double:8个字节
基本类型书写
整数
- a,默认为10进制 ,10 ,20。
- b,以0开头为8进制,045,021。
- c.,以0b开头为2进制,0b11101101。
- d,以0x开头为16进制,0x21458adf。
2.2、枚举类型:
2.2.1、定义格式:
enum 枚举名 {枚举元素1,枚举元素2,……};
2.2.2枚举类型与枚举变量的定义
先定义枚举类型,再定义枚举变量
定义枚举类型的同时定义枚举变量
省略枚举类型,直接定义枚举变量
枚举类型遍历的前提条件:枚举类型必须是连续的。
2.3、void类型:
2.4、派生类型
2.4.1、数组
所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素,最高的地址对应最后一个元素。
2.4.1.1、声明数组及初始化:
type arrayName [arraySize]={……};
2.4.1.2、多维数组
2.4.1.3、传递数组给函数
2.4.1.4、从函数返回数组
C 语言不允许返回一个完整的数组作为函数的参数,所以需要返回数组时建议返回数组指针。
2.4.1.5、指向数组的指针
数组名是一个指向数组中第一个元素的常量指针
使用数组名作为常量指针是合法的,反之亦然。因此,*(balance + 4) 是一种访问 balance[4] 数据的合法方式。
2.4.2、指针
指针是一个变量,其值为另一个变量的地址。即,内存位置的直接地址。(十六进制数)
2.4.2.1、C中的NULL指针
在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为空指针。
在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的。然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。
如需检查一个空指针,您可以使用 if 语句,如下所示:
if(ptr) / 如果 p 非空,则完成 /
if(!ptr) / 如果 p 为空,则完成 /
2.4.2.2、指针的算数运算
++与—类型
数组变量与指针的相等性编程使用
指针的比较:指针可以用关系运算符进行比较,如 ==、< 和 >。如果 p1 和 p2 指向两个相关的变量,比如同一个数组中的不同元素,则可对 p1 和 p2 进行大小比较。
2.4.2.3、指针数组
2.4.2.4、指向指针的指针
2.4.2.5、传递指针给函数
2.4.2.6、从函数返回指针
C 语言不支持在调用函数时返回局部变量的地址,除非定义局部变量为 static 变量。
因为局部变量是存储在内存的栈区内,当函数调用结束后,局部变量所占的内存地址便被释放了,因此当其函数执行完毕后,函数内的变量便不再拥有那个内存地址,所以不能返回其指针。
除非将其变量定义为 static 变量,static 变量的值存放在内存中的静态数据区,不会随着函数执行的结束而被清除,故能返回其地址。
使用表示指针的数组名(即第一个数组元素的地址)
2.4.2.7、函数指针
指向函数的指针变量。
如果在程序中定义了一个函数,那么在编译时系统就会为这个函数代码分配一段存储空间,这段存储空间的首地址称为这个函数的地址。而且函数名表示的就是这个地址。既然是地址我们就可以定义一个指针变量来存放,这个指针变量就叫作函数指针变量,简称函数指针。
函数指针的定义方式为:
函数返回值类型 ( 指针变量名) (函数参数列表);
指针变量名)”两端的括号不能省略,括号改变了运算符的优先级。如果省略了括号,就不是定义函数指针而是一个函数声明了,即声明了一个返回值类型为指针型的函数。
指向函数的指针变量没有 ++ 和 — 运算。
2.4.3、字符串
在 C 语言中,字符串实际上是使用 null 字符 ‘\0’ 终止的一维字符数组。
char greeting[] = “Hello”={‘h’, ‘e’, ‘l’, ‘l’, ‘o’, ‘\0’}
2.4.4、结构体
2.4.4.1、结构体的定义:
struct tag {
member-list
member-list
member-list
…
} variable-list ;
如果两个结构体互相包含,则需要对其中一个结构体进行不完整声明,如下所示:
2.4.4.2、结构体初始化:
2.4.4.3、访问结构成员:
2.4.4.4、结构作为函数参数
2.4.4.5、指向结构的指针
struct Books *struct_pointer;
为了使用结构指针访问结构的成员,您必须使用->运算符访问,struct_pointer->title;
2.4.4.6、位域
把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。
典型的实例:
- 用 1 位二进位存放一个开关量时,只有 0 和 1 两种状态。
- 读取外部文件格式——可以读取非标准的文件格式。例如:9 位的整数。
定义:位域定义与结构定义相仿,其形式为:
struct 位域结构名
{
位域列表
};
其中位域列表的形式为:
类型说明符 位域名: 位域长度
例如:
struct bs{
int a:8;
int b:2;
int c:6;
}data;
对于位域的定义尚有以下几点说明:
- 一个位域存储在同一个字节中,如一个字节所剩空间不够存放另一位域时,则会从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:
struct bs{
unsigned a:4;
unsigned :4; / 空域 /
unsigned b:4; / 从下一单元开始存放 /
unsigned c:4}
- 由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8位二进位。如果最大长度大于计算机的整数字长,一些编译器可能会允许域的内存重叠,另外一些编译器可能会把大于一个域的部分存储在下一个字中。
位域可以是无名位域,这时它只用来作填充或调整位置。无名的位域是不能使用的。
2.4.4.7、共用体
共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。您可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。
定义方式:
union [union tag]{
member definition;
member definition;
…
member definition;
} [one or more union variables];
union tag 是可选的,每个 member definition 是标准的变量定义,比如 int i; 或者 float f; 或者其他有效的变量定义。在共用体定义的末尾,最后一个分号之前,您可以指定一个或多个共用体变量,这是可选的。
共用体占用的内存应足够存储共用体中最大的成员。
访问方式:
为了访问共用体的成员,我们使用成员访问运算符(.)。成员访问运算符是共用体变量名称和我们要访问的共用体成员之间的一个句号。您可以使用 union 关键字来定义共用体类型的变量。2.5、typedef
2.5.1、定义方式:typedef unsigned char BYTE;
结构体使用方法:
typedef struct Books{
char title[50];
char author[50];
char subject[100];
int book_id;
} Book;
typedef vs #define
#define 是 C 指令,用于为各种数据类型定义别名,与 typedef 类似,但是它们有以下几点不同:
typedef 仅限于为类型定义符号名称,#define 不仅可以为类型定义别名,也能为数值定义别名,比如您可以定义 1 为 ONE。
typedef 是由编译器执行解释的,#define 语句是由预编译器进行处理的。3、存储类
存储类定义 C 程序中变量/函数的范围(可见性)和生命周期。这些说明符放置在它们所修饰的类型之前。
3.1、auto存储类:
所有局部变量默认的存储类(只能用在函数内,只能修饰局部变量)
3.2、register存储类:
定义存储在寄存器中而不是RAM中的局部变量,变量的最大尺寸等于寄存器的大小,且不能对他应用一元的‘&运算符。(它没有内存未知)
3.3、static存储类:
指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用 static 修饰局部变量可以在函数调用之间保持局部变量的值(调用函数时函数内增添了static存储类修饰符的变量值不会被重置。)。
static 修饰符也可以应用于全局变量。当 static 修饰全局变量时,会使变量的作用域限制在声明它的文件内。3.4、extern存储类:
用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当您使用 extern 时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。
当您有多个文件且定义了一个可以在其他文件中使用的全局变量或函数时,可以在其他文件中使用 extern 来得到已定义的变量或函数的引用。可以这么理解,extern 是用来在另一个文件中声明一个全局变量或函数。4、运算符
4.1、特殊的位运算符:
左移:<<二进制左移运算符,将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边的补0)
右移:>>二进制右移运算符,将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。4.2、特殊的赋值运算符:
4.3、杂项运算符:
4.4、运算符优先级:
5、判断
if……else if……else if……else
switch(a){
case a1:
……
break;
defaudlt:
}6、循环语句:
while;do while;for
Ctrl+c键可以终止一个无限循环。7、C函数
7.1、函数定义的一般形式如下:
return_type function_name``(`` parameter list ``)
{
body of the ``function
}
在函数声明中,参数的名称并不重要,只有参数的类型是必需的,因此下面也是有效的声明:
int max(int, int);
7.2、两种向函数传递参数的方式:(默认情况下,C使用传值调用)
8、C作用域规则:
C 语言中有三个地方可以声明变量:
在函数或块内部的局部变量(与全局变量同名,使用局部变量)
在所有函数外部的全局变量
在形式参数的函数参数定义中(与全局变量同名,使用局部变量)
全局变量与局部变量在内存中的区别:全局变量保存在内存的全局存储区中,占用静态的存储单元;
- 局部变量保存在栈中,只有在所在函数被调用时才动态地为变量分配存储单元。
当局部变量被定义时,系统不会对其初始化,您必须自行对其初始化。定义全局变量时,系统会自动对其初始化
9、输入&输出
C 语言把所有的设备都当作文件。所以设备(比如显示器)被处理的方式与文件相同。以下三个文件会在程序执行时自动打开,以便访问键盘和屏幕。
9.1、printf()和scanf()函数
C 语言中的 I/O (输入/输出) 通常使用 printf() 和 scanf() 两个函数。
格式控制符如下表
printf() 格式控制符的完整形式如下:
%[flag][width][.precision]type
[ ] 表示此处的内容可有可无,是可以省略的。
1) type 表示输出类型,比如 %d、%f、%c、%lf,type 就分别对应 d、f、c、lf;再如,%-9d
中 type 对应 d。
type 这一项必须有,这意味着输出时必须要知道是什么类型。
2) width 表示最小输出宽度,也就是至少占用几个字符的位置;例如,%-9d
中 width 对应 9,表示输出结果最少占用 9 个字符的宽度。
当输出结果的宽度不足 width 时,以空格补齐(如果没有指定对齐方式,默认会在左边补齐空格);当输出结果的宽度超过 width 时,width 不再起作用,按照数据本身的宽度来输出。
3) .precision 表示输出精度,也就是小数的位数。
当小数部分的位数大于 precision 时,会按照四舍五入的原则丢掉多余的数字;
当小数部分的位数小于 precision 时,会在后面补 0。
另外,.precision 也可以用于整数和字符串,但是功能却是相反的:
用于整数时,.precision 表示最小输出宽度。与 width 不同的是,整数的宽度不足时会在左边补 0,而不是补空格。
用于字符串时,.precision 表示最大输出宽度,或者说截取字符串。当字符串的长度大于 precision 时,会截掉多余的字符;当字符串的长度小于 precision 时,.precision 就不再起作用。
4) flag 是标志字符。%#x
中 flag 对应 #,%-9d
中 flags 对应-
。下表列出了 printf() 可以用的 flag
9.2、getchar()和putchar()函数
int getchar(void);读取下一个可用的字符,并把它返回为一个整数。这个函数在同一个时间内只会读取一个单一的字符
int putchar(int c):把字符输出到屏幕上,并返回相同的字符。这个函数在同一个时间内只会输出一个单一的字符。
9.3、gets()&puts()函数
char gets(char s) 函数读取一行到 s 所指向的缓冲区,直到一个终止符或 EOF(无资料可读状态)。
int puts(const char *s) 函数把字符串 s 和一个尾随的换行符输出。
10、文件读写
10.1、打开文件
您可以使用 fopen( ) 函数来创建一个新的文件或者打开一个已有的文件,这个调用会初始化类型 FILE 的一个对象,类型 FILE 包含了所有用来控制流的必要的信息。下面是这个函数调用的原型:
FILE fopen( const char filename, const char mode );
filename 是字符串,用来命名文件,访问模式 *mode 的值可以是下列值中的一个:
如果处理的是二进制文件,则需使用下面的访问模式来取代上面的访问模式:
10.2、关闭文件
为了关闭文件,请使用 fclose( ) 函数。函数的原型如下:
int fclose( FILE fp );
如果成功关闭文件,fclose( ) 函数返回零,如果关闭文件时发生错误,函数返回 EOF。这个函数实际上,会清空缓冲区中的数据,关闭文件,并释放用于该文件的所有内存。EOF 是一个定义在头文件 *stdio.h 中的常量。
10.3、写入文件
下面是把字符写入到流中的最简单的函数:
int fputc( int c, FILE fp );
函数 fputc() 把参数 c 的字符值写入到 fp 所指向的输出流中。如果写入成功,它会返回写入的字符,如果发生错误,则会返回 EOF。您可以使用下面的函数来把一个以 null 结尾的字符串写入到流中:
int fputs( const char s, FILE fp );
函数 fputs() 把字符串 s 写入到 fp 所指向的输出流中。如果写入成功,它会返回一个非负值,如果发生错误,则会返回 EOF。您也可以使用 **int fprintf(FILE fp,const char format, …)* 函数来写把一个字符串写入到文件中。
10.4、读取文件
下面是从文件读取单个字符的最简单的函数:
int fgetc( FILE fp );
fgetc() 函数从 fp 所指向的输入文件中读取一个字符。返回值是读取的字符,如果发生错误则返回 EOF。下面的函数允许您从流中读取一个字符串:
char fgets( char buf, int n, FILE fp );
函数 fgets() 从 fp 所指向的输入流中读取 n - 1 个字符。它会把读取的字符串复制到缓冲区 buf,并在最后追加一个 null 字符来终止字符串。
如果这个函数在读取最后一个字符之前就遇到一个换行符 ‘\n’ 或文件的末尾 EOF,则只会返回读取到的字符,包括换行符。您也可以使用 int fscanf(FILE fp, const char format, …) 函数来从文件中读取字符串,但是在遇到第一个空格和换行符时,它会停止读取。
10.5、二进制I/O函数
下面两个函数用于二进制输入和输出:
size_t fread(void ptr, size_t size_of_elements, size_t number_of_elements, FILE a_file);
size_t fwrite(const void ptr, size_t size_of_elements, size_t number_of_elements, FILE a_file);
这两个函数都是用于存储块的读写 - 通常是数组或结构体。