C 备忘清单 提供基本语法和方法的 C 快速参考备忘单。
入门 hello.c 1 2 3 4 5 6 #include <stdio.h> int main () { printf ("Hello World!" ); return 0 ; }
使用 gcc 编译 hello.c 源文件
运行编译后的二进制文件可执行文件(hello)
变量
1 2 3 4 5 6 int myNum = 15 ; int myNum2; myNum2 = 15 ; int myNum3 = 15 ; myNum3 = 10 ;
定义不同类型的变量
1 2 3 4 float myFloatNum = 5.99 ;char myLetter = 'D' ;
变量相加
1 2 int x = 5 , y = 6 ;int sum = x + y;
声明并初始化多个变量
1 2 int x = 5 , y = 6 , z = 50 ;int a, b, c = 10 ;
仅声明变量不初始化
1 2 3 4 5 int result;result = result + 10 ;
常量 Constants
常量是不能被改变的值,使用常量可以使代码更清晰和安全。
1 2 const int minutesPerHour = 60 ;const float PI = 3.14 ;
最佳实践
1 const int BIRTHYEAR = 1980 ;
命名规范
常量通常使用全大写字母,单词间用下划线分隔(如 BIRTHYEAR、MAX_LENGTH)。
数组大小
使用 const 定义数组大小,编译器将其作为编译时常量处理。
#define 与 const
#define:宏常量在预处理阶段替换,不进行类型检查
const:类型安全的常量,编译器可检查类型,推荐使用
注意事项
const 常量在定义时必须初始化,否则会导致编译错误。
const 常量的值不能被修改,任何尝试修改 const 常量的操作都会导致编译错误。
使用 const 常量可以提高代码的可读性和可维护性,避免魔法数字的使用。
注释 1 2 3 4 5 6 7 8 printf ("Hello World!" );
注意 :
单行注释 // 可以嵌套,如 /////。
行内注释应避免过长,以免影响代码可读性。
多行注释不能嵌套,否则会导致编译错误。
打印文本 1 2 3 4 5 6 7 8 9 10 printf ("I am learning C." );int testInteger = 5 ;printf ("Number = %d" , testInteger);float f = 5.99 ; printf ("Value = %f" , f);short a = 0b1010110 ; int b = 02713 ; long c = 0X1DAB83 ;
变量a和c分别为 short 和 long 型,所以输出必须加上对应的修饰符 h 和 l
以 8 进制形式输出 1 2 printf ("a=%ho, b=%o, c=%lo\n" , a, b, c);
以 10 进制形式输出 1 2 printf ("a=%hd, b=%d, c=%ld\n" , a, b, c);
以 16 进制形式输出(字母小写) 1 2 printf ("a=%hx, b=%x, c=%lx\n" , a, b, c);
以 16 进制形式输出(字母大写) 1 2 printf ("a=%hX, b=%X, c=%lX\n" , a, b, c);
控制空格数 1 2 3 4 5 6 7 8 9 10 int a1=20 , a2=345 , a3=700 ;int b1=56720 , b2=9999 , b3=20098 ;int c1=233 , c2=205 , c3=1 ;int d1=34 , d2=0 , d3=23 ;printf ("%-9d %-9d %-9d\n" , a1, a2, a3);printf ("%-9d %-9d %-9d\n" , b1, b2, b3);printf ("%-9d %-9d %-9d\n" , c1, c2, c3);printf ("%-9d %-9d %-9d\n" , d1, d2, d3);
输出结果
1 2 3 4 20 345 700 56720 9999 20098 233 205 1 34 0 23
解释:%-9d:d 表示十进制输出,9 表示最少占 9 个字符宽度,- 表示左对齐,不使用 - 则默认右对齐。
对于整型数据:
1 2 int a = 12345 ;printf ("%md" , a);
若 m <= 实际数据宽度,则按实际情况输出。
若 m > 实际数据宽度,则在左边用空格补齐。
printf("%0md", a); 则在左边用 0 补齐。
对于浮点型数据:
1 2 3 float a = 1.2345 ; printf ("%m.nf" , a);
实际小数位数 > n,截去多余小数,注意四舍五入。
实际小数位数 < n,在小数最后补 0。
若 m 省略则写作 %.n,整数部分按实际输出,小数部分按以上规则。
若 m < n+1,自动突破宽度限制,按实际数据输出。
若 m > n+1,左边补空格。
字符串 (Strings) 在 C 语言中,字符串是以 \0 结尾的字符数组,而不是一种单独的数据类型。可以通过字符数组来表示字符串。
定义并打印字符串 1 2 char greetings[] = "Hello World!" ;printf ("%s" , greetings);
访问字符串中的字符 1 2 char greetings[] = "Hello World!" ;printf ("%c" , greetings[0 ]);
访问字符串 greetings 的第一个字符 H
修改字符串中的字符 1 2 3 4 5 char greetings[] = "Hello World!" ;greetings[0 ] = 'J' ; printf ("%s" , greetings);
另一种创建字符串的方法 1 2 3 4 char greetings[] = {'H' ,'e' ,'l' ,'l' ,'\0' };printf ("%s" , greetings);
注意 :C 语言中没有 String 类型,字符串是由字符数组 char[] 表示的,且必须以 \0 结尾以标识字符串的结束。
条件判断 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 int time = 20 ;if (time < 18 ) { printf ("再会!" ); } else { printf ("晚上好!" ); } int time = 22 ;if (time < 10 ) { printf ("早上好!" ); } else if (time < 20 ) { printf ("再会!" ); } else { printf ("晚上好!" ); } int time = 10 ;if (time > 8 ) { if (time < 12 ) { printf ("中午好!" ) } }
说明
if 语句用于根据条件执行代码块。
else 语句在 if 条件不满足时执行。
else if 语句用于检查多个条件。
可以嵌套 if 语句以检查多个条件。
三元运算符 三元运算符(? :)是一种简洁的条件判断方式,用于根据条件选择表达式的值。由三个部分组成:
基本语法:(条件) ? 表达式1 : 表达式2; 如果 条件 为真,则返回 表达式1,否则返回 表达式2。
示例 1 2 3 4 int time = 20 ;(time < 18 ) ? printf ("再会!" ) : printf ("晚上好!" );
嵌套使用示例(不建议过多嵌套):
1 2 3 4 5 int time = 22 ;printf ((time < 10 ) ? "早上好!" : (time < 20 ) ? "再会!" : "晚上好!" );
Switch 1 2 3 4 5 6 7 8 int day = 4 ;switch (day) { case 3 : printf ("周三" ); break ; case 4 : printf ("周四" ); break ; default : printf ("期待周末" ); }
说明
switch 语句根据表达式的值跳转到匹配的 case 标签。
匹配到 case 后执行相应代码,并通过 break 跳出 switch。
如果没有匹配到任何 case,则执行 default 语句(如果存在)。
注意事项
switch 表达式可以是整型、字符型和枚举型。
case 后的常量表达式值不能相同。
case 后可以有多个语句,不需要 { } 括起来。
case 和 default 语句的顺序不影响程序执行结果。
break 语句用于结束 switch,如果没有 break,程序会继续执行下一个 case。
示例 1 2 3 4 5 6 7 8 int day = 3 ;switch (day) { case 3 : printf ("周三" ); case 4 : printf ("周四" ); break ; default : printf ("期待周末" ); }
While 循环 1 2 3 4 5 6 7 int i = 0 ;while (i < 5 ) { printf ("%d\n" , i); i++; }
解释
while 循环首先检查条件 i < 5 是否为真。
如果为真,程序进入循环体,执行打印操作并增加 i 的值。
循环会继续进行,直到 i 达到 5,此时条件 i < 5 不再为真,循环结束。
打印 1 到 10 的数字 1 2 3 4 5 6 7 int i = 1 ;while (i <= 10 ) { printf ("%d\n" , i); i++; }
打印偶数 1 2 3 4 5 6 7 8 9 int i = 0 ;while (i <= 10 ) { if (i % 2 == 0 ) { printf ("%d\n" , i); } i++; }
无限循环(需手动终止) 1 2 3 4 5 6 7 8 int i = 0 ;while (1 ) { printf ("无限循环\n" ); i++; if (i == 5 ) break ; }
Do/While 循环 1 2 3 4 5 6 7 int i = 0 ;do { printf ("%d\n" , i); i++; } while (i < 5 );
解释
do/while 循环至少执行一次循环体,因为条件检查在循环体执行之后进行。
首先执行 do 中的代码,然后检查条件 i < 5 是否为真。
如果为真,继续执行循环;如果为假,则退出循环。
打印从 5 开始的数字,直到条件不满足 1 2 3 4 5 6 7 int i = 5 ;do { printf ("%d\n" , i); i++; } while (i < 5 );
计算 1 到 10 的和 1 2 3 4 5 6 7 8 9 10 int i = 1 ;int sum = 0 ;do { sum += i; i++; } while (i <= 10 ); printf ("Sum: %d\n" , sum);
For 循环 1 2 3 for (表达式1 ; 表达式2 ; 表达式3 ) { 循环体语句; }
表达式1:设置初始条件,只执行一次。
表达式2:循环条件表达式,每次循环前检查。
表达式3:循环体执行后的调整操作。
示例:
1 2 3 4 5 6 int i;for (i = 0 ; i < 5 ; i++) { printf ("%d\n" , i); }
注意事项
for 语句的三个表达式不是必须的。
当条件表达式(表达式2)为假时,for 循环结束。
可以在循环体内使用 break、continue、goto 语句。
如果表达式2为空,则表示无限循环,如 for(;;) 相当于 while(1)。
变体
for (i = m; i < n; i++) 从 i = m 开始到 i = n-1,循环 n - m 次。
for (i = m; i <= n; i++) 从 i = m 到 i = n,循环 n - m + 1 次。
跳出循环 (Break/Continue/Goto)
break 语句跳出当前循环或 switch 语句,执行后续代码。
1 2 3 4 5 6 7 8 9 int i;for (i = 0 ; i < 10 ; i++) { if (i == 4 ) { break ; } printf ("%d\n" , i); }
continue 语句跳过当前循环的剩余语句,直接进入下一次循环。
1 2 3 4 5 6 7 8 9 int i;for (i = 0 ; i < 10 ; i++) { if (i == 4 ) { continue ; } printf ("%d\n" , i); }
goto 语句无条件跳转到指定标签位置。
1 2 3 4 5 6 7 8 9 10 11 12 13 int i = 0 ;while (i < 10 ) { if (i == 4 ) { goto skip; } printf ("%d\n" , i); i++; } skip: printf ("Exited the loop at i = %d\n" , i);
注意事项
标签必须在当前函数内定义,命名规则与变量相同。
goto 语句应慎用,避免代码逻辑混乱,通常使用循环或条件语句代替。
设置数组大小 1 2 3 4 5 6 7 8 int myNumbers[4 ];myNumbers[0 ] = 25 ; myNumbers[1 ] = 50 ; myNumbers[2 ] = 75 ; myNumbers[3 ] = 100 ;
枚举 Enum
1 enum week { Mon = 1 , Tues, Wed, Thurs, Fri, Sat, Sun };
定义枚举变量
1 2 3 enum week a , b , c ;enum week { Mon = 1 , Tues, Wed, Thurs, Fri, Sat, Sun } a, b, c;
有了枚举变量,就可以把列表中的值赋给它
1 2 3 4 enum week { Mon = 1 , Tues, Wed, Thurs, Fri, Sat, Sun };enum week a = Mon, b = Wed, c = Sat;enum week { Mon = 1 , Tues, Wed, Thurs, Fri, Sat, Sun } a = Mon, b = Wed, c = Sat;
枚举示例应用 1 2 3 4 5 6 7 8 9 10 11 enum week { Mon = 1 , Tues, Wed, Thurs} day;scanf ("%d" , &day);switch (day){ case Mon: puts ("Monday" ); break ; case Tues: puts ("Tuesday" ); break ; case Wed: puts ("Wednesday" ); break ; case Thurs: puts ("Thursday" ); break ; default : puts ("Error!" ); }
数组 Arrays 定义和访问数组元素 1 2 int myNumbers[] = {25 , 50 , 75 , 100 };printf ("%d" , myNumbers[0 ]);
更改数组元素 1 2 3 int myNumbers[] = {25 , 50 , 75 , 100 };myNumbers[0 ] = 33 ; printf ("%d" , myNumbers[0 ]);
循环遍历数组 1 2 3 4 5 6 7 int myNumbers[] = {25 , 50 , 75 , 100 };int i;for (i = 0 ; i < 4 ; i++) { printf ("%d\n" , myNumbers[i]); }
用户输入 1 2 3 4 5 6 7 8 9 10 11 int myNum;printf ("请输入一个数字: \n" );scanf ("%d" , &myNum);printf ("您输入的数字: %d" , myNum);
用户输入字符串 1 2 3 4 5 6 7 8 char firstName[30 ];printf ("输入您的名字: \n" );scanf ("%s" , firstName);printf ("Hello %s." , firstName);
内存地址 创建变量时,会为该变量分配一个内存地址。
1 2 3 int myAge = 43 ;printf ("%p" , &myAge);
要访问变量的内存地址,请使用引用运算符 (&)。
创建指针
1 2 3 4 5 6 7 int myAge = 43 ; int *ptr = &myAge; printf ("%d\n" , myAge); printf ("%p\n" , &myAge); printf ("%p\n" , ptr); printf ("%d\n" , *ptr);
取消引用 1 2 3 4 5 6 7 8 int myAge = 43 ; int * ptr = &myAge; printf ("%p\n" , ptr);printf ("%d\n" , *ptr);
指针变量
1 2 3 4 5 6 int myAge = 43 ; int * ptr = &myAge; printf ("%d\n" , myAge); printf ("%p\n" , &myAge); printf ("%p\n" , ptr);
运算符 算术运算符 1 2 3 4 int myNum = 100 + 50 ;int sum1 = 100 + 50 ; int sum2 = sum1 + 250 ; int sum3 = sum2 + sum2;
Operator
Name
Description
Example
+
加
将两个值相加
x + y
-
减
从另一个值中减去一个值
x - y
*
乘
将两个值相乘
x * y
/
除
将一个值除以另一个
x / y
%
取模
返回除法余数
x % y
++
增量
将变量的值增加 1
++
--
乘量
将变量的值减 1
--x
赋值运算符
符号
示例
如同
=
x = 5
x = 5
+=
x += 3
x = x + 3
-=
x -= 3
x = x - 3
*=
x *= 3
x = x * 3
/=
x /= 3
x = x / 3
%=
x %= 3
x = x % 3
&=
x &= 3
x = x & 3
|=
x |= 3
x = x | 3
^=
x ^= 3
x = x ^ 3
>>=
x >>= 3
x = x >> 3
<<=
x <<= 3
x = x << 3
比较运算符 1 2 3 4 5 int x = 5 ;int y = 3 ;printf ("%d" , x > y);
符号
名称
示例
==
等于
x == y
!=
不等于
x != y
>
大于
x > y
<
小于
x < y
>=
大于或等于
x >= y
<=
小于或等于
x <= y
比较运算符用于比较两个值
逻辑运算符
符号
名称
说明
示例
&&
与逻辑
如果两个语句都为真,则返回真
x < 5 && x < 10
||
或逻辑
如果其中一个语句为真,则返回真
x < 5 || x < 4
!
非逻辑
反转结果,如果结果为真则返回假
!(x < 5 && x < 10)
运算符示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 unsigned int a = 60 ; unsigned int b = 13 ; int c = 0 ; c = a & b; printf ("Line 1 - c 的值是 %d\n" , c );c = a | b; printf ("Line 2 - c 的值是 %d\n" , c );c = a ^ b; printf ("Line 3 - c 的值是 %d\n" , c );c = ~a; printf ("Line 4 - c 的值是 %d\n" , c );c = a << 2 ; printf ("Line 5 - c 的值是 %d\n" , c );c = a >> 2 ; printf ("Line 6 - c 的值是 %d\n" , c );
位运算符
运算符
描述
实例
&
按位与操作,按二进制位进行”与”运算
(A & B) 将得到 12 即为 0000 1100
|
按位或运算符,按二进制位进行”或”运算
(A | B) 将得到 61 即为 0011 1101
^
异或运算符,按二进制位进行”异或”运算
(A ^ B) 将得到 49 即为 0011 0001
~
取反运算符,按二进制位进行”取反”运算
(~A) 将得到 -61 即为 1100 0011
<<
二进制左移运算符
A << 2 将得到 240 即为 1111 0000
>>
二进制右移运算符
A >> 2 将得到 15 即为 0000 1111
数据类型 Data Types 基本数据类型
数据类型
大小 Size
范围 Range
描述 Description
char
1 字节
−128 ~ 127
单个字符/字母/数字/ASCII
signed char
1 字节
−128 ~ 127
-
unsigned char
1 字节
0 ~ 255
-
int
2 到 4 字节
−32,768 ~ 32,767
存储整数
signed int
2 字节
−32,768 ~ 32,767
unsigned int
2 字节
0 ~ 65,535
short int
2 字节
−32,768 ~ 32,767
signed short int
2 字节
−32,768 ~ 32,767
unsigned short int
2 字节
0 ~ 65,535
long int
4 字节
-2,147,483,648 ~ 2,147,483,647
signed long int
4 字节
-2,147,483,648 ~ 2,147,483,647
unsigned long int
4 字节
0 ~ 4,294,967,295
float
4 字节
double
8 字节
long double
10 字节
数据类型 1 2 3 4 5 6 7 8 9 10 11 int myNum = 5 ; float myFloatNum = 5.99 ; char myLetter = 'D' ; double myDouble = 3.2325467 ;printf ("%d\n" , myNum);printf ("%f\n" , myFloatNum);printf ("%c\n" , myLetter);printf ("%lf\n" , myDouble);
数据类型
说 明
char
字符型
short
短整型
int
整型
long
长整型
float
单精度浮点型
double
双精度浮点型
void
无类型
基本格式说明符
格式说明符
数据类型
%d 或 %i
int 整数
%f
float 单精度的十进制类型
%lf
double 高精度浮点数据或数字
%c
char 字符
%s
用于 strings 字符串
基本格式说明符
short
int
long
8 进制
%ho
%o
%lo
10 进制
%hd
%d
%ld
16 进制
%hx / %hX
%x / %X
%lx / %lX
数据格式示例 1 2 3 4 5 6 7 int myNum = 5 ;float myFloatNum = 5.99 ; char myLetter = 'D' ; printf ("%d\n" , myNum);printf ("%f\n" , myFloatNum);printf ("%c\n" , myLetter);
预处理器 预处理器指令
指令
描述
#define
定义宏
#include
包含一个源代码文件
#undef
取消已定义的宏
#ifdef
如果宏已经定义,则返回真
#ifndef
如果宏没有定义,则返回真
#if
如果给定条件为真,则编译下面代码
#else
#if 的替代方案
#elif
如果 #if 条件为假,当前条件为真
#endif
结束一个 #if……#else 条件编译块
#error
当遇到标准错误时,输出错误消息
#pragma
使用标准化方法,向编译器发布特殊的命令到编译器中
1 2 3 4 5 6 7 8 #define MAX_ARRAY_LENGTH 20 #include <stdio.h> #include "myheader.h" #undef FILE_SIZE #define FILE_SIZE 42
预定义宏
宏
描述
__DATE__
当前日期,一个以 “MMM DD YYYY” 格式表示的字符常量
__TIME__
当前时间,一个以 “HH:MM:SS” 格式表示的字符常量
__FILE__
这会包含当前文件名,一个字符串常量
__LINE__
这会包含当前行号,一个十进制常量
__STDC__
当编译器以 ANSI 标准编译时,则定义为 1
ANSI C 定义了许多宏,您可以使用这些宏,但是不能直接修改这些预定义的宏
预定义宏示例 1 2 3 4 5 6 7 8 9 #include <stdio.h> int main () { printf ("File :%s\n" , __FILE__); printf ("Date :%s\n" , __DATE__); printf ("Time :%s\n" , __TIME__); printf ("Line :%d\n" , __LINE__); printf ("ANSI :%d\n" , __STDC__); }
宏延续运算符(\) 一个宏通常写在一个单行上。
1 2 #define message_for(a, b) \ printf(#a " 和 " #b ": 我们爱你!\n" )
如果宏太长,一个单行容纳不下,则使用宏延续运算符 \
字符串常量化运算符(#) 1 2 3 4 5 6 7 8 9 #include <stdio.h> #define message_for(a, b) \ printf(#a " 和 " #b ": 我们爱你!\n" ) int main (void ) { message_for(Carole, Debra); return 0 ; }
当上面的代码被编译和执行时,它会产生下列结果:
需要把一个宏的参数转换为字符串常量时,使用字符串常量化运算符 #
标记粘贴运算符(##) 1 2 3 4 5 6 7 8 9 10 #include <stdio.h> #define tokenpaster(n) \ printf ("token" #n " = %d" , token##n) int main (void ) { int token34 = 40 ; tokenpaster(34 ); return 0 ; }
defined() 运算符 1 2 3 4 5 6 7 8 9 10 11 #include <stdio.h> #if !defined (MESSAGE) #define MESSAGE "You wish!" #endif int main (void ) { printf ("信息如下: %s\n" , \ MESSAGE); return 0 ; }
参数化的宏 1 2 3 int square (int x) { return x * x; }
宏重写上面的代码,如下:
1 #define square(x) ((x) * (x))
宏名称和左圆括号之间不允许有空格
1 2 3 4 5 6 7 8 #include <stdio.h> #define MAX(x,y) ((x) > (y) ? (x) : (y)) int main (void ) { printf ("20 到 10 之间的最大值是 %d\n" , \ MAX(10 , 20 )); return 0 ; }
Warning 和 Error
在 C 语言中,警告(Warning)和错误(Error)是编译器用于标识代码潜在问题或阻止代码编译的机制。
警告 警告提示代码中可能存在的问题,但不会阻止代码编译。处理警告可以提升代码质量和可移植性。
常见警告示例 未使用的变量
类型隐式转换(可能导致数据丢失)
1 2 int x = 3.14 ; int a = 2147483647 + 1 ;
函数声明与定义不匹配
错误 错误会阻止代码编译,必须修复才能继续编译。
常见错误示例 :
语法错误(如缺少分号)
函数定义冲突
1 2 void func (int ) ;void func (double ) ;
函数或变量未定义
1 y = 10 ; printf ("%d" , y);
头文件缺失或冲突
使用编译器指令控制警告和错误
抑制警告 可以使用编译器选项来关闭特定的警告,例如在 GCC 中:
1 2 3 gcc -w file.c gcc -Wall file.c gcc -Werror file.c
使用 #pragma 控制警告 在某些编译器中,可以使用 #pragma 指令启用或禁用警告:
1 2 3 4 5 6 7 #include <stdio.h> #pragma warning (disable : 4996) int main () { printf ("Hello, world!" ); return 0 ; }
总结
区别点
Warning(警告)
Error(错误)
严重程度
程序可继续编译,但可能存在隐患
编译无法完成,必须修复
编译结果
生成可执行文件
无法生成可执行文件
触发原因
潜在问题,例如隐式转换或未使用的变量
语法或语义错误,例如语法错误或未定义变量
修复必要性
可选择修复,但建议修复以避免潜在问题
必须修复才能继续编译
编译器选项调整
可以忽略或转换为错误(如 -Werror)
无法调整,必须修复
函数 函数声明和定义
1 2 3 4 int main () { printf ("Hello World!" ); return 0 ; }
函数由两部分组成
Declaration 声明函数名称、返回类型和参数 (如果有)
Definition 函数体 (要执行的代码)
1 2 3 4 5 6 7 8 9 10 void myFunction () ;int main () { myFunction(); return 0 ; } void myFunction () { printf ("晚上好!" ); }
调用函数 1 2 3 4 5 6 7 8 9 10 11 void myFunction () { printf ("晚上好!" ); } int main () { myFunction(); myFunction(); return 0 ; }
函数参数 1 2 3 4 5 6 7 8 9 10 void myFunction (char name[]) { printf ("Hello %s\n" , name); } int main () { myFunction("Liam" ); myFunction("Jenny" ); return 0 ; }
多个参数 1 2 3 4 5 6 7 8 9 10 void myFunction (char name[], int age) { printf ("你好 %s 你 %d 岁了。\n" ,name,age); } int main () { myFunction("Liam" , 3 ); myFunction("Jenny" , 14 ); return 0 ; }
返回值
1 2 3 4 5 6 7 8 9 int myFunction (int x) { return 5 + x; } int main () { printf ("结果: %d" , myFunction(3 )); return 0 ; }
两个参数
1 2 3 4 5 6 7 8 9 10 11 12 13 int myFunction (int x, int y) { return x + y; } int main () { printf ("结果: %d" , myFunction(5 , 3 )); int result = myFunction(5 , 3 ); printf ("结果 = %d" , result); return 0 ; }
递归示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 int sum (int k) ;int main () { int result = sum(10 ); printf ("%d" , result); return 0 ; } int sum (int k) { if (k > 0 ) { return k + sum(k - 1 ); } else { return 0 ; } }
数学函数 1 2 3 4 5 #include <math.h> printf ("%f" , sqrt (16 )); printf ("%f" , ceil (1.4 )); printf ("%f" , floor (1.4 )); printf ("%f" , pow (4 , 3 ));
abs(x) 绝对值
acos(x) 反余弦值
asin(x) 反正弦值
atan(x) 反正切
cbrt(x) 立方根
cos(x) 余弦
exp(x) Ex 的值
sin(x) x 的正弦值
tan(x) 角度的正切
Structures 结构 创建结构 1 2 3 4 struct MyStructure { int myNum; char myLetter; };
创建一个名为 s1 的结构变量
{7} 1 2 3 4 5 6 7 8 9 struct myStructure { int myNum; char myLetter; }; int main () { struct myStructure s1 ; return 0 ; }
结构中的字符串 {9} 1 2 3 4 5 6 7 8 9 10 11 12 13 struct myStructure { int myNum; char myLetter; char myString[30 ]; }; int main () { struct myStructure s1 ; strcpy (s1.myString, "Some text" ); printf ("我字符串: %s" , s1.myString); return 0 ; }
使用 strcpy 函数为字符串赋值
访问结构成员
{11,12,16} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 struct myStructure { int myNum; char myLetter; }; int main () { struct myStructure s1 ; s1.myNum = 13 ; s1.myLetter = 'B' ; struct myStructure s2 = {13 , 'B' }; printf ("My number: %d\n" , s1.myNum); printf ("My letter: %c\n" , s1.myLetter); return 0 ; }
创建不同的结构变量
1 2 3 4 5 6 7 8 struct myStructure s1 ;struct myStructure s2 ;s1.myNum = 13 ; s1.myLetter = 'B' ; s2.myNum = 20 ; s2.myLetter = 'C' ;
复制结构 {6} 1 2 3 4 5 6 struct myStructure s1 = { 13 , 'B' , "Some text" }; struct myStructure s2 ;s2 = s1;
示例中,将 s1 的值复制到 s2
修改值 {6,7} 1 2 3 4 5 6 7 8 9 10 11 struct myStructure s1 = { 13 , 'B' }; s1.myNum = 30 ; s1.myLetter = 'C' ; printf ("%d %c %s" , s1.myNum, s1.myLetter);
文件处理 文件处理函数
函数
描述 Description
fopen()
打开新文件或现有文件
fprintf()
将数据写入文件
fscanf()
从文件中读取数据
fputc()
将一个字符写入文件
fgetc()
从文件中读取一个字符
fclose()
关闭文件
fseek()
将文件指针设置到给定位置
fputw()
将整数写入文件
fgetw()
从文件中读取一个整数
ftell()
返回当前位置
rewind()
将文件指针设置为文件的开头
C 库中有许多函数可以打开/读取/写入/搜索和关闭文件
打开模式参数
模式 Mode
描述 Description
r
以读取模式打开一个文本文件,允许读取文件
w
以写模式打开一个文本文件,允许写入文件
a
以追加模式打开一个文本文件 如果文件不存在,则会创建一个新文件
r+
以读写模式打开一个文本文件,允许读写文件
w+
以读写模式打开一个文本文件,允许读写文件
a+
以读写模式打开一个文本文件,允许读写文件
rb
以读取模式打开二进制文件
wb
以写入模式打开二进制文件
ab
以追加模式打开二进制文件
rb+
以读写模式打开二进制文件
wb+
以读写模式打开二进制文件
ab+
以读写模式打开二进制文件
打开文件:fopen() {6} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <stdio.h> void main ( ) { FILE *fp; char ch; fp = fopen("file_handle.c" , "r" ); while (1 ) { ch = fgetc(fp); if (ch == EOF) break ; printf ("%c" , ch); } fclose(fp); }
对文件执行所有操作后,必须关闭 fclose() 该文件
写入文件:fprintf() {7} 1 2 3 4 5 6 7 8 9 #include <stdio.h> main() { FILE *fp; fp = fopen("file.txt" , "w" ); fprintf (fp, "fprintf 的 Hello 文件..\n" ); fclose(fp); }
读取文件:fscanf() {6} 1 2 3 4 5 6 7 8 9 10 #include <stdio.h> main(){ FILE *fp; char buff[255 ]; fp = fopen("file.txt" , "r" ); while (fscanf (fp, "%s" , buff)!=EOF) { printf ("%s " , buff); } fclose(fp); }
写入文件:fputc() {6} 1 2 3 4 5 6 7 8 #include <stdio.h> main(){ FILE *fp; fp = fopen("file1.txt" , "w" ); fputc('a' ,fp); fclose(fp); }
读取文件:fgetc() {8} 1 2 3 4 5 6 7 8 9 10 11 12 13 #include <stdio.h> #include <conio.h> void main () { FILE *fp; char c; clrscr(); fp=fopen("myfile.txt" , "r" ); while ((c=fgetc(fp))!=EOF){ printf ("%c" , c); } fclose(fp); getch(); }
写入文件:fputs() {8} 1 2 3 4 5 6 7 8 9 10 11 #include <stdio.h> #include <conio.h> void main () { FILE *fp; clrscr(); fp = fopen("myfile2.txt" ,"w" ); fputs ("hello c programming" ,fp); fclose(fp); getch(); }
读取文件:fgets() {10} 1 2 3 4 5 6 7 8 9 10 11 12 13 #include <stdio.h> #include <conio.h> void main () { FILE *fp; char text[300 ]; clrscr(); fp=fopen("myfile2.txt" , "r" ); printf ("%s" , fgets(text, 200 , fp)); fclose(fp); getch(); }
fseek() {8} 1 2 3 4 5 6 7 8 9 10 11 #include <stdio.h> void main () { FILE *fp; fp = fopen("myfile.txt" ,"w+" ); fputs ("This is Book" , fp); fseek(fp, 7 , SEEK_SET); fputs ("Kenny Wong" , fp); fclose(fp); }
将文件指针设置到给定位置
rewind() {11} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdio.h> #include <conio.h> void main () { FILE *fp; char c; clrscr(); fp=fopen("file.txt" , "r" ); while ((c=fgetc(fp)) != EOF){ printf ("%c" , c); } rewind(fp); while ((c=fgetc(fp)) != EOF){ printf ("%c" , c); } fclose(fp); getch(); }
ftell() {11} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <stdio.h> #include <conio.h> void main () { FILE *fp; int length; clrscr(); fp = fopen("file.txt" , "r" ); fseek(fp, 0 , SEEK_END); length = ftell(fp); fclose(fp); printf ("文件大小: %d bytes" , length); getch(); }
C 网络编程 网络编程介绍 C使用sockets进行网络通信。包含头文件:
#include <sys/socket.h>: 套接字操作,如创建、绑定和监听套接字
#include <arpa/inet.h>: IP 地址转换
#include <unistd.h>: 关闭套接字等
#include <netinet/in.h>: 网络地址结构定义和相关敞亮
创建套接字 网络通信的第一步是创建套接字。套接字是网络通信的基础,通过它可以与远程主机进行数据交换。
服务端 1 2 3 4 5 6 7 8 9 int server_fd, new_socket; int port = 8080 ; if ((server_fd = socket (AF_INET, SOCK_STREAM, 0 )) == 0 ) { perror ("socket failed" ); exit (EXIT_FAILURE); }
客户端 1 2 3 4 5 6 7 8 int sock = 0 ; struct sockaddr_in serv_addr; if ((sock = socket (AF_INET, SOCK_STREAM, 0 )) < 0 ) { perror ("Socket creation failed" ); exit (EXIT_FAILURE); }
绑定套接字 服务端创建套接字后,需要将其绑定到特定的 IP 地址和端口,以便客户端能够连接。
服务端 1 2 3 4 5 6 7 8 9 10 11 struct sockaddr_in address; address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons (port); if (bind (server_fd, (struct sockaddr *)&address, sizeof (address)) < 0 ) { perror ("bind failed" ); exit (EXIT_FAILURE); }
监听和接收连接 服务端在绑定套接字之后,需要进入监听状态,以等待客户端的连接请求。
服务端 1 2 3 4 5 6 7 8 9 10 11 12 13 14 if (listen (server_fd, 3 ) < 0 ) { perror ("listen failed" ); exit (EXIT_FAILURE); } int addrlen = sizeof (address); if ((new_socket = accept (server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0 ) { perror ("accept failed" ); exit (EXIT_FAILURE); }
连接到服务端 客户端使用 connect() 函数连接到服务器的 IP 地址和端口。
客户端 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons (port); if (inet_pton (AF_INET, "127.0.0.1" , &serv_addr.sin_addr) <= 0 ) { perror ("Invalid address/ Address not supported" ); exit (EXIT_FAILURE); } if (connect (sock, (struct sockaddr *)&serv_addr, sizeof (serv_addr)) < 0 ) { perror ("Connection Failed" ); exit (EXIT_FAILURE); }
发送和接收数据 一旦连接建立,服务端和客户端可以通过套接字发送和接收数据。
服务端 1 2 3 4 5 6 7 8 9 char buffer[1024 ] = {0 }; int valread = read (new_socket, buffer, 1024 ); printf ("Client: %s\n" , buffer); const char *response = "Hello from server" ; send (new_socket, response, strlen (response), 0 ); printf ("Server message sent\n" );
客户端 1 2 3 4 5 6 7 8 9 const char *message = "Hello from client" ; send (sock, message, strlen (message), 0 ); printf ("Client message sent\n" );char buffer[1024 ] = {0 }; int valread = read (sock, buffer, 1024 ); printf ("Server: %s\n" , buffer);
关闭套接字 完成通信后,双方都应关闭各自的套接字以释放资源。
服务端 1 2 3 4 close (new_socket); close (server_fd);
客户端
I/O多路复用 多路复用介绍 在网络编程中,服务端可以使用 I/O 多路复用 技术,如 select、poll 或 epoll。这些技术允许服务端同时监听多个文件描述符(如套接字),并在其中一个发生事件时进行处理,提升系统效率。包含头文件:
#include <sys/select.h>: 提供 select
#include <poll.h>: 提供 poll
#include <sys/epoll.h>: 提供epoll
使用select 1 2 3 4 5 6 7 8 9 10 fd_set read_fds; FD_ZERO(&read_fds); FD_SET(server_socket, &read_fds); int max_fd = server_socket;int activity = select(max_fd + 1 , &read_fds, NULL , NULL , NULL ); if (activity < 0 && errno != EINTR) { perror("select error" ); }
使用poll 1 2 3 4 5 6 7 8 9 struct pollfd fds [2]; fds[0 ].fd = server_socket; fds[0 ].events = POLLIN; int poll_count = poll(fds, 2 , -1 ); if (poll_count < 0 ) { perror("poll error" ); }
使用epoll 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int epoll_fd = epoll_create1(0 ); struct epoll_event event ;event.events = EPOLLIN; event.data.fd = server_socket; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_socket, &event) == -1 ) { perror("epoll_ctl failed" ); } struct epoll_event events [10]; int event_count = epoll_wait(epoll_fd, events, 10 , -1 ); for (int i = 0 ; i < event_count; i++) { if (events[i].data.fd == server_socket) { } }
杂项 Docker 运行环境
另见
C 教程 (jaywcjlove.github.io)