提供基本语法和方法的 C 快速参考备忘单。
#include <stdio.h>
int main() {
printf("Hello World!");
return 0;
}
使用 gcc
编译 hello.c
源文件
$ gcc -o hello hello.c
运行编译后的二进制文件可执行文件(hello
)
$ ./hello
# 输出 => Hello World
int myNum = 15;
int myNum2; // 声明变量 myNum2
// 变量声明后第一次赋值我们称为初始化
// 如果 初始化 和 赋值 在同一行
// 那么我们可以直接称为 定义变量 myNum2
myNum2 = 15;
int myNum3 = 15; // myNum3 值为 15
myNum3 = 10; // 现在 myNum3 值为 10
float myFloatNum = 5.99; // 浮点数
char myLetter = 'D'; // 字符
int x = 5;
int y = 6;
int sum = x + y; // 添加变量相加
// 声明多个变量
int x = 5, y = 6, z = 50;
常量在 C 语言中我们一般理解为不能被改变的值,活用常量与符号常量
const int minutesPerHour = 60;
const float PI = 3.14;
最佳实践
const int BIRTHYEAR = 1980;
// 这是一个注释
printf("Hello World!"); // 这是一个注释
/* 多行注释,上面的代码将打印出 Hello World!
到屏幕上,真是太棒了 */
printf("I am learning C.");
int testInteger = 5;
printf("Number = %d", testInteger);
float f = 5.99; // 浮点数
printf("Value = %f", f);
short a = 0b1010110; // 2 进制数字
int b = 02713; // 8 进制数字
long c = 0X1DAB83; // 16 进制数字
// 以 8 进制形似输出
printf("a=%ho, b=%o, c=%lo\n", a, b, c);
// 输出 => a=126, b=2713, c=7325603
// 以 10 进制形式输出
printf("a=%hd, b=%d, c=%ld\n", a, b, c);
// 输出 => a=86, b=1483, c=1944451
// 以 16 进制形式输出(字母小写)
printf("a=%hx, b=%x, c=%lx\n", a, b, c);
// 输出 => a=56, b=5cb, c=1dab83
// 以 16 进制形式输出(字母大写)
printf("a=%hX, b=%X, c=%lX\n", a, b, c);
// 输出 => a=56, b=5CB, c=1DAB83
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);
输出结果
20 345 700
56720 9999 20098
233 205 1
34 0 23
%-9d
中,d
表示以 10
进制输出,9
表示最少占 9
个字符的宽度,宽度不足以空格补齐,-
表示左对齐
char greetings[] = "Hello World!";
printf("%s", greetings);
访问字符串
char greetings[] = "Hello World!";
printf("%c", greetings[0]);
修改字符串
char greetings[] = "Hello World!";
greetings[0] = 'J';
printf("%s", greetings);
// 输出 "Jello World!"
另一种创建字符串的方法
char greetings[] = {'H','e','l','l','\0'};
printf("%s", greetings);
// 输出 "Hell!"
C
没有 String 类型,使用 char
类型并创建一个字符 array
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 = 20;
(time < 18) ? printf("再会!") : printf("晚上好!");
int day = 4;
switch (day) {
case 3: printf("周三"); break;
case 4: printf("周四"); break;
default:
printf("期待周末");
}
// 输出 -> "周四" (day 4)
int i = 0;
while (i < 5) {
printf("%d\n", i);
i++;
}
注意:不要忘记增加条件中使用的变量,否则循环永远不会结束,成为“死循环”!
int i = 0;
do {
printf("%d\n", i);
i++;
} while (i < 5);
int i;
for (i = 0; i < 5; i++) {
printf("%d\n", i);
}
int i;
for (i = 0; i < 10; i++) {
if (i == 4) {
break;
}
printf("%d\n", i);
}
在 i
等于 4
时跳出循环
int i;
for (i = 0; i < 10; i++) {
if (i == 4) {
continue;
}
printf("%d\n", i);
}
示例跳过 4
的值
int i = 0;
while (i < 10) {
if (i == 4) {
break;
}
printf("%d\n", i);
i++;
}
int i = 0;
while (i < 10) {
i++;
if (i == 4) {
continue;
}
printf("%d\n", i);
}
int myNumbers[] = {25, 50, 75, 100};
printf("%d", myNumbers[0]);
// 输出 25
更改数组元素
int myNumbers[] = {25, 50, 75, 100};
myNumbers[0] = 33;
printf("%d", myNumbers[0]);
循环遍历数组
int myNumbers[] = {25, 50, 75, 100};
int i;
for (i = 0; i < 4; i++) {
printf("%d\n", myNumbers[i]);
}
设置数组大小
// 声明一个由四个整数组成的数组:
int myNumbers[4];
// 添加元素
myNumbers[0] = 25;
myNumbers[1] = 50;
myNumbers[2] = 75;
myNumbers[3] = 100;
enum week { Mon = 1, Tues, Wed, Thurs, Fri, Sat, Sun };
定义枚举变量
enum week a, b, c;
enum week { Mon = 1, Tues, Wed, Thurs, Fri, Sat, Sun } a, b, c;
有了枚举变量,就可以把列表中的值赋给它
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;
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!");
}
// 创建一个整数变量来存储我们从用户那里得到的数字
int myNum;
// 要求用户输入一个数字
printf("请输入一个数字: \n");
// 获取并保存用户输入的号码
scanf("%d", &myNum);
// 输出用户输入的数字
printf("您输入的数字: %d", myNum);
// 创建一个字符串
char firstName[30];
// 要求用户输入一些文本
printf("输入您的名字: \n");
// 获取并保存文本
scanf("%s", firstName);
// 输出文本
printf("Hello %s.", firstName);
创建变量时,会为该变量分配一个内存地址
int myAge = 43;
printf("%p", &myAge);
// 输出:0x7ffe5367e044
要访问它,请使用引用运算符 (&
)
int myAge = 43; // 一个 int 变量
printf("%d", myAge); // 输出 myAge(43)的值
// 输出 myAge 的内存地址(0x7ffe5367e044)
printf("%p", &myAge);
int myAge = 43; // 一个 int 变量
int* ptr = &myAge; // 名为 ptr 的指针变量,用于存储 myAge 的地址
printf("%d\n", myAge); // 输出 myAge (43) 的值
printf("%p\n", &myAge); // 输出 myAge 的内存地址(0x7ffe5367e044)
printf("%p\n", ptr); // 用指针(0x7ffe5367e044)输出myAge的内存地址
int myAge = 43; // 变量声明
int* ptr = &myAge; // 指针声明
// 参考:用指针输出 myAge 的
// 内存地址(0x7ffe5367e044)
printf("%p\n", ptr);
// 取消引用:用指针输出 myAge 的值 (43)
printf("%d\n", *ptr);
int myNum = 100 + 50;
int sum1 = 100 + 50; // 150 (100 + 50)
int sum2 = sum1 + 250; // 400 (150 + 250)
int sum3 = sum2 + sum2; // 800 (400 + 400)
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 |
int x = 5;
int y = 3;
printf("%d", x > y);
// 返回 1(真),因为 5 大于 3
符号 | 名称 | 示例 |
---|---|---|
== | 等于 | x == y |
!= | 不等于 | x != y |
> | 大于 | x > y |
< | 小于 | x < y |
>= | 大于或等于 | x >= y |
<= | 小于或等于 | x <= y |
比较运算符用于比较两个值
符号 | 名称 | 说明 | 示例 |
---|---|---|---|
&& | 与 逻辑 | 如果两个语句都为真,则返回真 | x < 5 && x < 10 |
|| | 或 逻辑 | 如果其中一个语句为真,则返回真 | x < 5 || x < 4 |
! | 非 逻辑 | 反转结果,如果结果为真则返回假 | !(x < 5 && x < 10) |
unsigned int a = 60; /* 60 = 0011 1100 */
unsigned int b = 13; /* 13 = 0000 1101 */
int c = 0;
c = a & b; /* 12 = 0000 1100 */
printf("Line 1 - c 的值是 %d\n", c );
c = a | b; /* 61 = 0011 1101 */
printf("Line 2 - c 的值是 %d\n", c );
c = a ^ b; /* 49 = 0011 0001 */
printf("Line 3 - c 的值是 %d\n", c );
c = ~a; /*-61 = 1100 0011 */
printf("Line 4 - c 的值是 %d\n", c );
c = a << 2; /* 240 = 1111 0000 */
printf("Line 5 - c 的值是 %d\n", c );
c = a >> 2; /* 15 = 0000 1111 */
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 |
数据类型 | 大小 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 字节 |
// 创建变量
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 |
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 | 使用标准化方法,向编译器发布特殊的命令到编译器中 |
// 所有的 MAX_ARRAY_LENGTH 替换为 20
#define MAX_ARRAY_LENGTH 20
// 系统库中获取 stdio.h
#include <stdio.h>
// 本地目录中获取 myheader.h
#include "myheader.h"
#undef FILE_SIZE
#define FILE_SIZE 42 // 取消已定义并定义为 42
宏 | 描述 |
---|---|
__DATE__ | 当前日期,一个以 "MMM DD YYYY" 格式表示的字符常量 |
__TIME__ | 当前时间,一个以 "HH:MM:SS" 格式表示的字符常量 |
__FILE__ | 这会包含当前文件名,一个字符串常量 |
__LINE__ | 这会包含当前行号,一个十进制常量 |
__STDC__ | 当编译器以 ANSI 标准编译时,则定义为 1 |
ANSI C
定义了许多宏,您可以使用这些宏,但是不能直接修改这些预定义的宏
#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__);
}
一个宏通常写在一个单行上。
#define message_for(a, b) \
printf(#a " 和 " #b ": 我们爱你!\n")
如果宏太长,一个单行容纳不下,则使用宏延续运算符 \
#include <stdio.h>
#define message_for(a, b) \
printf(#a " 和 " #b ": 我们爱你!\n")
int main(void) {
message_for(Carole, Debra);
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
Carole 和 Debra: 我们爱你!
需要把一个宏的参数转换为字符串常量时,使用字符串常量化运算符 #
#include <stdio.h>
#define tokenpaster(n) \
printf ("token" #n " = %d", token##n)
int main(void){
int token34 = 40;
tokenpaster(34);
return 0;
}
#include <stdio.h>
#if !defined (MESSAGE)
#define MESSAGE "You wish!"
#endif
int main(void) {
printf("信息如下: %s\n", \
MESSAGE);
return 0;
}
int square(int x) {
return x * x;
}
宏重写上面的代码,如下:
#define square(x) ((x) * (x))
宏名称和左圆括号之间不允许有空格
#include <stdio.h>
#define MAX(x,y) ((x) > (y) ? (x) : (y))
int main(void) {
printf("20 到 10 之间的最大值是 %d\n", \
MAX(10, 20));
return 0;
}
int main() {
printf("Hello World!");
return 0;
}
函数由两部分组成
void myFunction() { // 声明 declaration
// 函数体(要执行的代码)(definition)
}
Declaration
声明函数名称、返回类型和参数 (如果有)Definition
函数体 (要执行的代码)// 函数声明
void myFunction();
// 主要方法
int main() {
myFunction(); // --> 调用函数
return 0;
}
void myFunction() {// 函数定义
printf("晚上好!");
}
// 创建函数
void myFunction() {
printf("晚上好!");
}
int main() {
myFunction(); // 调用函数
myFunction(); // 可以被多次调用
return 0;
}
// 输出 -> "晚上好!"
// 输出 -> "晚上好!"
void myFunction(char name[]) {
printf("Hello %s\n", name);
}
int main() {
myFunction("Liam");
myFunction("Jenny");
return 0;
}
// Hello Liam
// Hello Jenny
void myFunction(char name[], int age) {
printf("你好 %s 你 %d 岁了。\n",name,age);
}
int main() {
myFunction("Liam", 3);
myFunction("Jenny", 14);
return 0;
}
// 你好 Liam 你 3 岁了。
// 你好 Jenny 你 14 岁了。
int myFunction(int x) {
return 5 + x;
}
int main() {
printf("结果: %d", myFunction(3));
return 0;
}
// 输出 8 (5 + 3)
两个参数
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;
}
// 结果: 8 (5 + 3)
// 结果 = 8 (5 + 3)
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;
}
}
#include <math.h>
printf("%f", sqrt(16)); // 平方根
printf("%f", ceil(1.4)); // 四舍五入 (入)
printf("%f", floor(1.4)); // 四舍五入 (舍)
printf("%f", pow(4, 3)); // x(4)的y(3)次方
abs(x)
绝对值acos(x)
反余弦值asin(x)
反正弦值atan(x)
反正切cbrt(x)
立方根cos(x)
余弦exp(x)
Ex 的值sin(x)
x 的正弦值tan(x)
角度的正切struct MyStructure { // 结构声明
int myNum; // 成员(int 变量)
char myLetter; // 成员(char 变量)
}; // 用分号结束结构
创建一个名为 s1
的结构变量
struct myStructure {
int myNum;
char myLetter;
};
int main() {
struct myStructure s1;
return 0;
}
struct myStructure {
int myNum;
char myLetter;
char myString[30]; // String
};
int main() {
struct myStructure s1;
strcpy(s1.myString, "Some text");
// 打印值
printf("我字符串: %s", s1.myString);
return 0;
}
使用 strcpy
函数为字符串赋值
// 创建一个名为 myStructure 的结构
struct myStructure {
int myNum;
char myLetter;
};
int main() {
// 创建一个名为 s1 的 myStructure 结构变量
struct myStructure s1;
// 为 s1 的成员赋值
s1.myNum = 13;
s1.myLetter = 'B';
// 创建一个名为 s2 的 myStructure 结构变量
// 并为其赋值
struct myStructure s2 = {13, 'B'};
// 打印值
printf("My number: %d\n", s1.myNum);
printf("My letter: %c\n", s1.myLetter);
return 0;
}
创建不同的结构变量
struct myStructure s1;
struct myStructure s2;
// 为不同的结构变量赋值
s1.myNum = 13;
s1.myLetter = 'B';
s2.myNum = 20;
s2.myLetter = 'C';
struct myStructure s1 = {
13, 'B', "Some text"
};
struct myStructure s2;
s2 = s1;
示例中,将 s1
的值复制到 s2
// 创建一个结构变量并为其赋值
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+ | 以读写 模式打开二进制文件 |
#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()
该文件
#include <stdio.h>
main() {
FILE *fp;
fp = fopen("file.txt", "w"); // 打开文件
// 将数据写入文件
fprintf(fp, "fprintf 的 Hello 文件..\n");
fclose(fp); // 关闭文件
}
#include <stdio.h>
main(){
FILE *fp;
char buff[255]; // 创建char数组存储文件数据
fp = fopen("file.txt", "r");
while(fscanf(fp, "%s", buff)!=EOF) {
printf("%s ", buff);
}
fclose(fp);
}
#include <stdio.h>
main(){
FILE *fp;
fp = fopen("file1.txt", "w"); // 打开文件
fputc('a',fp); // 将单个字符写入文件
fclose(fp); // 关闭文件
}
#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();
}
#include<stdio.h>
#include<conio.h>
void main(){
FILE *fp;
clrscr();
fp = fopen("myfile2.txt","w");
fputs("hello c programming",fp);
fclose(fp);
getch();
}
#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();
}
#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);
}
将文件指针设置到给定位置
#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();
}
// 输出
// Hello World!Hello World!
#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();
}
// 输出
// 文件大小: 18 bytes
C使用sockets进行网络通信。包含头文件:
#include <sys/socket.h>
: 套接字操作,如创建、绑定和监听套接字#include <arpa/inet.h>
: IP 地址转换#include <unistd.h>
: 关闭套接字等#include <netinet/in.h>
: 网络地址结构定义和相关敞亮网络通信的第一步是创建套接字。套接字是网络通信的基础,通过它可以与远程主机进行数据交换。
int server_fd, new_socket; // 定义服务器文件描述符和新连接的套接字
int port = 8080; // 服务器使用的端口号
// 创建套接字文件描述符
// AF_INET 表示使用 IPv4 协议,SOCK_STREAM 表示使用 TCP 协议,协议参数通常为 0(默认 TCP)
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
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 地址和端口,以便客户端能够连接。
struct sockaddr_in address; // 定义存储地址信息的结构体
address.sin_family = AF_INET; // 设置地址族为 IPv4
address.sin_addr.s_addr = INADDR_ANY; // 将服务器绑定到所有可用的网络接口(即本机的所有 IP 地址)
address.sin_port = htons(port); // 将端口号转换为网络字节序,大端模式
// 将套接字绑定到指定的地址和端口上
// bind() 将服务器的文件描述符与 IP 地址和端口号进行绑定,以便客户端能够通过该地址和端口访问服务器
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
服务端在绑定套接字之后,需要进入监听状态,以等待客户端的连接请求。
// 开始监听客户端连接
// 监听连接请求
// listen() 函数将套接字设置为被动模式,准备接收来自客户端的连接请求
if (listen(server_fd, 3) < 0) { // 第二个参数 3 表示连接请求的队列大小
perror("listen failed");
exit(EXIT_FAILURE);
}
int addrlen = sizeof(address); // 获取地址结构体的大小
// accept() 函数会阻塞等待客户端的连接请求,一旦连接请求到来,创建一个新的套接字 new_socket 用于数据传输
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept failed");
exit(EXIT_FAILURE);
}
客户端使用 connect()
函数连接到服务器的 IP 地址和端口。
// 设置服务器地址
serv_addr.sin_family = AF_INET; // 设置地址族为 IPv4
serv_addr.sin_port = htons(port); // 将端口号转换为网络字节序
// 将 IP 地址转换为二进制并存储在 serv_addr 结构体中
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
perror("Invalid address/ Address not supported");
exit(EXIT_FAILURE);
}
// 连接服务器
// connect() 函数将客户端的套接字与服务器的地址绑定,从而建立连接
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("Connection Failed");
exit(EXIT_FAILURE);
}
一旦连接建立,服务端和客户端可以通过套接字发送和接收数据。
// 服务端从客户端接收数据
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");
// 客户端发送数据给服务端
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); // 打印接收到的服务端数据
完成通信后,双方都应关闭各自的套接字以释放资源。
// 关闭服务端套接字
close(new_socket); // 关闭用于数据传输的客户端套接字
close(server_fd); // 关闭服务器的监听套接字
// 关闭客户端套接字
close(sock); // 关闭客户端的套接字
在网络编程中,服务端可以使用 I/O 多路复用 技术,如 select
、poll
或 epoll
。这些技术允许服务端同时监听多个文件描述符(如套接字),并在其中一个发生事件时进行处理,提升系统效率。包含头文件:
#include <sys/select.h>
: 提供 select
#include <poll.h>
: 提供 poll
#include <sys/epoll.h>
: 提供epoll
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");
}
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");
}
int epoll_fd = epoll_create1(0); // 创建 epoll 文件描述符
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
创建 Dockerfile
文件
FROM alpine:3.14
RUN apk add --no-cache gcc musl-dev
RUN apk add --no-cache g++
生成本地 myalpine 镜像
docker build -t myalpine .
运行映像,把当前路径 ($PWD)
映射至容器的 /test
目录,用 gcc
编译程序,exit
返回
docker run -it -v $PWD:/test myalpine
root@b1a38bd7107a:/# cd test
root@b1a38bd7107a:/test# gcc -o hello hello.c
Hello World
root@b1a38bd7107a:/test# exit
exit