一、选择控制语句
1、if语句
if(条件表达式) //条件成立,执行下方内容,不成立就跳过
{
执行内容;
}
if(条件表达式)
{
执行内容;
}
//条件成立,执行if内的内容,不成立,跳过if执行else内的内容
else
{
执行内容;
}
//多个条件判断用else if,最后以else收尾
if(条件表达式)
{
执行内容;
}
else if(条件表达式)
{
执行内容;
}
else if(条件表达式)
{
执行内容;
}
.
.
.
else
{
执行内容;
}
2、switch语句
switch(表达式) //表达式只能是字符型或者整形
{
case 常量表达式1:
语句1;
break; //跳出switch
case 常量表达式2:
语句2;
break;
default:
语句3;
break;
}
//case后的常量表达式与switch后的表达式进行比较
//如果相等则执行下方语句,执行完break跳出
//以上条件都不符合执行defaul内的语句然后跳出
//在实际应用中根据需要调整,也可以直接跳出
3、for循环
for(表达式1;表达式2;表达式3)
{
"循环体";
}
//表达式1整个循环过程只执行一次
//表达式2为循环条件,每做一次循环就回头和2比较,为真继续循环,为假跳出循环
//表达式3在表达式2为真后执行
例:
int i=0;
for(i=1;i<=10;i++)
{
"循环体";
}
//过程:
//(1)i初始值为1;
//(2)i<=10成立;
//(3)执行i++,此时i=2;
//(4)执行循环体
//(5)i<=10成立;
//(6)执行i++,此时i=3;
// .
// .
// .
//当i<=10为假时跳出循环
4、while循环
while(条件表达式)
{
"循环体";
}
//执行while循环先判断while后的表达式是否为为真
//与for循环的区别就是for循环先执行再判断
//while循环是先判断再执行
do
{
"循环体";
}while(条件表达式);
//do while循环先执行do后的循环体,在进行判断
5、goto语句
goto是跳转语句,只能在同一函数内部跳转,无法跨函数跳转
下面是我在学习过程中遇到的问题(红色字体)
当 goto
语句向后跳转时,在 goto
到的板块执行完后,程序不会回头执行 goto
语句和目标标签之间的代码块,这些代码块会被直接跳过,程序会按照正常的顺序继续执行目标标签之后的代码。
#include <stdio.h>
void func() {
int i = 0;
goto start;//跳到start板块
printf("这行代码不会被执行\n");
start:
while (i < 3) {
printf("i = %d\n", i);
i++;
if (i == 2) {
// 向前跳转
goto end;
}
}
end:
printf("循环结束\n");
}
int main() {
func();
return 0;
}
二、数组
1、数组分类
按数据类型分类
//字符型
char s[10]; s[0]、s[1]、s[2]、...s[9]
//短整型
short int a[10] ; a[0]、a[1]、a[2]=7、...a[9]=8
//整形
int a[10]; a[0]、a[1]、a[2]、...a[9]
//长整型
long int a[10]; a[0]、 a[1]、a[2]、...a[9]
//浮点型
float a[10]; a[0]、 a[1]、a[2]、... a[9]=3.14f
double a[6]; a[0]、 a[1]、a[2]、...a[9]=3.1415926
//指针数组
char *a[8];
int *a[8];
//结构体数组
struct stu boy[10];
按维数分类
//一维数组
int a[10];
a[0]、a[1]、a[2]、a[3]、a[4]、a[5]、a[6]、a[7]、a[8]、a[9]
//二维数组
int a[2][3];
a[0][0]、a[0][1]、a[0][2]
a[1][0]、a[1][1]、a[1][2]
//多维数组
int a[2][3][4]; //可以看成是多个二维数组构成的
2、定义一个数组
定义一维数组
//定义了一个名字为a,含有5个元素的数组
int a[5];
//定义数组时不给出数组元素的个数,通过初始化定义数组大小
int a[];
a[]={1,2,3,4,5}; //a[0]=1、a[1]=2、a[2]=3、a[3]=4、a[4]=5
定义二维数组
//前一个表示行数,后一个表示列数
int a[2][3]; //定义了2行3列的数组,即
//a[0][0]、a[0][1]、a[0][2]
//a[1][0]、a[1][1]、a[1][2]
//二维数组在定义时可不给出行数,但必须有列数
int a[][3];
a[][3]={1,2,3},
{4,5,6}; //通过初始化定义了行数
3、数组的初始化
一维数组的初始化
//全部初始化
int a[5]={1、2、3、4、5};
//部分初始化
int a[5]={1、2、3}; //位数不够的后面补0
//即a[0]=1、a[1]=2、a[2]=3、a[3]=0、a[4]=0
二维数组的初始化
//全部初始化
int a[2][3]={{1,2,3},{3,4,5}};
//部分初始化
int a[2][3]={{1,2,5},{3,4}}; //同理,后面0补齐
//逐个初始化
a[2][3]={1,2,3,4,5,6}; //逐个初始化顺序是先从第零行开始赋值
//然后下一行
4、数组的引用(遍历数组)
字符数组以"\0"结尾,比实际多一位
//遍历一维数组
#include <stdio.h>
int main() {
// 定义一个包含 5 个元素的整型数组
int arr[5] = {1, 2, 3, 4, 5};
int i;
// 使用 for 循环遍历数组
for (i = 0; i < 5; i++) {
// 打印数组中每个元素的值
printf("arr[%d] = %d\n", i, arr[i]);
}
return 0;
}
//遍历字符数组
#include <stdio.h>
int main() {
// 定义一个字符数组(字符串)
char str[] = "Hello";
int i;
// 遍历字符数组
for (i = 0; str[i] != '\0'; i++) {
// 打印字符数组中每个字符
printf("%c ", str[i]);
}
printf("\n");
return 0;
}
//遍历多维数组
#include <stdio.h>
int main() {
// 定义一个 2 行 3 列的二维整型数组
int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};
int i, j;
// 外层循环遍历行
for (i = 0; i < 2; i++) {
// 内层循环遍历列
for (j = 0; j < 3; j++) {
// 打印二维数组中每个元素的值
printf("arr[%d][%d] = %d\n", i, j, arr[i][j]);
}
}
return 0;
}
三、函数
1、函数的分类
(1)C语言库函数
#include <stdio.h> //输入输出函数
这类函数用于处理输入和输出操作,比如从标准输入读取数据,向标准输出或文件写入数据等。
#include <stdio.h> //输入输出函数
printf:格式化输出到标准输出设备(通常是屏幕)。
#include <stdio.h>
int main() {
int num = 10;
printf("The number is: %d\n", num);
return 0;
}
scanf:从标准输入设备(通常是键盘)读取格式化输入。
#include <stdio.h>
int main() {
int num;
printf("Enter a number: ");
scanf("%d", &num);
printf("You entered: %d\n", num);
return 0;
}
fopen:打开一个文件,返回一个文件指针。
#include <stdio.h>
int main() {
FILE *fp = fopen("test.txt", "w");
if (fp != NULL) {
fputs("Hello, World!", fp);
fclose(fp);
}
return 0;
}
#include <string.h> //字符串处理函数
这些函数用于处理字符串,包括字符串的复制、连接、比较等操作。
strcpy:将一个字符串复制到另一个字符串。
#include <stdio.h>
#include <string.h>
int main() {
char src[] = "Hello";
char dest[10];
strcpy(dest, src);
printf("Copied string: %s\n", dest);
return 0;
}
strcat:将一个字符串连接到另一个字符串的末尾。
#include <stdio.h>
#include <string.h>
int main() {
char str1[20] = "Hello";
char str2[] = " World";
strcat(str1, str2);
printf("Concatenated string: %s\n", str1);
return 0;
}
strcmp:比较两个字符串的大小。
#include <stdio.h>
#include <string.h>
int main() {
char str1[] = "apple";
char str2[] = "banana";
int result = strcmp(str1, str2);
if (result < 0) {
printf("%s is less than %s\n", str1, str2);
} else if (result > 0) {
printf("%s is greater than %s\n", str1, str2);
} else {
printf("%s is equal to %s\n", str1, str2);
}
return 0;
}
#include<math.h> //数学函数
用于执行各种数学运算,如三角函数、对数函数、幂函数等。
sqrt:计算一个数的平方根。
#include <stdio.h>
#include <math.h>
int main() {
double num = 25.0;
double result = sqrt(num);
printf("Square root of %lf is %lf\n", num, result);
return 0;
}
sin:计算一个角度的正弦值(角度以弧度为单位)。
#include <stdio.h>
#include <math.h>
int main() {
double angle = 30.0;
double rad = angle * M_PI / 180.0;
double result = sin(rad);
printf("Sine of %lf degrees is %lf\n", angle, result);
return 0;
}
#include<stdlib.h> //内存管理函数
用于动态分配和释放内存。
malloc:分配指定大小的内存块。
free:释放之前由malloc、calloc或realloc分配的内存块。
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr = (int *)malloc(5 * sizeof(int));
if (ptr != NULL) {
for (int i = 0; i < 5; i++) {
ptr[i] = i;
}
for (int i = 0; i < 5; i++) {
printf("%d ", ptr[i]);
}
free(ptr);
}
return 0;
}
#include<time.h> //时间日期函数
用于处理时间和日期相关的操作。
time:返回当前的时间(以秒为单位,从 1970 年 1 月 1 日开始计算)。
#include <stdio.h>
#include <time.h>
int main() {
time_t current_time = time(NULL);
printf("Current time in seconds since epoch: %ld\n", current_time);
return 0;
}
#include<ctype.h> //字符处理函数
用于对单个字符进行处理和判断。
isupper:判断一个字符是否为大写字母。
#include <stdio.h>
#include <ctype.h>
int main() {
char ch = 'A';
if (isupper(ch)) {
printf("%c is an uppercase letter.\n", ch);
} else {
printf("%c is not an uppercase letter.\n", ch);
}
return 0;
}
(2)有参和无参函数
有参函数
//参数在后面的()内,可以是一个或多个,参数类型随意
int function(int a,float b,double c)
{
...;
}
无参函数
//无参函数()内什么都不写或者写入void
int function(void)
{
...;
}
int function()
{
...;
}
(3)有无返回值的函数
有返回值的函数
//函数体内必须有return
//返回值的类型在函数
int function() //表示返回int值
{
...;
return 1;
}
//若没有给出返回值类型,默认为int型
function()
{
...;
return 0;
}
无返回值的函数
//在定义函数时前面加void
void function(形参表)
{
...;
return;
}
2、调用函数
变量名=函数名(实参列表) //带返回值
函数名(实参列表) //不带返回值
//有返回值
int function(int x,int y)
{
...;
}
int main()
{
int number;
number=function(4,8);
.
.
.
}
//无返回值
void function()
{
printf("hello,world");
}
int main()
{
function();
}
四、变量的储存类型
1、内存分区简介
(1)内存分为物理内存和虚拟内存我们写程序时看到的都是虚拟内存
操作系统会在物理内存和虚拟内存之间做映射
(2)在运行程序时系统将虚拟内存分区
堆
动态申请内存时,在堆里开辟空间
栈
主要存放局部变量
静态全局区
代码区
存放我们的代码
文字常量区
2、普通全局变量
概念:在函数外部定义的变量
int a=100; //a为全局变量
int main()
{
.
.
.
}
作用范围:程序的所有地方,用之前声明 extern int a;
生命周期:程序运行的整个过程,从开始到结束
注意:如果定义一个全局变量不付初值,默认初值为0
3、静态全局变量 static
static int a; //a是一个静态全局变量
int main()
{
.
.
.
}
与全局变量的区别就是加了static只能在它所属的.c文件中使用,无法使用extern,其他的一样
4、普通局部变量
概念:函数内部定义的或者复合语句中定义的
int main()
{
int a; //局部变量
for()
{
int b; //局部变量
}
}
作用范围:只在函数内部有效,只在复合语句内部有效
生命周期:函数调用前不占用空间,调用函数时才开辟空间给局部变量,函数结束空间释放
5、静态局部变量
与局部变量的区别在生命周期
第一次调用函数开辟空间赋值,函数结束时不释放此后再次调用该函数不再开辟空间,也不赋初值,用的还是static的变量
6、外部函数和内部函数
外部函数:我们定义的函数都是外部函数,即可在程序的任何一个.c文件中使用
内部函数:加static,只能在此.c文件中使用
五、预处理、动态库、静态库
1、C语言编译过程
(1)预编译
将.c的头文件展开,宏展开
生成的文件是.i文件
(2)编译
将预处理后的.i文件生成.s文件
(3)汇编
将.s文件生成.o文件
(4)链接
将.o文件链接成目标文件
2、define宏定义
(1)不带参宏
定义宏用define去定义
终止宏定义用undef
#define PI 3.14 //预编译时出现PI就用3.14去替换
//想要终止宏定义用#undef
int main()
{
double a;
a=PI; //此时a=3.14
#undef PI //终止宏定义PI=3.14
#define PI 6.28
double b;
b=PI; //此时PI=6.28
return 0;
}
(2)带参宏
#define S(a,b) a*b //例如遇到S(2,6)替换成2*6
注意:a、b不带类型名
3、选择性编译
(1)#ifdef
//如果在当前.c的ifdef的上边定义过了BBB,就编译代码段1
//否则编译代码段2
#ifdef BBB
代码段1;
#else
代码段2;
#endif
#include <stdio.h>
// 定义一个宏
#define DEBUG
int main() {
// 使用 #ifdef 检查 DEBUG 宏是否被定义
#ifdef DEBUG
printf("调试模式已开启,正在输出调试信息...\n");
#endif
printf("程序正常运行...\n");
return 0;
}
//与if else语句最直接的区别就是if else的条件是表达式,ifdef是条件
(2)#ifndef
与ifdef互补
#ifndef BBB
代码段1;
#else
代码段2;
#endif
(3)#if
//表达式为真,执行第一段代码,否则执行第二段
#if 表达式
程序段1;
#else
程序段2;
#endif
4、静态库与动态库
(1)静态编译使用的是静态库文件进行编译,动态编译用的是动态库文件进行编译
(2)二者区别:
a、使用的库文件格式不一样
b、静态编译把静态库文件打包编译到可执行程序中
c、动态编译不会把动态库文件打包编译到可执行程序中,只是编译链接关系
5、静态库与动态库文件的制作
静态库文件的制作
静态库在链接时会被完整地复制到可执行文件中,程序运行时不再依赖静态库文件。以下是制作静态库的详细步骤:
(1) 编写源文件
假设我们有两个源文件 add.c
和 sub.c
,分别实现加法和减法功能,同时有对应的头文件 math.h
声明函数。
#include "math.h"
int add(int a, int b) {
return a + b;
}
#include "math.h"
int sub(int a, int b) {
return a - b;
}
#ifndef MATH_H
#define MATH_H
int add(int a, int b);
int sub(int a, int b);
#endif
(2) 编译源文件生成目标文件
使用 gcc
编译器将源文件编译成目标文件(.o):
gcc -c add.c sub.c
这会生成 add.o
和 sub.o
两个目标文件。
(3)创建静态库
使用 ar
工具将目标文件打包成静态库文件:
ar rcs libmath.a add.o sub.o
r
:将目标文件插入到静态库中。c
:创建静态库,如果库文件不存在则创建。s
:写入一个目标文件索引到库中,或者更新一个已存在的索引。
(4)使用静态库
编写一个测试程序 test.c
:
#include <stdio.h>
#include "math.h"
int main() {
int a = 10, b = 5;
printf("Addition: %d\n", add(a, b));
printf("Subtraction: %d\n", sub(a, b));
return 0;
}
编译并链接静态库:
gcc -o test test.c -L. -lmath
-L.
:指定库文件的搜索路径为当前目录。
-lmath
:链接名为 libmath.a
的静态库。
(5)运行程序
./test
动态库文件的制作
动态库在程序运行时才会被加载,多个程序可以共享同一个动态库,节省内存空间。以下是制作动态库的详细步骤:
(1)编写源文件
使用上面的 add.c
、sub.c
和 math.h
文件。
(2) 编译源文件生成目标文件
使用 gcc
编译器,并添加 -fPIC
选项,生成与位置无关的代码:
gcc -fPIC -c add.c sub.c
-fPIC
:生成位置无关代码,使得动态库可以在不同的内存地址加载。
(3)创建动态库
使用 gcc
编译器将目标文件链接成动态库文件:
gcc -shared -o libmath.so add.o sub.o
-shared
:指定生成动态库文件。
(4)使用动态库
编写测试程序 test.c
(与静态库测试程序相同):
#include <stdio.h>
#include "math.h"
int main() {
int a = 10, b = 5;
printf("Addition: %d\n", add(a, b));
printf("Subtraction: %d\n", sub(a, b));
return 0;
}
编译并链接动态库:
gcc -o test test.c -L. -lmath
(5)运行程序
在运行程序之前,需要将动态库的路径添加到系统的动态库搜索路径中。可以通过设置 LD_LIBRARY_PATH
环境变量来实现:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
./test
六、指针
1、指针的定义方法
(1)简单的指针变量
数据类型 * 指针变量名
int* p; //*是用来修饰变量的,说明p是一个指针变量
//变量名是p
(2)指针的运算符
“ & ”取地址、“ * ”取值
int a=53;
int *p;
p=&a;
//p保存了a的地址,也可以说p指向了a;
int num;
num=*p;
//此时,num=a;
2、指针和变量的关系
(1)指针的类型:int、long int、float、函数指针、结构体指针等
对应类型的指针只能保存对应类型数据的地址,如果想让不同类型的变量相互赋值,需要强制数据类型转换
//强制转换数据类型
int a=33;
char*p;
p=(char*)&a;
(2)变量地址
在 C 语言里,每个变量在内存中都有一个起始地址,这个起始地址指向该变量所占内存块的第一个字节。对于一个占四个字节的变量 a
,它的地址就是这四个连续字节中第一个字节的地址。
比如有一个int型变量a占有四个字节,每一个字节都有一个地址,那么这个a变量的指针指向第一个字节的地址。
#include <stdio.h>
int main() {
int a = 10; // int 类型通常占四个字节
char *p = (char *)&a; // 将 int 类型变量 a 的地址强制转换为 char 类型指针
// 输出变量 a 的起始地址
printf("变量 a 的起始地址: %p\n", (void *)&a);
// 输出 a 所占四个字节的每个字节的地址
for (int i = 0; i < 4; i++) {
printf("第 %d 个字节的地址: %p\n", i + 1, (void *)(p + i));
}
return 0;
}
//1.定义变量:
//int a = 10; 定义了一个 int 类型的变量 a,
//在大多数系统中,int 类型占四个字节。
//2.指针类型转换:
//char *p = (char *)&a; 将 a 的地址强制转换为 char 类型的指针。
//char 类型占一个字节,通过 p + i 可以依次访问 a 所占四个字节中的每个字节。
//3.输出地址:
//printf("变量 a 的起始地址: %p\n", (void *)&a); 输出变量 a 的起始地址。
//通过 for 循环,printf("第 %d 个字节的地址: %p\n", i + 1, (void *)(p + i));
//依次输出 a 所占四个字节中每个字节的地址。
为什么要强行将int型变量a转换成char型指针?
(1)指针步长的概念
在 C 语言中,指针的步长取决于它所指向的数据类型。当对指针进行算术运算(如 p + 1
)时,指针会根据其指向的数据类型的大小向前移动相应的字节数。具体规则如下:
对于 char
类型指针,其指向的数据类型是 char
,char
类型通常占 1 个字节,所以 char
类型指针每次加 1,指针会向前移动 1 个字节。
对于 int
类型指针,其指向的数据类型是 int
,在大多数系统中 int
类型占 4 个字节,所以 int
类型指针每次加 1,指针会向前移动 4 个字节。
示例:
#include <stdio.h>
int main() {
int a = 10;
int *int_ptr = &a;
char *char_ptr = (char *)&a;
// 输出 int 类型指针加1后的地址变化
printf("int 类型指针初始地址: %p\n", (void *)int_ptr);
printf("int 类型指针加1后的地址: %p\n", (void *)(int_ptr + 1));
// 输出 char 类型指针加1后的地址变化
printf("char 类型指针初始地址: %p\n", (void *)char_ptr);
printf("char 类型指针加1后的地址: %p\n", (void *)(char_ptr + 1));
return 0;
}
在上述代码中:
int_ptr
是 int
类型指针,int_ptr + 1
会使指针向前移动 4 个字节(因为 int
类型通常占 4 个字节)。
char_ptr
是 char
类型指针,char_ptr + 1
会使指针向前移动 1 个字节(因为 char
类型占 1 个字节)。
(2)逐个字节访问内存的需求
如果想要逐个字节地访问 int
类型变量 a
所占用的 4 个字节的内存,就需要使用 char
类型指针。因为 char
类型指针每次加 1 只会移动 1 个字节,这样就可以依次访问到 a
所占的每个字节。例如:
#include <stdio.h>
int main() {
int a = 10;
char *p = (char *)&a;
for (int i = 0; i < 4; i++) {
printf("第 %d 个字节的地址: %p\n", i + 1, (void *)(p + i));
}
return 0;
}
在这个代码中,通过将 int
类型变量 a
的地址强制转换为 char
类型指针,就可以使用 p + i
依次访问 a
所占的 4 个字节的内存地址。
综上所述,将 int
类型变量的地址强制转换为 char
类型指针是为了利用 char
类型指针的步长特性,实现对 int
类型变量所占用内存空间的逐个字节访问。
3、指针和数组元素的关系
(1)示例:
int a[5];
int *p;
p=&a[0];
//p中保存了数组a中的a[0]的地址
(2)数组元素的引用方法
方法一:直接引用
//方法1
int a[5];
a[2]=100;
方法二:指针名+下标
//方法2
int a[5];
int *p;
p=a;
p[2]=100;
方法2中的p=a可能有人看不懂,p不是指针变量吗?怎么能被a赋值呢?
在C 语言中,数组名在大多数表达式里会被隐式转换为指向数组首元素的指针,也就是说,数组名代表的是数组首元素的地址。
方法三: 通过指针变量运算加取值
//方法3
int a[5];
int *p;
p=a;
*(p+2)=100; //相当于a[2]=100;
方法四:通过数组名+取值的方法
//方法4
int a[5];
*(a+2)=100;//相当于a[2]=100;
对于方法四的解释上面说过了,数组名代表的是数组首元素的地址。
4、指针的运算
(1)指针可以加一个整数,往下指几个它指向的变量,结果还是地址
指针指向数组的时候,加上一个整数才有意义
int a[5];
int *p;
p=a;
p+2; //p是a[0]的地址,p+2是a[2]是&a[2];
例如p保存的地址编号是2000,则p+2代表的地址编号是2008;
char num[5];
char *p;
p=num;
p+2; //相当于&num[2]
例如p存放的地址是2000,则p+2代表的地址是2002;
取决于变量的类型占几个字节;
(2)两个相同类型的指针可以比较大小
前提:只有两个相同类型的指针指向同一个数组的元素时,比较大小才有意义,指向前一个元素地址的指针小于指向后一个元素的指针
#include<stdio.h>
int main(int argc,char *argv[])
{
int a[10];
int *p,*q,n;
p=&a[1];
q=&a[6];
if(p<q)
{
printf("p<q\n");
}
else if(p>q)
{
printf("p>q\n");
}
else
{
printf("p=q\n");
}
return 0;
}
运行结果:
p<q
(3)两个相同类型的指针可以做减法
前提:两个相同类型的指针指向同一个数组的元素时,做减法才有意义,做减法的结果是,两个指针指向的中间有多少个元素。这个数组
#include<stdio.h>
int main()
{
int a[10];
int *p,*q;
p=&a[0];
q=&a[3];
printf("%d\n",q-p);
}
(4)两个相同类型的指针可以相互赋值
int *p;
int *q;
int a;
p=&a;
q=p;
//不同类型的指针相互赋值必须进行强制类型转换
5、指针数组
(1)指针和数组的关系
指针可以保存数组元素的地址;
可以定义一个数组,数组中有若干个相同类型指针变量这个数组被称为指针数组;
指针数组的概念:
指针数组本身是一个数组,是若干个相同类型的指针变量构成的集合;
(2)指针数组的定义方法
int *p[5]; //例如我定义了一个指针数组
//用法
int a;
p[0]=&a;
int b[10];
p[1]=&b[5];
其中,p[2]和 *(p+2)是等价的都是指针数组中第二个元素
(3)字符指针数组
#include<stdio.h>
int main()
{
char *name[5]={"hello","china","beijing","project","computer"};
int i;
for(i=0;i<5;i++)
{
printf("%s\n",name[i]);
}
return 0;
}
6、指针的指针
指针的指针,即指针的地址,我们定义了一个指针变量,它本身占用了四个字节,也有地址编号。
int a=0x12345678;
//假设a的地址为0x00002000;
int *p;
p=&a;
//p中存放的地址编号为0x00002000;
//假设p的地址编号为0x00003000;
int **q;
q=&p;
//q中存放的p的地址为0x00002000;
7、字符串和指针
(1)字符串概念:以“\0”结尾的若干个字符的集合,如“helloworld”;
(2)字符串的地址:第一个字符的地址,如“helloworld”的地址就是“h”的地址,我们可以定义一个字符指针变量保存字符串的地址,如char *s=“helloworld”;
(3)字符串的储存形式:数组、文字常量区、堆
a.字符串存放在数组中
字符串存放在数组中,其实就是内存中开辟了一段空间放字符串;
char string[100]="helloworld";
b.字符串存放在文字常量区 在文字苍凉去开辟了一段空间存放字符串,将字符串的首地址赋给指针变量;
char *str="helloworld";
定义了一个指针变量str只能存放字符地址编号,对于“helloworld”这个字符串中的字符不是存放在str指针变量中,str只存放了“h”的地址编号,整个“helloworld”存放在文字常量区
c.字符串存放在堆区
使用malloc等函数在堆区申请空间,将字符串拷贝到堆区;
char *str=(char*)malloc(10*sizeof(char));
//动态申请了10个字节的储存空间
//首地址给str赋值
strcpy(str."helloworld");
//将字符串“helloworld”拷贝到str指向的内存里
8、字符串的可修改性
字符串内容是否可以修改,取决于字符串存放在哪里;
(1)存放在数组中的字符串的内容是可修改的
char str[100]="madeinchina";
str[0]=k;
//则str内存放的字符串变为"kadeinchina";
(2)文字常量区里的内容是不可修改的
char *str="helloworld";
*str='k';//错误
//“h”存放在文字常量区,不可修改
注:1.str指向文字常量区的时候,它指向的内存的内容不可修改。
2.str是指针变量可以指向别的地方,即可以给str重新赋值。
(3)堆区的内容是可以修改的
char *str=(char*)malloc(10);
strcpy(str."helloworld");
*str='y';//正确
9、字符串三种储存形式的初始化及使用赋值
(1)字符数组初始化:
char buf_aver[20]="hello world";
使用时赋值:
使用scanf或者strcpy
char buf[20]="helloworld";
buf="hellomother";//错误,因为字符数组的名字是常量,本恩用“=”给常量赋值。
strcpy(buf,"hellomother");//正确,数组中的内容是可以修改的
scanf("%s",buf);//正确
(2)指针指向文字常量区,初始化:
char *buf_point="hello world";
使用时赋值:
char *buf_point="helloworld";
buf_point="hellokitty";//正确。buf_point指向另一个字符串
strcpy(buf_point,"hellokitty");//错误,这种情况,buf_point指向的是文字常量区,内容只读
(3)指针指向堆区,堆区存放字符串
不能初始化,只能先给指针赋值,让指针指向堆区,再使用strcpy、scanf等方法把字符串拷贝到堆区。
char *buf_heap;
buf_heap=(char*)malloc(15);
strcpy(buf_heap,"hello world");
scanf("%s",buf_heap);
使用时赋值:
char *buf_heap;
buf_heap=(char*)malloc(15);
strcpy(buf_heap,"helloworld");
scanf("%s",buf_heap);
10、数组指针
数组指针是一个指针,指针数组是一个数组
数组指针的定义方法
int (*p)[5];//定义了一个数组指针变量p
//p指向的是整型的有5个元素的数组
//p+1往下指五个整型,跳过一个有五个整型元素的数组
#include<stdio.h>
int main()
{
int a[3][5];//定义了一个3行5列的一个二维数组
int(*p)[5];//定义一个数组指针变量p,p+1跳一个有5个元素的整型数组
printf("a=%p\n",a);//第0行的行地址
printf("a+1=%p\n",a+1);//第1行的行地址,a和a +1差20个字节
p=a;
printf("p=%p\n",p);
printf("p+1=%p\n",p+1);//p+1跳一个有5个整型元素的一维数组
return 0;
}
七、动态内存申请
1、动态分配函数
stdlib.h
(1)malloc函数
函数原型:void *malloc(unsigned int size);
功能:在内存的动态存储区(堆区)中分配一块长度为size字节的连续区域,用来存放类型说明符指定的类型。函数原型返回void*指针,使用时必须做强制的类型转换,分配的空间内容不确定,一般使用memset初始化。
返回值:分配空间的起始地址(分配成功)
NULL(分配失败)
注意:
a.在调用malloc之后,一定要判断下是否申请内存成功。
b.如果多次malloc申请内存,申请的内存不一定是连续的
使用方法:
char *p;
p=(char*)malloc(20);
(2)free函数(释放内存函数)
函数定义:void free(void *ptr)
函数说明:free函数释放ptr指向的内存,其中,free释放的必须是malloc、calloc、realloc申请的动态内存。
使用方法:
char *p=(char*)malloc(20);
free(p);
注意:free后,因为没有给p赋值,所以p还是指向原先动态申请的内存,但是不能用,因为被释放掉了,此时p成了野指针。
(3)calloc函数
函数定义:void* calloc(size_t nmemb,size_t size);
(size_t实际上是无符号整型,它在头文件中是用typedef定义出来的);
函数功能:在内存的堆中,申请nmemb块,每块的大小为size个字节的连续区域
使用方法:
char*p(char*)calloc(3,100);
//在堆中申请了三块,每块大小100个字节,即300个字节的连续区域;
(4)realloc函数(重新申请内存)
我们调用calloc和malloc函数时,单次申请的内存是连续的,多次不一定连续,如果我们申请了一次内存,我们想再次申请的内存挨着原来申请的内存,或者想释放后面的一部分内存,我们就是用realloc。
函数定义:void* realloc(void* s,unsigned int newsize);
函数功能:在原先s指向的内存上重新申请内存,新的内存大小为newsize个字节,如果原先内存后面有足够大的空间,则追加,如果后面的内存不够用,则realloc函数会在堆区找一个newsize个字节大小的内存申请,然后将原先内存的内容拷贝过来,然后释放原先的内存,最后返回新内存的地址。
如果newsize比原先的内存小,则会释放原先内存的后面的储存空间,只留前面newsize个字节。返回值:新申请内存的首地址;
例:(可以参考以下代码再去回顾上方的解释)
char*p;
p=(char*)malloc(100);
//在100后面追加50个字节
p=(char*)realloc(p,150);
//p指向的新的内存大小为150个字节
例:(第二种情况示例)
char*p;
p=(char*)malloc(100);
//重新申请内存,内存大小为50字节
p=(char*)realloc(p,50);
//p指向的内存的新的大小为50字节,原100字节后面的50字节被释放;
calloc、malloc、realloc动态申请的内存,只有在free或者程序结束时才被释放;
2、内存泄偶
概念:申请的内存首地址丢失,再也没法使用了,也没法释放,这块内存就泄露了
例1:
int main()
{
char*p;
p=(char*)malloc(100);
//p指向了一块内存;
p="thank you";//p指向了其他地方
//因此你无法找到原来申请100内存的首地址,无法使用
return 0;
}
例2:
void function()
{
char*p;
p=(char*)malloc(100);
}
int main()
{
function();
function();
return 0;
}
//每调用一次function,泄露100个字节;
内存泄漏解决方法:
方法一:
void function()
{
char*p;
p=(char*)malloc(100);
free(p);
}
//函数用完就释放
int main()
{
function();
function();
return 0;
}
方法二:
char*function()
{
char*p;
p=(char*)malloc(100);
return p;
}
int main()
{
char*q;
q=function();
//通过中间变量q来使用动态申请100字节内存;
free(q);
//用完释放;
return 0;
}
八、字符串处理函数
#pragma指令:用于指定计算机或者操作系统特定的编译功能;
#pragma warning(disable:4996) 在c文件开始处写上这句话,即告诉编译器忽略4996警告,strcpy、scanf等一些不安全的标准c库函数在vs中可以使用。
1、_strlen函数
函数定义:size_t strlen(const char* s);
函数功能:测字符指针s指向的字符串中字符的个数,不包括'\0';
返回值:字符串中字符个数;
例:
#include<stdio.h>
#include<string.h>
int main()
{
char str1[20]="hello";
char*str2="hello";
printf("%d\n",sizeof(str1));//20
printf("%d\n",sizeof(str2));//4
printf("%d\n",strlen(str1));//5
printf("%d\n",strlen(str2));//5
return 0;
}
sizeof是关键字,测量的数据占用内存空间大小;
如果sizeof测量的是数组的名字,则测得是数组占多少个字节;
如果sizeof测的是指针变量,则测的是指针变量本身占几个字节;
strlen是库函数,它测的是字符指针指向的字符串中字符的个数,不管指针是数组的名字,还是个指针变量。
2、_strcpy和_strncpy函数
字符拷贝函数
函数定义:char*strcpy(char*dest,const char *src);
函数功能:拷贝src指向的字符串到dest指针指向的内存中,'\0'也会被拷贝;
返回值:目的内存的地址;
注意:使用此函数时,必须保证dest指向的内存足够大,否则会出现内存污染;
例1:
#include<stdio.h>
#include<string.h>
int main()
{
char str[100]="ababababababababababa";
strcpy(str,"hello");
printf("str=%s\n",str);
return 0;
}
输出:str=hello
函数定义:char*strncpy(char*dest,const char *src,size_t n);
函数功能:将src指向的字符串前n个字节,拷贝到dest指向的内存中;
返回值:目的内存的首地址;
注意:strncpy不拷贝'\0';如果n大于src指向的字符串中的字符个数,则在dest后面填充n-strlen(src)个'\0';
例2:
#include<stdio.h>
#include<string.h>
int main()
{
char buf[100]="abababababababa";
strncpy(buf,"helloworld",5);
printf("%s\n",buf);
return 0;
}
输出:buf=helloabababababababa
3、_strcat和_strncat函数
字符追加函数
函数定义:char* strcat(char*dest,const char* src);
函数功能:strcat函数追加src字符串到dest指向的字符串的后面,追加在第一个\0的后面,\0前面的内容不懂
注意:保证dest指向的空间足够大
例:
#include<stdio.h>
#include<string.h>
int main()
{
char str[20]="ab\0ababababababa";
char *src="hello";
strcat(str,src);
printf("%s\n",str);
return 0;
}
输出:str=abhello
函数定义:char* strncat(char*dest,const char* src,size_t n);
函数功能:追加src指向的字符串的前n个字符,追加到dest指向的字符的后面;
注意:如果n大于src字符串的字符个数,则只将src字符串追加到dest指向的字符串的后面,同样追加到第一个"\0"的后面。
例:
#include<stdio.h>
#include<string.h>
int main()
{
char str[20]="ab\0ababababa";
char *str="hello";
strncpy(str,src,3);
printf("%s\n",str);
return 0;
}
输出:abhel
4、_strcmp和_strncmp函数
字符串比较函数
函数定义:int strcmp(const char*s1,const char*s2);
函数说明:比较字符串s1和s2的大小,逐个字符去比较ascII码,一旦比较出大小就返回;
返回值:
s1>s2,则返回1;s1<s2,则返回-1;s1=s2,则返回0;
函数定义:int strcmp(const char*s1,const char*s2,size_t n);
函数说明:比较s1和s2指向的字符串中的前n个字符;
5、_strchr和_strrchr函数
字符查找函数
函数定义:char* strchr(const char*s,int c);
函数说明:strchr
会从字符串 str
的起始位置开始,逐个字符检查,直到找到字符 c
或遇到字符串结束符 \0
。
返回值:返回找到字符的地址;未找到的话返回NULL;
函数定义:char* strrchr(const char*s,int c);
函数说明:strrchr
会从字符串 str
的起始位置开始,逐个字符检查,直到字符串结束符 \0
,然后返回字符 c
最后一次出现的位置。
二者区别:
strchr
查找字符的第一次出现位置。strrchr
查找字符的最后一次出现位置。
6、_strstr函数
字符串匹配函数
函数定义:char *strstr(const char *haystack, const char *needle);
参数说明:haystack
:要搜索的主字符串(以 \0
结尾的字符数组);needle
:要查找的子字符串(以 \0
结尾的字符数组)。
函数功能:strstr
会从主字符串 haystack
的起始位置开始,逐个字符检查,直到找到子字符串 needle
或遇到主字符串的结束符 \0
。
返回值:如果找到子字符串 needle
,则返回指向主字符串 haystack
中子字符串第一次出现位置的指针;如果未找到子字符串 needle
,则返回 NULL
。
例:
#include <stdio.h>
#include <string.h>
int main() {
const char *haystack = "Hello, World!";
const char *needle = "World";
// 查找子字符串 "World" 在主字符串中的位置
char *result = strstr(haystack, needle);
if (result != NULL) {
printf("子字符串 '%s' 在主字符串中的位置是: %ld\n", needle, result - haystack);
printf("从该子字符串开始的部分是: %s\n", result);
} else {
printf("子字符串 '%s' 未找到。\n", needle);
}
return 0;
}
输出:
子字符串 'World' 在主字符串中的位置是: 7
从该子字符串开始的部分是: World!
7、_memset函数
空间设定函数
函数定义:void*memset(void *ptr,int value,size_t num);
函数功能:memset函数是将ptr指向的内存空间的num个字节全部赋值为value参数;
ptr:只想任意类型的指针,即指向我们需要修改的内存;
value:给ptr指向的内存空间赋值;
num:确定将ptr所指向的内存中的num个字节全都用value代替
返回值:目的内存的首地址,即ptr的值;
例:
#include <stdio.h>
#include <string.h>
int main() {
char str[50] = "Hello, World!";
// 将 str 的前 5 个字节填充为 '*'
memset(str, '*', 5);
printf("填充后的字符串: %s\n", str);
return 0;
}
输出:
填充后的字符串: *****, World!
8、atoi/atol/atof函数
字符串转换数值
(1)_atol函数
函数原型:long atol(const char *str);
函数功能:将字符串转换为long型整数
返回值:返回转换后的long值,如果没法转换,返回0;
说明:
atol
的行为与 atoi
类似,但返回的是 long
类型。
适用于需要处理较大整数的场景。
例:
#include <stdio.h>
#include <stdlib.h>
int main() {
const char *str = "987654321";
long num = atol(str);
printf("转换后的长整数: %ld\n", num); // 输出: 987654321
return 0;
}
(2)_atoi函数
函数原型:int atoi(const char *str);
函数功能:将字符串转换为int型整数;
返回值:返回转换后的int型,无法转换则返回0;
说明:
atoi
会忽略字符串开头的空白字符(如空格、制表符等)。
从第一个非空白字符开始解析,直到遇到非数字字符或字符串结束符 \0
。
如果字符串无法转换为有效的整数,则返回 0
。
例:
#include <stdio.h>
#include <stdlib.h>
int main() {
const char *str = "12345";
int num = atoi(str);
printf("转换后的整数: %d\n", num); // 输出: 12345
return 0;
}
(3)_atof函数
函数原型:double atof(const char *str);
函数功能:将字符串转换为 double
类型的浮点数。
返回值:返回转换后的 double
值。如果字符串无法转换,则返回 0;
说明:
atof
会忽略字符串开头的空白字符;
从第一个非空白字符开始解析,直到遇到非浮点数字符或字符串结束符 \0;
支持科学计数法(如 "1.23e4"
);
例:
#include <stdio.h>
#include <stdlib.h>
int main() {
const char *str = "3.14159";
double num = atof(str);
printf("转换后的浮点数: %f\n", num); // 输出: 3.141590
return 0;
}
9、_strtok函数
字符串切割函数
函数原型:char *strtok(char *str, const char *delimiters);
函数功能:strtok
会从字符串 str
中查找分隔符,并将分隔符替换为 \0
,从而将字符串分割成多个子字符串。每次调用 strtok
会返回一个子字符串的指针。
参数说明:
str
:
要分割的字符串(以 \0
结尾的字符数组)。如果是第一次调用 strtok
,需要传入待分割的字符串。如果是后续调用,可以传入 NULL
,表示继续分割同一个字符串。
delimiters
:
分隔符字符串(以 \0
结尾的字符数组)。函数会将 delimiters
中的每个字符都视为分隔符。
例:
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "Hello,World!This is a test.";
const char *delimiters = " ,!."; // 分隔符为空格、逗号、感叹号和句号
// 第一次调用 strtok
char *token = strtok(str, delimiters);
while (token != NULL) {
printf("子字符串: %s\n", token);
// 后续调用 strtok
token = strtok(NULL, delimiters);
}
return 0;
}
输出:
子字符串: Hello
子字符串: World
子字符串: This
子字符串: is
子字符串: a
子字符串: test
九、结构体
1、结构体类型定义
定义方法:
(1)先定义结构体类型,再去定义结构体变量
struct 结构体类型名{
成员列表;
};
例:
struct stu{
int num;
char name[20];
char sex;
}
在有了结构体类型后,就可以定义结构体变量了:
struct stu jack,mike,lucy;
//定义了三个变量,分别为jack,mike,lucy
//其中,每个变量都包含三个成员,分别为num,name,sex
我们暂时认为结构体变量大小是所有成员变量大小之和;
(2)在定义结构体类型时顺便定义结构体变量
struct stu{
int num;
char name[20];
char sex;
}jack,mike,lucy;
(3)在定义结构体类型时,没有结构体类型名,顺便定义结构体变量
因为没有类型名,以后不能再定义相关类型的数据了;
struct{
int num;
char name[20];
char sex;
}jack,mike;
//此后不能再定义第三个变量了,除非回过头来加上去
(4)typedef替换结构体类型名简化代码
typedef struct stu{
int num;
char name[20];
char sex;
}STU;
用STU替换结构体,STU就相当于整个结构体,以后定义结构体变量直接就是:
STU jack,mike,lucy;
2、结构体变量定义初始化及使用
(1)结构体变量的定义和初始化
struct stu{
int num;
char name[20];
char sex;
};
int main()
{
struct stu jack;
struct stu mike={21,"mike","m"};
return 0;
}
(2)结构体变量的使用
变量成员引用方法:
struct stu{
int num;
char name[20];
char sex;
}jack,mike,lucy;
int main()
{
jack.num=21;
//这里如果报错用strcpy函数赋值
jack.name="jack";
jack.sex="m";
//这里如果报错用strcpy函数赋值
strcpy(jack.name,"jack");
strcpy(jack.sex,"m");
return 0;
}
这里直接给jack.name或者jack.sex赋值会报错,因为jack.name和jack.sex是个数组的名字,是常量,不能给常量赋值
(3)结构体成员嵌套使用
struct birdate{
int year;
int month;
int day;
};
struct stu{
int num;
char name[20];
char sex;
struct birdate birthday;
};
int main()
{
struct stu jack={21,"jack","m"};
jack.birthday.year=1830;
jack.birthday.month=4;
jack.birthday.day=9;
return 0;
}
3、结构体变量相互赋值
只有相同类型的结构体变量可以相互赋值
#include<stdio.h>
struct sut{
int num;
char name[20];
char sex;
};
int main()
{
struct stu jack={21,"jack","m"};
struct stu mike;
//把jack的值赋给mike
mike=jack;
return 0;
}
4、结构体数组
结构体数组由若干个相同类型的结构体变量构成的集合
(1)结构体数组的定义方法
struct stu{
int num;
char name[20];
char sex;
};
//定义了一个struct stu类的结构体数组peo
struct stu peo[3];
//包含三个元素peo[0]、peo[1]、peo[2];
(2)结构体数组的引用方法
struct stu{
int num;
char name[20];
char sex;
};
struct stu peo[3];
//直接引用
peo[0].num=21;
strcpy(peo[0].name,"jack");
5、结构体指针
即结构体的地址,结构体变量存放内存中,也有起始地址,我们定义一个变量来存放这个首地址,那么这个变量就是结构体指针变量
(1)定义方法
struct stu{
int num;
char name[20];
char sex;
};
//定义一个struct stu类型的指针变量p
struct stu* p;
//定义一个结构体变量jack
struct stu jack;
//访问方法
p=&jack;
(*p).num=21;
p->num=21;
通过指针引用结构体变量成员的前提:指针必须先指向一个结构体
(2)结构体指针应用场景
a.保存结构体变量的地址
typedef struct stu{
int num;
char name[20];
char sex;
}STU;
int main()
{
STU *p,jack;
p=&jack;
p->jack.num=21;
strcpy(p->jack.name,"jack");
return 0;
}
b.传结构体变量的地址
#include<stdio.h>
#include<string.h>
typedef struct stu{
int num;
char name[20];
char sex;
}STU;
void function(STU* p)
{
p->num=21;
strcpy((*p).name,"jack");
strcpy(p->sex,"m");
}
int main()
{
STU jack;
function(&jack);
return 0;
}
6、枚举
(1)枚举类型的定义方法
enum 枚举类型名{
枚举值列表;
};
(2)枚举变量的定义方法
enum 枚举类型名 枚举变量名;
//定义枚举类型week
enum week
{
mon,tue,wed,thu,fri,sat,sun
};
//定义了枚举变量workday和weekend;
enum workday,weekend;
//workday和weekend只能取mon,tue,...,sun;
注意:枚举值是常量,不能再赋值;
枚举变量是由系统定义的一个表示序号的数值,默认从0开始,如上方的从mon开始,mon为0,tue为1,......
我们可以改变枚举变量的默认值,如:
enum week
{
mon=3,tue=2,wed=5,thu,fri,sat,sun
};
十、文件
1、文件指针
文件指针在程序中用来标识(代表)一个文件的,在打开文件的时候得到文件指针,文件指针就代表我们打开的文件;
我们对文件进行读、写、关闭等操作的时候,对文件指针进行操作即可,我们将文件指针,传给读、写、关闭等函数,函数对他们进行操作;
(1)定义文件指针的一般形式
FILE* 指针变量标识符;
(2)对文件操作的步骤
a.对文件进行读写之前要打开文件得到文件指针
b.可以通过文件指针对文件进行读写操作
c.读写等操作完毕后,要关闭文件,关闭文件后,就不能再通过此文件指针操作文件了
2、_fopen和_fclose函数
(1)_fopen函数
函数声明:FILE*open(const char* path,const char* mode)
函数说明:打开一个已经存在的文件,并返回这个文件的文件指针。
函数参数:const char* path打开的文件的路径
const char* mode文件打开的方式(只读、只写、可读、可写等)
打开方式的几种形式:
r:以只读方式打开文件
文件不存在则返回NULL;
文件存在且成功打开,返回文件指针进行后续操作
FILE* fl;
fl=fopen("test.txt","r");
w:以只写方式打开文件
文件不存在,以指定文件名创建此文件,并且打开文件;
文件存在,清空文件内容,打开文件,然后进行写入操作;
文件打不开,比如只读,则返回NULL;
a:以追加方式打开文件
文件不存在,以指定文件名创建此文件;
文件存在,从文件的结尾处进行写入操作
+:同时以读写方式打开文件
(2)_fclose函数
函数声明:int fclose(FILE *fl);
函数说明:关闭fl指向的文件。
返回值:成功返回0,失败返回非0;
3、_fgetc和_fputc函数
一次读写一个字符
(1)_fgetc
函数声明:int fgetc(FILE* stream);
函数说明:fgetc从stream所标识的文件中读取一个字节,将字节值返回
返回值:
以t的方式:读到文件结尾返回EOF;
以b的方式:读到文件结尾,使用feof(文件指针)判断结尾
(2)_fputc
函数声明:int fputc(int c,FILE* stream)
函数说明:fputc将c的值写到stream所代表的文件中;
返回值:
输入成功,返回输入的字节值;
输入失败,返回EOF;
4、_fgets和_fputs函数
一次读写一个字符串
(1)_fgets
函数声明:char *fgets(char *str, int n, FILE *stream);
函数功能:
fgets
会从 stream
中读取最多 n-1
个字符,并在末尾自动添加 \0
。
如果在读取 n-1
个字符之前遇到换行符(\n
),则停止读取,并将换行符包含在结果中。
如果到达文件末尾,则停止读取。
返回值:
成功时,返回 str
的指针。
失败或到达文件末尾时,返回 NULL
。
#include <stdio.h>
int main() {
char buffer[100];
printf("请输入一行文本: ");
if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
printf("你输入的内容是: %s", buffer);
} else {
printf("读取失败或到达文件末尾。\n");
}
return 0;
}
(2)_fputs
函数声明:int fputs(const char *str, FILE *stream);
函数功能:
fputs
会将字符串 str
写入到 stream
中,但不包括结尾的 \0
。
如果字符串中包含换行符(\n
),则换行符也会被写入。
返回值:
成功时,返回非负整数。
失败时,返回 EOF
。
#include <stdio.h>
int main() {
const char *text = "Hello, World!\n";
if (fputs(text, stdout) != EOF) {
printf("字符串写入成功。\n");
} else {
printf("写入失败。\n");
}
return 0;
}
5、_fread和_fwrite函数
fread
和 fwrite
是 C 标准库中用于二进制数据读写的函数,定义在头文件 <stdio.h>
中。它们分别用于从文件流中读取数据和向文件流中写入数据,适合处理二进制文件或大块数据。
(1)_fread
函数原型:size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
函数功能:
fread
会从 stream
中读取 size * count
字节的数据,并将其存储到 ptr
指向的内存块中。
适合读取二进制文件或大块数据。
返回值:
返回实际读取的数据项数量(可能小于 count
)。
如果返回值小于 count
,可能是到达文件末尾或发生错误。
#include <stdio.h>
int main() {
FILE *file = fopen("data.bin", "rb");
if (file == NULL) {
perror("文件打开失败");
return 1;
}
int buffer[10];
size_t num_read = fread(buffer, sizeof(int), 10, file);
printf("读取了 %zu 个整数。\n", num_read);
fclose(file);
return 0;
}
(2)_fwrite
函数原型:size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
函数功能:
fwrite
会将 ptr
指向的内存块中的 size * count
字节数据写入到 stream
中。
适合写入二进制文件或大块数据。
#include <stdio.h>
int main() {
FILE *file = fopen("data.bin", "wb");
if (file == NULL) {
perror("文件打开失败");
return 1;
}
int buffer[] = {1, 2, 3, 4, 5};
size_t num_written = fwrite(buffer, sizeof(int), 5, file);
printf("写入了 %zu 个整数。\n", num_written);
fclose(file);
return 0;
}