导图社区 C语言基础知识总结
C语言是一种通用的、面向过程式的计算机程序设计语言,下图快速让你对C语言基础知识有个全面了解。
编辑于2020-10-14 18:47:45C语言【一】
格式化输出
转换说明符 含义 d,i 插入值类型为有符号整型,转换为十进制形式 o,u,x,X 插入值类型为无符号整型,转换为八机制、十进制和十六进制形式。x表示以小写字母a-f显示16进制数,X表示以A-F显示十六进制数。 f 插入值类型为double,转换为十进制形式,小数点放置于正确位置。默认小数点后显示6个数字。 e,E 插入值类型为double,转换为科学计数法标识。默认小数点后保留6位。选择e,将e放在指数前面,如果选择E,则将E放在指数前 g,G 插入值类型为double, g将插入值转换为f形式或e形式,G将插入值转换为f形式或E形式。在数值的指数部分小于-4或不小于精度值时,选择e或E形式. 不显示尾部的0,小数点仅在后面跟有数字时才显示。 c 插入值类型为无符号字符,输出字符形式 s 插入值类型为字符串。当达到精度或遇到空字符停止写操作 p 插入值类型为指针,转换为可显示格式的void*型值 n 输入值类型为int型指针,用于存储到本次调用到目前位置输出的字符个数 % 输出字符%
关键字
auto 声明自动变量 break 跳出当前循环 case 开关语句分支 char 声明字符型变量或返回值类型 const 定义常量,如果一个变量被 const 修饰,那么它的值就不能再被改变 continue 结束当前循环,开始下一轮循环 default 开关语句中的"其它"分支 do 循环语句的循环体 double 声明双精度浮点型变量或返回值类型 else 条件语句否定分支(与 if 连用) enum 声明枚举类型 extern 声明变量或是在其它文件或本文件的其他位置定义 float 声明浮点型变量或返回值类型 for 一种循环语句 goto 无条件跳转语句 if 条件语句 int 声明整型变量或 long 声明长整型变量或返回值类型 register 声明寄存器变量 return 子程序返回语句(可以带参数,也可不带参数) short 声明短整型变量或 signed 声明有符号类型变量或 sizeof 计算数据类型或变量长度(即所占字节数) static 声明静态变量 struct 声明结构体类型 switch 用于开关语句 typedef 用以给数据类型取别名 unsigned 声明无符号类型变量或 union 声明共用体类型 void 声明无返回值或无参数,声明无类型指针 volatile 说明变量在程序执行中可被隐含地改变 while 循环语句的循环条件
C99 新增关键字 _Bool _Complex _Imaginary inline restrict C11 新增关键字 _Alignas _Alignof _Atomic _Generic _Noreturn _Static_assert _Thread_local
常量
整数常量
85 /* 十进制 */ 0213 /* 八进制 */ 0x4b /* 十六进制 */ 30 /* 整数 */ 30u /* 无符号整数 */ 30l /* 长整数 */ 30ul /* 无符号长整数 */
浮点常量
3.14159 /* 合法的 */ 314159E-5L /* 合法的 */
字符常量
转义序列 含义 \\ \ 字符 \' ' 字符 \" " 字符 \? ? 字符 \a 警报铃声 \b 退格键 \f 换页符 \n 换行符 \r 回车 \t 水平制表符 \v 垂直制表符 \ooo 一到三位的八进制数 \xhh . . . 一个或多个数字的十六进制数
字符串常量
定义常量
使用 #define 预处理器。 使用 const 关键字。
变量
类型 描述 char 通常是一个字节(八位)。这是一个整数类型。 int 对机器而言,整数的最自然的大小。 float 单精度浮点值。单精度是这样的格式,1位符号,8位指数,23位小数。  double 双精度浮点值。双精度是1位符号,11位指数,52位小数。  void 表示类型的缺失。
数据类型
整数类型 下表列出了关于标准整数类型的存储大小和值范围的细节: 类型 存储大小 值范围 char 1 字节 -128 到 127 或 0 到 255 unsigned char 1 字节 0 到 255 signed char 1 字节 -128 到 127 int 2 或 4 字节 -32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647 unsigned int 2 或 4 字节 0 到 65,535 或 0 到 4,294,967,295 short 2 字节 -32,768 到 32,767 unsigned short 2 字节 0 到 65,535 long 4 字节 -2,147,483,648 到 2,147,483,647 unsigned long 4 字节 0 到 4,294,967,295
浮点类型 下表列出了关于标准浮点类型的存储大小、值范围和精度的细节: 类型 存储大小 值范围 精度 float 4 字节 1.2E-38 到 3.4E+38 6 位小数 double 8 字节 2.3E-308 到 1.7E+308 15 位小数 long double 16 字节 3.4E-4932 到 1.1E+4932 19 位小数
void 类型 void 类型指定没有可用的值。它通常用于以下三种情况下: 序号 类型与描述 1 返回为空 C 中有各种都不返回值,或者您可以说它们返回空。不返回值的的返回类型为空。例如 void exit (int status); 2 参数为空 C 中有各种不接受任何参数。不带参数的可以接受一个 void。例如 int rand(void); 3 指针指向 void 类型为 void * 的指针代表对象的地址,而不是类型。例如,内存分配 void *malloc( size_t size ); 返回指向 void 的指针,可以转换为任何数据类型。
C 存储类
auto:默认
register
register 存储类用于定义存储在寄存器中而不是 RAM 中的局部变量
static
static 存储类指示编译器在程序的生命周期内保持局部变量的存在
extern
extern 存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。
运算符
算术运算符
运算符 描述 实例 + 把两个操作数相加 A + B 将得到 30 - 从第一个操作数中减去第二个操作数 A - B 将得到 -10 * 把两个操作数相乘 A * B 将得到 200 / 分子除以分母 B / A 将得到 2 % 取模运算符,整除后的余数 B % A 将得到 0 ++ 自增运算符,整数值增加 1 A++ 将得到 11 -- 自减运算符,整数值减少 1 A-- 将得到 9
关系运算符
运算符 描述 实例 == 检查两个操作数的值是否相等,如果相等则条件为真。 (A == B) 为假。 != 检查两个操作数的值是否相等,如果不相等则条件为真。 (A != B) 为真。 > 检查左操作数的值是否大于右操作数的值,如果是则条件为真。 (A > B) 为假。 < 检查左操作数的值是否小于右操作数的值,如果是则条件为真。 (A < B) 为真。 >= 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。 (A >= B) 为假。 <= 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。 (A <= B) 为真。
逻辑运算符
运算符 描述 实例 && 称为逻辑与运算符。如果两个操作数都非零,则条件为真。 (A && B) 为假。 || 称为逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真。 (A || B) 为真。 ! 称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假。 !(A && B) 为真。
位运算符
假设如果 A = 60,且 B = 13,现在以二进制格式表示,它们如下所示: A = 0011 1100 B = 0000 1101 ----------------- A&B = 0000 1100 A|B = 0011 1101 A^B = 0011 0001 ~A = 1100 0011 下表显示了 C 语言支持的位运算符。假设变量 A 的值为 60,变量 B 的值为 13,则: 运算符 描述 实例 & 按位与操作,按二进制位进行"与"运算。运算规则: 0&0=0; 0&1=0; 1&0=0; 1&1=1; (A & B) 将得到 12,即为 0000 1100 | 按位或运算符,按二进制位进行"或"运算。运算规则: 0|0=0; 0|1=1; 1|0=1; 1|1=1; (A | B) 将得到 61,即为 0011 1101 ^ 异或运算符,按二进制位进行"异或"运算。运算规则: 0^0=0; 0^1=1; 1^0=1; 1^1=0; (A ^ B) 将得到 49,即为 0011 0001 ~ 取反运算符,按二进制位进行"取反"运算。运算规则: ~1=0; ~0=1; (~A ) 将得到 -61,即为 1100 0011,一个有符号二进制数的补码形式。 << 二进制左移运算符。将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。 A << 2 将得到 240,即为 1111 0000 >> 二进制右移运算符。将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。
赋值运算符
运算符 描述 实例 = 简单的赋值运算符,把右边操作数的值赋给左边操作数 C = A + B 将把 A + B 的值赋给 C += 加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数 C += A 相当于 C = C + A -= 减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数 C -= A 相当于 C = C - A *= 乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数 C *= A 相当于 C = C * A /= 除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数 C /= A 相当于 C = C / A %= 求模且赋值运算符,求两个操作数的模赋值给左边操作数 C %= A 相当于 C = C % A <<= 左移且赋值运算符 C <<= 2 等同于 C = C << 2 >>= 右移且赋值运算符 C >>= 2 等同于 C = C >> 2 &= 按位与且赋值运算符 C &= 2 等同于 C = C & 2 ^= 按位异或且赋值运算符 C ^= 2 等同于 C = C ^ 2 |= 按位或且赋值运算符
杂项运算符
运算符 描述 实例 sizeof() 返回变量的大小。 sizeof(a) 将返回 4,其中 a 是整数。 & 返回变量的地址。 &a; 将给出变量的实际地址。 * 指向一个变量。 *a; 将指向一个变量。 ? : 条件表达式 如果条件为真 ? 则值为 X : 否则值为 Y
优先级
类别 运算符 结合性 后缀 () [] -> . ++ - - 从左到右 一元 + - ! ~ ++ - - (type)* & sizeof 从右到左 乘除 * / % 从左到右 加减 + - 从左到右 移位 << >> 从左到右 关系 < <= > >= 从左到右 相等 == != 从左到右 位与 AND & 从左到右 位异或 XOR ^ 从左到右 位或 OR | 从左到右 逻辑与 AND && 从左到右 逻辑或 OR || 从左到右 条件 ?: 从右到左 赋值 = += -= *= /= %=>>= <<= &= ^= |= 从右到左 逗号 , 从左到右
语法
C 判断
语句 描述 if 语句 一个 if 语句 由一个布尔表达式后跟一个或多个语句组成。 if...else 语句 一个 if 语句 后可跟一个可选的 else 语句,else 语句在布尔表达式为假时执行。 嵌套 if 语句 您可以在一个 if 或 else if 语句内使用另一个 if 或 else if 语句。 switch 语句 一个 switch 语句允许测试一个变量等于多个值时的情况。 嵌套 switch 语句 您可以在一个 switch 语句内使用另一个 switch 语句。
C 循环
循环类型 C 语言提供了以下几种循环类型。点击链接查看每个类型的细节。 循环类型 描述 while 循环 当给定条件为真时,重复语句或语句组。它会在执行循环主体之前测试条件。 for 循环 多次执行一个语句序列,简化管理循环变量的代码。 do...while 循环 除了它是在循环主体结尾测试条件外,其他与 while 语句类似。 嵌套循环 您可以在 while、for 或 do..while 循环内使用一个或多个循环。
循环控制语句 循环控制语句改变你代码的执行顺序。通过它你可以实现代码的跳转。 C 提供了下列的循环控制语句。点击链接查看每个语句的细节。 控制语句 描述 break 语句 终止循环或 switch 语句,程序流将继续执行紧接着循环或 switch 的下一条语句。 continue 语句 告诉一个循环体立刻停止本次循环迭代,重新开始下次循环迭代。 goto 语句 将控制转移到被标记的语句。但是不建议在程序中使用 goto 语句。
无限循环:for( ; ; )
C 函数
定义
return_type function_name( parameter list ) { body of the function }
函数声明
return_type function_name( parameter list );
调用函数
函数参数
调用类型 描述 传值调用 该方法把参数的实际值复制给函数的形式参数。在这种情况下,修改函数内的形式参数不会影响实际参数。 引用调用 通过指针传递方式,形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。
C 作用域规则
在函数或块内部的局部变量 在所有函数外部的全局变量 在形式参数的函数参数定义中
C 数组
声明数组 在 C 中要声明一个数组,需要指定元素的类型和元素的数量,如下所示: type arrayName [ arraySize ];
初始化数组 在 C 中,您可以逐个初始化数组,也可以使用一个初始化语句,如下所示: double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};
访问数组元素 数组元素可以通过数组名称加索引进行访问。元素的索引是放在方括号内,跟在数组名称的后边。例如: double salary = balance[9];
C 中数组详解
多维数组
传递数组给函数
从函数返回数组
C enum(枚举)
enum DAY { MON=1, TUE, WED, THU, FRI, SAT, SUN };
C语言【二】
C 指针
定义:type *var-name;
如何使用指针? 使用指针时会频繁进行以下几个操作:定义一个指针变量、把变量地址赋值给指针、访问指针变量中可用地址的值。这些是通过使用一元运算符 * 来返回位于操作数所指定地址的变量的值。下面的实例涉及到了这些操作: 实例 #include <stdio.h> int main () { int var = 20; /* 实际变量的声明 */ int *ip; /* 指针变量的声明 */ ip = &var; /* 在指针变量中存储 var 的地址 */ printf("Address of var variable: %p\n", &var ); /* 在指针变量中存储的地址 */ printf("Address stored in ip variable: %p\n", ip ); /* 使用指针访问值 */ printf("Value of *ip variable: %d\n", *ip ); return 0; }
C 函数指针与回调函数
函数指针 函数指针是指向函数的指针变量。 通常我们说的指针变量是指向一个整型、字符型或数组等变量,而函数指针是指向函数。 函数指针可以像一般函数一样,用于调用函数、传递参数。 函数指针变量的声明: typedef int (*fun_ptr)(int,int); // 声明一个指向同样参数、返回值的函数指针类型
回调函数 函数指针作为某个函数的参数
C 字符串
C 字符串 在 C 语言中,字符串实际上是使用 null 字符 '\0' 终止的一维字符数组。因此,一个以 null 结尾的字符串,包含了组成字符串的字符。 下面的声明和初始化创建了一个 "Hello" 字符串。由于在数组的末尾存储了空字符,所以字符数组的大小比单词 "Hello" 的字符数多一个。 char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; 依据数组初始化规则,您可以把上面的语句写成以下语句: char greeting[] = "Hello";
序号 函数 & 目的 1 strcpy(s1, s2); 复制字符串 s2 到字符串 s1。 2 strcat(s1, s2); 连接字符串 s2 到字符串 s1 的末尾。 3 strlen(s1); 返回字符串 s1 的长度。 4 strcmp(s1, s2); 如果 s1 和 s2 是相同的,则返回 0;如果 s1<s2 则返回小于 0;如果 s1>s2 则返回大于 0。 5 strchr(s1, ch); 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。 6 strstr(s1, s2); 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。
#include <string.h>
C 结构体
定义结构 为了定义结构,您必须使用 struct 语句。struct 语句定义了一个包含多个成员的新的数据类型,struct 语句的格式如下: struct tag { member-list member-list member-list ... } variable-list ;
struct Books { char title[50]; char author[50]; char subject[100]; int book_id; } book;
结构体变量的初始化 和其它类型变量一样,对结构体变量可以在定义时指定初始值。
struct Books { char title[50]; char author[50]; char subject[100]; int book_id; } book = {"C 语言", "RUNOOB", "编程语言", 123456};
访问结构成员 为了访问结构的成员,我们使用成员访问运算符(.)
printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n", book.title, book.author, book.subject, book.book_id);
结构作为函数参数 您可以把结构作为函数参数,传参方式与其他类型的变量或指针类似。
/* 函数声明 */ void printBook( struct Books book );
指向结构的指针 您可以定义指向结构的指针,方式与定义指向其他类型变量的指针相似
void printBook( struct Books *book ) { printf( "Book title : %s\n", book->title); printf( "Book author : %s\n", book->author); printf( "Book subject : %s\n", book->subject); printf( "Book book_id : %d\n", book->book_id); } /* 通过传 Book1 的地址来输出 Book1 信息 */ printBook( &Book1 );
C 共用体
共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。您可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。 定义共用体 为了定义共用体,您必须使用 union 语句,方式与定义结构类似。union 语句定义了一个新的数据类型,带有多个成员。union 语句的格式如下: union [union tag] { member definition; member definition; ... member definition; } [one or more union variables];
union Data { int i; float f; char str[20]; } data;
访问共用体成员 为了访问共用体的成员,我们使用成员访问运算符(.)
C 位域
位域声明 在结构内声明位域的形式如下: struct { type [member_name] : width ; };
struct { unsigned int age : 3; } Age;
C typedef
C 语言提供了 typedef 关键字,您可以使用它来为类型取一个新的名字
typedef unsigned char BYTE;BYTE b1, b2;
typedef vs #define #define 是 C 指令,用于为各种数据类型定义别名,与 typedef 类似,但是它们有以下几点不同: typedef 仅限于为类型定义符号名称,#define 不仅可以为类型定义别名,也能为数值定义别名,比如您可以定义 1 为 ONE。 typedef 是由编译器执行解释的,#define 语句是由预编译器进行处理的。
C 输入 & 输出
标准文件 C 语言把所有的设备都当作文件。所以设备(比如显示器)被处理的方式与文件相同。以下三个文件会在程序执行时自动打开,以便访问键盘和屏幕。 标准文件 文件指针 设备 标准输入 stdin 键盘 标准输出 stdout 屏幕 标准错误 stderr 您的屏幕 文件指针是访问文件的方式,本节将讲解如何从屏幕读取值以及如何把结果输出到屏幕上。 C 语言中的 I/O (输入/输出) 通常使用 printf() 和 scanf() 两个函数。 scanf() 函数用于从标准输入(键盘)读取并格式化, printf() 函数发送格式化输出到标准输出(屏幕)。
getchar() & putchar() 函数 int getchar(void) 函数从屏幕读取下一个可用的字符,并把它返回为一个整数。这个函数在同一个时间内只会读取一个单一的字符。您可以在循环内使用这个方法,以便从屏幕上读取多个字符。 int putchar(int c) 函数把字符输出到屏幕上,并返回相同的字符。这个函数在同一个时间内只会输出一个单一的字符。您可以在循环内使用这个方法,以便在屏幕上输出多个字符。
#include <stdio.h> int main( ) { int c; printf( "Enter a value :"); c = getchar( ); printf( "\nYou entered: "); putchar( c ); printf( "\n"); return 0; }
gets() & puts() 函数 char *gets(char *s) 函数从 stdin 读取一行到 s 所指向的缓冲区,直到一个终止符或 EOF。 int puts(const char *s) 函数把字符串 s 和一个尾随的换行符写入到 stdout。
#include <stdio.h> int main( ) { char str[100]; printf( "Enter a value :"); gets( str ); printf( "\nYou entered: "); puts( str ); return 0; }
scanf() 和 printf() 函数 int scanf(const char *format, ...) 函数从标准输入流 stdin 读取输入,并根据提供的 format 来浏览输入。 int printf(const char *format, ...) 函数把输出写入到标准输出流 stdout ,并根据提供的格式产生输出。 format 可以是一个简单的常量字符串,但是您可以分别指定 %s、%d、%c、%f 等来输出或读取字符串、整数、字符或浮点数。还有许多其他可用的格式选项,可以根据需要使用。如需了解完整的细节,可以查看这些函数的参考手册
C 文件读写
打开文件 您可以使用 fopen( ) 函数来创建一个新的文件或者打开一个已有的文件,这个调用会初始化类型 FILE 的一个对象,类型 FILE 包含了所有用来控制流的必要的信息。下面是这个函数调用的原型: FILE *fopen( const char * filename, const char * mode ); 在这里,filename 是字符串,用来命名文件,访问模式 mode 的值可以是下列值中的一个: 模式 描述 r 打开一个已有的文本文件,允许读取文件。 w 打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会从文件的开头写入内容。如果文件存在,则该会被截断为零长度,重新写入。 a 打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容。 r+ 打开一个文本文件,允许读写文件。 w+ 打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件。 a+ 打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。 如果处理的是二进制文件,则需使用下面的访问模式来取代上面的访问模式: "rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"
关闭文件 为了关闭文件,请使用 fclose( ) 函数。函数的原型如下: int fclose( FILE *fp ); 如果成功关闭文件,fclose( ) 函数返回零,如果关闭文件时发生错误,函数返回 EOF。这个函数实际上,会清空缓冲区中的数据,关闭文件,并释放用于该文件的所有内存。EOF 是一个定义在头文件 stdio.h 中的常量。 C 标准库提供了各种函数来按字符或者以固定长度字符串的形式读写文件。
读取文件 下面是从文件读取单个字符的最简单的函数: 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, ...) 函数来从文件中读取字符串,但是在遇到第一个空格和换行符时,它会停止读取。
#include <stdio.h> int main() { FILE *fp = NULL; char buff[255]; fp = fopen("/tmp/test.txt", "r"); fscanf(fp, "%s", buff); printf("1: %s\n", buff ); fgets(buff, 255, (FILE*)fp); printf("2: %s\n", buff ); fgets(buff, 255, (FILE*)fp); printf("3: %s\n", buff ); fclose(fp); }
二进制 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); 这两个函数都是用于存储块的读写 - 通常是数组或结构体。
C 预处理器
C 预处理器 C 预处理器不是编译器的组成部分,但是它是编译过程中一个单独的步骤。简言之,C 预处理器只不过是一个文本替换工具而已,它们会指示编译器在实际编译之前完成所需的预处理。我们将把 C 预处理器(C Preprocessor)简写为 CPP。 所有的预处理器命令都是以井号(#)开头。它必须是第一个非空字符,为了增强可读性,预处理器指令应从第一列开始。下面列出了所有重要的预处理器指令: 指令 描述 #define 定义宏 #include 包含一个源代码文件 #undef 取消已定义的宏 #ifdef 如果宏已经定义,则返回真 #ifndef 如果宏没有定义,则返回真 #if 如果给定条件为真,则编译下面代码 #else #if 的替代方案 #elif 如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码 #endif 结束一个 #if……#else 条件编译块 #error 当遇到标准错误时,输出错误消息 #pragma 使用标准化方法,向编译器发布特殊的命令到编译器中
#define MAX_ARRAY_LENGTH 20 这个指令告诉 CPP 把所有的 MAX_ARRAY_LENGTH 替换为 20。使用 #define 定义常量来增强可读性。 #include <stdio.h> #include "myheader.h" 这些指令告诉 CPP 从系统库中获取 stdio.h,并添加文本到当前的源文件中。下一行告诉 CPP 从本地目录中获取 myheader.h,并添加内容到当前的源文件中。 #undef FILE_SIZE #define FILE_SIZE 42 这个指令告诉 CPP 取消已定义的 FILE_SIZE,并定义它为 42。 #ifndef MESSAGE #define MESSAGE "You wish!" #endif
C 头文件
C 头文件 头文件是扩展名为 .h 的文件,包含了 C 函数声明和宏定义,被多个源文件中引用共享。有两种类型的头文件:程序员编写的头文件和编译器自带的头文件。
引用头文件的语法 使用预处理指令 #include 可以引用用户和系统头文件。它的形式有以下两种: #include <file> 这种形式用于引用系统头文件。它在系统目录的标准列表中搜索名为 file 的文件。在编译源代码时,您可以通过 -I 选项把目录前置在该列表前。 #include "file" 这种形式用于引用用户头文件。它在包含当前文件的目录中搜索名为 file 的文件。在编译源代码时,您可以通过 -I 选项把目录前置在该列表前。
只引用一次头文件
有条件引用
C 强制类型转换
C 强制类型转换 强制类型转换是把变量从一种类型转换为另一种数据类型。
(type_name) expression
C 错误处理
C 错误处理 C 语言不提供对错误处理的直接支持,但是作为一种系统编程语言,它以返回值的形式允许您访问底层数据。在发生错误时,大多数的 C 或 UNIX 函数调用返回 1 或 NULL,同时会设置一个错误代码 errno,该错误代码是全局变量,表示在函数调用期间发生了错误。您可以在 errno.h 头文件中找到各种各样的错误代码。 所以,C 程序员可以通过检查返回值,然后根据返回值决定采取哪种适当的动作。开发人员应该在程序初始化时,把 errno 设置为 0,这是一种良好的编程习惯。0 值表示程序中没有错误。
errno、perror() 和 strerror() C 语言提供了 perror() 和 strerror() 函数来显示与 errno 相关的文本消息。 perror() 函数显示您传给它的字符串,后跟一个冒号、一个空格和当前 errno 值的文本表示形式。 strerror() 函数,返回一个指针,指针指向当前 errno 值的文本表示形式。
被零除的错误 在进行除法运算时,如果不检查除数是否为零,则会导致一个运行时错误。 为了避免这种情况发生,下面的代码在进行除法运算前会先检查除数是否为零:
C 递归
递归指的是在函数的定义中使用函数自身的方法。
数的阶乘
斐波那契数列
0、1、1、2、3、5、8、13、21、34、……
F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 3,n ∈ N*)
C 可变参数
int func(int, ... ) { . . . } int main() { func(2, 2, 3); func(3, 2, 3, 4); }
请注意,函数 func() 最后一个参数写成省略号,即三个点号(...),省略号之前的那个参数是 int,代表了要传递的可变参数的总数。为了使用这个功能,您需要使用 stdarg.h 头文件,该文件提供了实现可变参数功能的函数和宏。具体步骤如下: 定义一个函数,最后一个参数为省略号,省略号前面可以设置自定义参数。 在函数定义中创建一个 va_list 类型变量,该类型是在 stdarg.h 头文件中定义的。 使用 int 参数和 va_start 宏来初始化 va_list 变量为一个参数列表。宏 va_start 是在 stdarg.h 头文件中定义的。 使用 va_arg 宏和 va_list 变量来访问参数列表中的每个项。 使用宏 va_end 来清理赋予 va_list 变量的内存。
#include <stdio.h> #include <stdarg.h> double average(int num,...) { va_list valist; double sum = 0.0; int i; /* 为 num 个参数初始化 valist */ va_start(valist, num); /* 访问所有赋给 valist 的参数 */ for (i = 0; i < num; i++) { sum += va_arg(valist, int); } /* 清理为 valist 保留的内存 */ va_end(valist); return sum/num; } int main() { printf("Average of 2, 3, 4, 5 = %f\n", average(4, 2,3,4,5)); printf("Average of 5, 10, 15 = %f\n", average(3, 5,10,15)); } Average of 2, 3, 4, 5 = 3.500000 Average of 5, 10, 15 = 10.000000
C 内存管理
本章将讲解 C 中的动态内存管理。C 语言为内存的分配和管理提供了几个函数。这些函数可以在 <stdlib.h> 头文件中找到。 序号 函数和描述 1 void *calloc(int num, int size); 在内存中动态地分配 num 个长度为 size 的连续空间,并将每一个字节都初始化为 0。所以它的结果是分配了 num*size 个字节长度的内存空间,并且每个字节的值都是0。 2 void free(void *address); 该函数释放 address 所指向的内存块,释放的是动态分配的内存空间。 3 void *malloc(int num); 在堆区分配一块指定大小的内存空间,用来存放数据。这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。 4 void *realloc(void *address, int newsize); 该函数重新分配内存,把内存扩展到 newsize。
动态分配内存 编程时,如果您预先知道数组的大小,那么定义数组时就比较容易
char *description; /* 动态分配内存 */ description = (char *)malloc( 200 * sizeof(char) ); if( description == NULL ) { fprintf(stderr, "Error - unable to allocate required memory\n"); } else { strcpy( description, "Zara ali a DPS student in class 10th"); } 上面的程序也可以使用 calloc() 来编写,只需要把 malloc 替换为 calloc 即可,如下所示: calloc(200, sizeof(char));
C 命令行参数
执行程序时,可以从命令行传值给 C 程序。这些值被称为命令行参数,它们对程序很重要,特别是当您想从外部控制程序,而不是在代码内对这些值进行硬编码时,就显得尤为重要了。 命令行参数是使用 main() 函数参数来处理的,其中,argc 是指传入参数的个数,argv[] 是一个指针数组,指向传递给程序的每个参数。下面是一个简单的实例,检查命令行是否有提供参数,并根据参数执行相应的动作:
int main( int argc, char *argv[] ) { if( argc == 2 ) { printf("The argument supplied is %s\n", argv[1]); } else if( argc > 2 ) { printf("Too many arguments supplied.\n"); } else { printf("One argument expected.\n"); } } $./a.out testing The argument supplied is testing 使用两个参数,编译并执行上面的代码,它会产生下列结果: $./a.out testing1 testing2 Too many arguments supplied.
C 排序算法
冒泡排序 冒泡排序(英语:Bubble Sort)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。
#include <stdio.h> void bubble_sort(int arr[], int len) { int i, j, temp; for (i = 0; i < len - 1; i++) for (j = 0; j < len - 1 - i; j++) if (arr[j] > arr[j + 1]) { temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } int main() { int arr[] = { 22, 34, 3, 32, 82, 55, 89, 50, 37, 5, 64, 35, 9, 70 }; int len = (int) sizeof(arr) / sizeof(*arr); bubble_sort(arr, len); int i; for (i = 0; i < len; i++) printf("%d ", arr[i]); return 0; }
选择排序 选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
void swap(int *a,int *b) //交換兩個變數 { int temp = *a; *a = *b; *b = temp; } void selection_sort(int arr[], int len) { int i,j; for (i = 0 ; i < len - 1 ; i++) { int min = i; for (j = i + 1; j < len; j++) //走訪未排序的元素 if (arr[j] < arr[min]) //找到目前最小值 min = j; //紀錄最小值 swap(&arr[min], &arr[i]); //做交換 } }
插入排序 插入排序(英语:Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到 {\displaystyle O(1)} {\displaystyle O(1)}的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后 挪位,为最新元素提供插入空间。
void insertion_sort(int arr[], int len){ int i,j,temp; for (i=1;i<len;i++){ temp = arr[i]; for (j=i;j>0 && arr[j-1]>temp;j--) arr[j] = arr[j-1]; arr[j] = temp; } }
希尔排序 希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。 希尔排序是基于插入排序的以下两点性质而提出改进方法的: 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位
void shell_sort(int arr[], int len) { int gap, i, j; int temp; for (gap = len >> 1; gap > 0; gap = gap >> 1) for (i = gap; i < len; i++) { temp = arr[i]; for (j = i - gap; j >= 0 && arr[j] > temp; j -= gap) arr[j + gap] = arr[j]; arr[j + gap] = temp; } }
归并排序 把数据分为两段,从两段中逐个选最小的元素移入新数据段的末尾。 可从上到下或从下到上进行。