序言
本文为作者整理c语言复习笔记所得,旨在能快速巩固、回忆c语言的基础知识。因此,本文只适合初学者入门和因太久未接触导致的遗忘之人。
Ethan X.
一、C程序的基本结构
(一)、基础概念
让我们从C程序的基本结构开始。一个基础的C程序包含以下元素:
- 预处理器指令:预处理器指令是在C程序编译之前进行处理的指令。这些指令都以
#
符号开始。最常见的预处理器指令是#include
,用于包含其他文件或库。例如,#include <stdio.h>
会包含C语言的标准输入输出库。
#include <stdio.h>
- 函数:在C程序中,代码是由函数构成的。函数是一段可以被其他部分的代码调用的代码块。C语言程序至少有一个函数,即
main()
函数,这是程序执行的入口点。
int main() {
// Your code goes here
}
main
函数返回一个整数,通常用return 0;
表示程序成功执行。return 0;
语句是可选的,如果没有写,编译器会自动添加。
int main() {
// Your code goes here
return 0;
}
- 函数体:大括号
{}
中的代码称为函数体。在main()
函数中,你的所有代码都将在这里编写。
每一段C代码都由一个或多个函数组成,每个函数完成一个特定的任务。代码的执行从main()
函数开始,这是C语言程序的入口点。
在这个基本结构中,我们可以添加更多的函数、变量和其他元素,如控制结构、数组、指针、结构体等,这都将在复习的后续部分介绍。
记住,C语言使用分号;
来结束一个语句,这是C语言的重要语法规则。如果忘记在语句后添加分号,编译器会报错。
以上就是C程序的基本结构,希望对你有所帮助。
(二)、巩固练习
1、编写一个简单的"Hello, World!"程序。
一个简单的 “Hello, World!” 程序在C语言中的写法如下:
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
这个程序的解释如下:
#include <stdio.h>
:这是预处理器指令,告诉C编译器在实际编译之前包含标准输入输出头文件(stdio.h)。int main()
:这是主函数,程序从这里开始执行。printf("Hello, World!\n");
:printf
函数用于在屏幕上打印输出。这里,它打印出 “Hello, World!”。\n
是一个特殊的字符,代表换行,使得输出内容不会在一行中。return 0;
:这是主函数的返回语句。主函数返回0,表示程序成功结束。
你需要把这段代码保存到一个 .c
文件中,然后使用C编译器(如gcc)来编译和运行它。如果一切正确,它会在屏幕上打印出 “Hello, World!”。
2、试着使用不同的数据类型(例如int、float、char)并输出它们。
在这个练习中,你将创建几个不同类型的变量,并使用printf函数来输出它们。这是一个例子:
#include <stdio.h>
int main() {
int integerVar = 100;
float floatingVar = 331.79;
char charVar = 'G';
printf("整型变量的值为 %d\n", integerVar);
printf("浮点型变量的值为 %.2f\n", floatingVar);
printf("字符型变量的值为 %c\n", charVar);
return 0;
}
这个程序的解释如下:
- 我们首先定义了三个变量:一个int(整型)变量
integerVar
,一个float(浮点型)变量floatingVar
,以及一个char(字符型)变量charVar
。 - 然后我们使用
printf
函数来输出这些变量的值。在printf中,我们使用格式化字符串来指定输出的格式。%d
用于int,%.2f
用于float,并且我们限定了小数点后的位数为2,%c
用于char。 - 注意到,我们在printf函数中使用逗号来分隔格式化字符串和我们要输出的变量。
当你运行这个程序,你会在控制台看到这些变量的值。你也可以尝试更改这些变量的值,或者添加更多的变量,来看看会发生什么。
二、注释
(一)、基础概念
在编程中,注释用于解释代码的功能,以帮助其他人(或者你自己)理解代码的工作方式。注释对于编写清晰、可读性强的代码至关重要。在C语言中,有两种方式可以添加注释:
- 单行注释:单行注释是通过
//
来创建的。//
后面的所有内容都会被视为注释,只到这一行结束。
// 这是一个单行注释
int a = 10; // 这也是一个单行注释
在这个例子中,int a = 10;
是一个语句,而// 这也是一个单行注释
是一个注释。在这行中的//
之后的所有内容都会被视为注释。
- 多行注释:多行注释是通过
/*
和*/
来创建的。这两个标记之间的所有内容都会被视为注释。
/* 这是一个
多行
注释 */
在这个例子中,/*
和*/
之间的所有内容都是注释。
在C语言中,注释是被编译器忽略的,所以你可以在注释中写任何你想写的内容,而不会影响到你的程序。使用注释来解释你的代码的功能和工作方式,能让你的代码更易于理解和维护。
以上就是C语言中关于注释的内容,如果你有任何问题,或者想要讨论其他的部分,欢迎随时告诉我。
(二)、巩固练习
1、在你的代码中添加适当的单行和多行注释。
这个练习的目标是在你的C代码中添加注释。注释是一种让你的代码更易于理解和维护的有效方式。C语言中有两种注释,单行注释和多行注释。
单行注释以 //
开头,从 //
开始,直到这一行的末尾,所有内容都会被编译器忽略。
多行注释以 /*
开头,并以 */
结束。这种类型的注释可以跨越多行,/*
和 */
之间的所有内容都会被编译器忽略。
下面是一个例子:
#include <stdio.h>
int main() {
// 这是一个单行注释
/* 这是一个
多行注释 */
printf("Hello, World!\n"); // 在行尾添加注释
return 0;
}
在这个程序中,我们在不同位置添加了注释:在一行的开始,跨越多行,以及在一行的结尾。
你的任务是,将这个代码复制到你的开发环境中,尝试添加、修改和删除注释,看看它们是如何影响(或者说不影响)程序输出的。
三、数据类型
(一)、基础概念
在C语言中,数据类型决定了变量或函数可以操作的数据的种类。以下是C语言中主要的数据类型:
- 整数类型:包括
int
、short
、long
和long long
,用于存储整数值。这些类型的大小和精度可能取决于特定的系统和编译器实现。 - 浮点类型:包括
float
和double
,用于存储小数值,double
提供的精度高于float
。 - 字符类型:
char
类型用于存储单个字符。 - 布尔类型:C99开始,C语言支持
_Bool
或者通过包含stdbool.h
文件来支持bool
,用于存储逻辑值(true
或false
)。 - 复合类型:包括数组、结构体、联合体和枚举类型。
- 派生类型:包括指针类型、数组类型、结构类型、共用体类型和函数类型。
- 空类型:
void
类型表明没有可用的值。它常常用在函数返回值为空的情况下。
这些类型都有它们各自的大小和值的范围,这些都是由编译器和用于编译的特定硬件决定的。
当你声明一个变量时,必须要给出它的类型,例如:
int myNumber;
float myFloat;
char myChar;
你也可以在声明时初始化变量:
int myNumber = 10;
float myFloat = 3.14;
char myChar = 'a';
以上就是C语言中的数据类型的基本介绍。如果你有任何问题,或者想要讨论其他的部分,欢迎随时告诉我。
(二)、巩固练习
1、使用各种数据类型(如整型、浮点型、字符型)创建变量,并试图修改和输出它们的值。
在这个练习中,你需要创建并修改不同类型的变量,并使用printf函数输出它们。以下是一个示例:
#include <stdio.h>
int main() {
int integerVar = 100;
float floatingVar = 331.79;
char charVar = 'G';
printf("初始值: 整型变量 = %d, 浮点型变量 = %.2f, 字符型变量 = %c\n", integerVar, floatingVar, charVar);
// 修改变量的值
integerVar = 200;
floatingVar = 500.32;
charVar = 'M';
printf("修改后的值: 整型变量 = %d, 浮点型变量 = %.2f, 字符型变量 = %c\n", integerVar, floatingVar, charVar);
return 0;
}
在这个程序中:
- 我们首先定义了三个变量:一个int类型的
integerVar
,一个float类型的floatingVar
,和一个char类型的charVar
。然后我们打印了这些变量的初始值。 - 接着,我们改变了这些变量的值,然后再次打印它们的值。
你可以复制这个代码,尝试更改变量的初始值和后来的值,看看程序是如何响应的。你也可以尝试添加更多类型的变量,比如双精度浮点型(double)和长整型(long)。
四、变量
(一)、基础概念
在C语言中,变量是用来存储数据的标识符(即名称)。声明变量时,你需要指定一个数据类型,这将决定变量可以存储的数据类型、大小以及数值范围。
以下是C语言变量声明的一般形式:
type variable_list;
在这里,type
必须是一个有效的C数据类型,variable_list
可以包含一个或多个以逗号分隔的标识符名称。例如:
int i, j, k;
char c, ch;
float f, salary;
double d;
变量可以在声明的时候被初始化(指定一个初始值)。它们被初始化的值必须与变量声明的类型兼容。这里有一些初始化变量的例子:
int i = 100;
float f = 3.14;
char ch = 'a';
以下是在声明时初始化多个变量的示例:
int i = 5, j = 6, k = 7;
char c = 'C', ch = 'H';
变量在声明后可以被赋值,例如:
int i; // 声明
i = 10; // 赋值
也可以在一行内完成变量的声明和赋值:
int i = 10; // 声明并赋值
这就是C语言中关于变量的基本信息。如果你有任何问题,或者想要讨论其他的部分,欢迎随时告诉我。
五、控制结构
(一)、基础概念
在C语言中,我们有几种控制结构可以控制程序流程。主要有三种控制结构:顺序结构、选择结构和循环结构。
- 顺序结构:这是最基本的控制结构。它按照程序代码的顺序执行指令。
- 选择结构:选择结构允许你根据条件选择执行特定的代码块。在C语言中,我们主要使用
if
、if...else
和switch
语句来进行选择。
// if 语句
if(condition) {
// 当条件为真时执行的代码
}
// if...else 语句
if(condition) {
// 当条件为真时执行的代码
} else {
// 当条件为假时执行的代码
}
// switch 语句
switch(expression) {
case constant1:
// 当表达式等于constant1时执行的代码
break;
case constant2:
// 当表达式等于constant2时执行的代码
break;
// 可以有任意数量的 case 语句
default:
// 当所有的 case 都不匹配时执行的代码
}
- 循环结构:循环结构允许你多次执行一段代码。在C语言中,我们主要使用
for
、while
和do...while
循环。
// for 循环
for(initialization; condition; increment/decrement) {
// 当条件为真时重复执行的代码
}
// while 循环
while(condition) {
// 当条件为真时重复执行的代码
}
// do...while 循环
do {
// 代码块
} while(condition); // 当条件为真时重复执行的代码
以上就是C语言中关于控制结构的基本信息。如果你有任何问题,或者想要讨论其他的部分,欢迎随时告诉我。
(二)、巩固练习
1、编写使用if、else if、else语句的程序。
在这个练习中,你将学习如何使用 if
、else if
和 else
语句来控制程序的流程。以下是一个示例:
#include <stdio.h>
int main() {
int score = 85;
if (score >= 90) {
printf("你的等级是A。\n");
} else if (score >= 80) {
printf("你的等级是B。\n");
} else if (score >= 70) {
printf("你的等级是C。\n");
} else if (score >= 60) {
printf("你的等级是D。\n");
} else {
printf("你的等级是F。\n");
}
return 0;
}
这个程序首先定义了一个名为 score
的变量,然后使用一系列的 if
、else if
和 else
语句来检查 score
的值,并根据 score
的值输出相应的等级。
你可以更改 score
的值,看看程序的输出是如何改变的。你也可以尝试添加更多的条件判断,或者改变现有条件判断的顺序,看看会发生什么。
2、编写一个for循环来打印出一系列数字。
在这个练习中,你将使用for循环来打印一系列的数字。以下是一个示例:
#include <stdio.h>
int main() {
int i;
for(i = 0; i < 10; i++) {
printf("%d\n", i);
}
return 0;
}
在这个程序中,我们定义了一个变量 i
,然后使用for循环使 i
从0增加到9。在每次循环中,我们都打印 i
的值。
for循环的语法包括三部分:
- 初始化语句(
i = 0
):在循环开始前执行,通常用于初始化计数器。 - 条件语句(
i < 10
):在每次循环开始时检查,如果为真,则执行循环体,如果为假,则退出循环。 - 更新语句(
i++
):在每次循环结束时执行,通常用于更新计数器。
你可以更改循环的初始化语句,条件语句和更新语句,看看它们是如何影响循环的。例如,你可以尝试将循环计数到100,或者以2的步长进行计数。
3、使用while和do-while循环打印出一系列数字。
这个练习将使用 while
和 do-while
循环来打印一系列的数字。以下是一个示例:
使用 while
循环:
#include <stdio.h>
int main() {
int i = 0;
while(i < 10) {
printf("%d\n", i);
i++;
}
return 0;
}
在这个程序中,我们定义了一个变量 i
,然后使用 while
循环使 i
从0增加到9。在每次循环中,我们都打印 i
的值。
使用 do-while
循环:
#include <stdio.h>
int main() {
int i = 0;
do {
printf("%d\n", i);
i++;
} while(i < 10);
return 0;
}
这个 do-while
循环的功能和 while
循环的功能基本相同。主要的区别在于,do-while
循环会先执行一次循环体,然后再检查条件。这意味着,即使条件一开始就是假的,循环体也至少会被执行一次。
你可以更改循环的条件和更新语句,看看它们是如何影响循环的。例如,你可以尝试将循环计数到100,或者以2的步长进行计数。
六、函数
(一)、基础概念
在C语言中,函数是一组被组织起来进行特定任务的语句集。所有的C程序都有至少一个函数,也就是主函数 main()
。函数可以提高代码的模块性和代码的重用性。
函数的定义
一个C语言的函数由一个函数头和一个函数体组成。以下是C语言中函数的格式:
return_type function_name( parameter list ) {
body of the function
}
return_type
:这定义了函数返回的类型。这可以是任何数据类型。function_name
:这是函数的实际名称。函数名和参数列表一起构成了函数签名。parameters
:参数就像是占位符。当函数被调用时,你传递一个值给参数,这个值被称为实际参数。参数列表包括参数的类型、顺序、数量。参数是可选的,也就是说,函数可能不包含参数。function body
:函数体包含一组定义函数任务的语句。
例如:
int add(int a, int b) { // 定义函数 add
int sum;
sum = a + b;
return sum; // 返回 sum
}
函数的调用
当我们在需要的时候,可以通过函数的名字来调用函数。例如:
int result = add(10, 20); // 调用函数 add
在这个例子中,函数 add
被调用,参数 10
和 20
被传递给函数,函数的返回值被赋值给变量 result
。
以上就是C语言中关于函数的基本信息。如果你有任何问题,或者想要讨论其他的部分,欢迎随时告诉我。
(二)、巩固练习
1、创建一个函数,将两个数字作为输入,返回它们的和。
这个练习中,你需要创建一个函数,接受两个参数,并返回它们的和。以下是一个示例:
#include <stdio.h>
// 定义函数
int add(int num1, int num2) {
int sum;
sum = num1 + num2;
return sum;
}
int main() {
int result;
// 调用函数
result = add(10, 20);
printf("10 + 20 = %d\n", result);
return 0;
}
在这个程序中:
- 我们首先定义了一个名为
add
的函数。这个函数接受两个int
类型的参数num1
和num2
,计算它们的和,并返回结果。 - 在
main
函数中,我们调用了add
函数,并将结果存储在result
变量中,然后打印出结果。
你可以尝试更改函数的参数和返回值,看看它们是如何影响程序的。例如,你可以尝试创建一个返回两个数差的函数,或者返回两个数乘积的函数。
2、编写一个递归函数,如计算阶乘。
在这个练习中,你将使用递归函数来计算阶乘。阶乘是一个常见的递归例子,因为一个数的阶乘可以定义为它与其减1的数的阶乘的乘积,这就创造了递归的可能性。
下面是一个计算阶乘的函数的例子:
#include <stdio.h>
// 定义阶乘函数
long long factorial(int n) {
if(n == 0) { // 基本情况
return 1;
} else { // 递归情况
return n * factorial(n - 1);
}
}
int main() {
int num = 5;
long long result = factorial(num);
printf("%d的阶乘是%lld\n", num, result);
return 0;
}
在这个程序中,我们定义了一个计算阶乘的函数 factorial
。这个函数有一个基本情况(当n
为0时,返回1)和一个递归情况(返回n
乘以n-1
的阶乘)。
然后,在main
函数中,我们调用了这个函数,传入了一个数字5,并将结果打印出来。
你可以试着改变 main
函数中的 num
变量的值,看看结果是如何变化的。你也可以试着编写其他递归函数,例如用于计算斐波那契数列的函数。
七、数组
(一)、基础概念
在C语言中,数组是由相同类型的元素组成的数据结构,这些元素在内存中连续存储。数组中的每个元素都可以通过数组名加索引来访问。
数组的声明
以下是C语言中数组声明的一般形式:
type arrayName [ arraySize ];
在这里,type
是数组中元素的数据类型,arrayName
是数组的名称,arraySize
指定了数组中元素的数量。
例如:
int myArray[10];
这段代码声明了一个包含10个整数的数组myArray
。
数组的初始化
当你声明一个数组,你可以同时进行初始化:
int myArray[5] = {1, 2, 3, 4, 5};
如果你省略掉数组的大小,编译器会自动计算:
int myArray[] = {1, 2, 3, 4, 5}; // 编译器会自动计算数组的大小
访问数组元素
数组中的元素是通过索引访问的,数组的索引从0开始,所以第一个元素的索引是0,第二个元素的索引是1,依此类推。
例如:
int myArray[5] = {1, 2, 3, 4, 5};
int first = myArray[0]; // 访问第一个元素
int second = myArray[1]; // 访问第二个元素
以上就是C语言中关于数组的基本信息。如果你有任何问题,或者想要讨论其他的部分,欢迎随时告诉我。
(二)、巩固练习
1、创建一个整型数组,填充并打印其内容。
在这个练习中,你将创建一个整数数组,填充它并打印其内容。以下是一个示例:
#include <stdio.h>
int main() {
int numbers[5] = {1, 2, 3, 4, 5};
int i;
// 使用for循环打印数组内容
for(i = 0; i < 5; i++) {
printf("%d\n", numbers[i]);
}
return 0;
}
在这个程序中,我们首先创建了一个包含5个整数的数组 numbers
,并初始化为1到5。然后我们使用 for
循环遍历数组并打印每个元素的值。
注意C中数组的索引从0开始,因此 numbers[0]
是数组的第一个元素, numbers[4]
是数组的最后一个元素。
你可以尝试更改数组的大小,更改初始化的值,或者更改打印元素的顺序,看看它们是如何影响程序的。例如,你可以尝试创建一个有10个元素的数组,或者将元素初始化为2的倍数,或者倒序打印元素。
2、尝试创建一个二维数组并打印其内容。
在这个练习中,你将创建一个二维数组,填充它并打印其内容。以下是一个示例:
#include <stdio.h>
int main() {
int numbers[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int i, j;
// 使用嵌套的for循环打印二维数组的内容
for(i = 0; i < 3; i++) {
for(j = 0; j < 3; j++) {
printf("%d ", numbers[i][j]);
}
printf("\n");
}
return 0;
}
在这个程序中,我们首先创建了一个3x3的二维整数数组 numbers
,并初始化为1到9。然后我们使用嵌套的 for
循环遍历二维数组并打印每个元素的值。
在C中,二维数组可以被视为一个表格,第一个索引表示行,第二个索引表示列。因此 numbers[0][0]
是数组的第一个元素, numbers[2][2]
是数组的最后一个元素。
你可以尝试更改数组的大小,更改初始化的值,或者更改打印元素的顺序,看看它们是如何影响程序的。例如,你可以尝试创建一个4x4的数组,或者将元素初始化为不同的值,或者倒序打印元素。
八、指针
(一)、基础概念
在C语言中,指针是一个非常重要的概念。一个指针变量指的是一个变量,其值为另一个变量的地址。换句话说,它“指向”了内存中的另一个位置。
声明指针
在C中,我们使用星号(*)来声明指针。以下是声明指针的一般格式:
type *var_name;
在这里,type
是指针的数据类型,var_name
是指针变量的名称。例如:
int *p; // 声明一个整型指针 p
char *ch; // 声明一个字符型指针 ch
初始化指针
指针可以在声明时被初始化。例如:
int var = 20;
int *p = &var;
在这个例子中,&var
是变量 var
的地址,所以 p
现在指向了 var
。
访问指针指向的值
当我们声明了一个指针并给它赋值后,我们可以通过解引用来访问它指向的值。解引用是通过在指针前面使用星号(*)来实现的。
例如:
int var = 20; // 声明一个整型变量 var
int *p = &var; // 声明并初始化一个指针 p
printf("%d", *p); // 输出 p 指向的值
这个代码将输出 20
,也就是 p
指向的值。
以上就是C语言中关于指针的基本信息。如果你有任何问题,或者想要讨论其他的部分,欢迎随时告诉我。
(二)、巩固练习
1、创建一个指针,使其指向一个变量,并试图通过指针修改变量的值。
在这个练习中,你将创建一个指针,使其指向一个变量,并试图通过指针修改变量的值。以下是一个示例:
#include <stdio.h>
int main() {
int number = 10;
int *p;
// 将指针p指向变量number
p = &number;
printf("原始值: %d\n", number);
// 通过指针p修改number的值
*p = 20;
printf("修改后的值: %d\n", number);
return 0;
}
在这个程序中,我们首先创建了一个整数变量 number
并初始化为10,然后我们创建了一个整数指针 p
,并使它指向 number
的地址(通过 &
运算符)。然后我们打印 number
的原始值,通过指针 p
修改 number
的值,再打印 number
的新值。
注意,*
运算符被用来访问或修改指针所指向的值。所以 *p = 20;
的意思是 “将 p
所指向的变量的值设置为20”。
你可以尝试更改变量的初始值,或者通过指针修改为不同的值,看看它们是如何影响程序的。例如,你可以尝试将 number
初始化为其他值,或者通过指针将其修改为其他值。
2、尝试动态分配内存,如使用malloc函数。
在这个练习中,你将学习如何使用 malloc
函数动态分配内存。以下是一个示例:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr;
int num = 5;
// 使用malloc动态分配内存
ptr = (int*)malloc(num * sizeof(int));
if (ptr == NULL) {
printf("内存分配失败。\n");
return 1;
}
for (int i = 0; i < num; i++) {
ptr[i] = i;
}
// 打印动态分配内存空间中的值
for (int i = 0; i < num; i++) {
printf("%d ", ptr[i]);
}
// 记得在使用完后释放内存
free(ptr);
return 0;
}
在这个程序中:
- 我们首先定义了一个整型指针
ptr
和一个整型变量num
,并将num
初始化为5。 - 然后我们使用
malloc
函数为num
个int
类型的变量分配了内存,sizeof(int)
用来获取int
类型的大小。 - 接着我们检查是否成功分配了内存。如果
ptr
为NULL
,则表示内存分配失败。 - 接着,我们使用循环将新分配的内存空间中的每个
int
初始化为它的索引值(也就是i)。 - 然后,我们使用循环打印新分配的内存空间中的每个
int
。 - 最后,我们使用
free
函数释放之前分配的内存,这是一个非常重要的步骤,如果忘记释放内存,会导致内存泄露,长期累积可能会使系统资源耗尽。
你可以尝试改变 num
的值,看看程序是如何为更多或更少的 int
分配内存的。你也可以尝试更改初始化的值,或者在释放内存之前尝试访问已分配的内存,看看会发生什么。
九、结构体
(一)、基础概念
在C语言中,结构体是一种用户定义的数据类型,允许你存储不同类型的数据项。它用于把一组逻辑相关的变量组织在一起。
定义结构体
定义结构体的一般格式如下:
struct structure_name {
member_type1 member_name1;
member_type2 member_name2;
member_type3 member_name3;
...
};
例如,我们可以定义一个名为 Student
的结构体来存储学生的信息:
struct Student {
char name[50];
int roll;
float marks;
};
在这个结构体中,name
、roll
和 marks
是结构体的成员。
创建结构体变量
定义了结构体类型后,我们就可以创建该类型的变量:
struct Student stu;
在这里,stu
是 Student
结构体的一个实例。
访问结构体成员
我们可以使用点运算符 .
来访问结构体的成员:
strcpy(stu.name, "Tom");
stu.roll = 123;
stu.marks = 89.5;
以上的代码将 “Tom” 赋给了 stu
的 name
成员,123
赋给了 roll
成员,89.5
赋给了 marks
成员。
以上就是C语言中关于结构体的基本信息。如果你有任何问题,或者想要讨论其他的部分,欢迎随时告诉我。
(二)、巩固练习
1、创建一个结构体,如"学生",包含几个成员如姓名、年龄、分数,并创建该结构体的一个实例。
在这个练习中,你将创建一个名为 “学生” 的结构体,并添加一些成员变量,如姓名、年龄和分数。然后你将创建这个结构体的一个实例。以下是一个示例:
#include <stdio.h>
// 定义结构体
struct Student {
char name[50];
int age;
int score;
};
int main() {
// 创建结构体实例并初始化
struct Student student1 = {"张三", 20, 85};
printf("姓名:%s\n", student1.name);
printf("年龄:%d\n", student1.age);
printf("分数:%d\n", student1.score);
return 0;
}
在这个程序中,我们首先定义了一个名为 “Student” 的结构体,它包含了一个字符数组(用于存储名字)、一个整数(用于存储年龄)和另一个整数(用于存储分数)。然后,我们在 main
函数中创建了这个结构体的一个实例 student1
,并进行了初始化。
注意,结构体成员可以通过.
运算符来访问。所以 student1.name
、student1.age
和 student1.score
分别表示学生的名字、年龄和分数。
你可以试着添加更多的成员到结构体中,或者创建更多的结构体实例,看看它们是如何工作的。例如,你可以添加一个 “性别” 成员到结构体中,或者创建一个 “student2” 实例。
十、标准库函数
(一)、基础概念
C语言提供了大量的标准库函数,这些函数可以进行各种操作,如输入/输出处理、数学计算、字符处理、时间/日期处理等。这些函数被定义在不同的头文件中,你可以在需要使用这些函数的时候包含相应的头文件。
以下是一些常见的C语言标准库函数和它们所在的头文件:
- 输入/输出函数(stdio.h):包含了用于文件输入和输出的函数,如
printf()
,scanf()
,fopen()
,fclose()
等。 - 数学函数(math.h):包含了数学计算的函数,如
sqrt()
,pow()
,sin()
,cos()
,log()
等。 - 字符和字符串处理函数(string.h / ctype.h):
string.h
包含了字符串处理的函数,如strcpy()
,strcat()
,strlen()
,strcmp()
等。ctype.h
包含了字符处理的函数,如isalpha()
,isdigit()
,islower()
,isupper()
,tolower()
,toupper()
等。 - 时间日期函数(time.h):包含了处理日期和时间的函数,如
time()
,ctime()
,strftime()
等。
以上只是一小部分C语言的标准库函数,实际上,C语言的标准库函数非常丰富,可以满足绝大部分的编程需求。
以下是使用标准库函数的一个例子,我们使用 math.h
中的 sqrt()
函数来计算一个数的平方根:
#include <stdio.h>
#include <math.h>
int main() {
double num = 9.0;
double squareRoot = sqrt(num);
printf("The square root of %.2f is %.2f\n", num, squareRoot);
return 0;
}
以上就是C语言中关于标准库函数的基本信息。如果你有任何问题,或者想要讨论其他的部分,欢迎随时告诉我。
(二)、巩固练习
1、编写一个程序,使用math.h中的函数,如pow和sqrt。
在这个练习中,你将学习如何使用 math.h
库中的函数。以下是一个示例,使用了 pow
(幂运算)和 sqrt
(开方)函数:
#include <stdio.h>
#include <math.h>
int main() {
double base = 2.0;
double exponent = 3.0;
double result;
// 使用pow函数
result = pow(base, exponent);
printf("%lf的%lf次方等于:%lf\n", base, exponent, result);
// 使用sqrt函数
result = sqrt(result);
printf("上面结果的平方根等于:%lf\n", result);
return 0;
}
在这个程序中,我们首先定义了两个双精度浮点变量 base
和 exponent
,并分别初始化为2.0和3.0。然后我们使用 pow
函数计算 base
的 exponent
次方,并将结果存储在 result
中。然后我们打印 result
的值。接着,我们使用 sqrt
函数计算 result
的平方根,并再次打印 result
的值。
注意,pow
函数的第一个参数是基数,第二个参数是指数。sqrt
函数只有一个参数,即要计算平方根的数。这两个函数都返回一个 double
类型的结果。
你可以尝试改变 base
和 exponent
的值,看看它们是如何影响结果的。你也可以尝试使用 math.h
中的其他函数,例如 sin
、cos
、tan
等。
2、使用string.h中的函数,如strcpy和strcmp。
在这个练习中,你将学习如何使用 string.h
库中的函数。以下是一个示例,使用了 strcpy
(复制字符串)和 strcmp
(比较字符串)函数:
#include <stdio.h>
#include <string.h>
int main() {
char str1[20] = "Hello";
char str2[20] = "World";
char str3[20];
// 使用strcpy函数复制字符串
strcpy(str3, str1);
printf("复制后的字符串是:%s\n", str3);
// 使用strcmp函数比较字符串
if(strcmp(str1, str2) == 0) {
printf("字符串str1和str2是相同的。\n");
} else {
printf("字符串str1和str2是不同的。\n");
}
return 0;
}
在这个程序中,我们首先定义了三个字符数组 str1
、str2
和 str3
,并初始化 str1
和 str2
。然后我们使用 strcpy
函数将 str1
的内容复制到 str3
,并打印 str3
的内容。接着,我们使用 strcmp
函数比较 str1
和 str2
的内容。如果 strcmp
返回0,那么 str1
和 str2
是相同的,否则它们是不同的。
注意,strcpy
函数的第一个参数是目标字符串,第二个参数是源字符串。strcmp
函数的两个参数是要比较的两个字符串。
你可以尝试更改 str1
和 str2
的值,看看它们是如何影响结果的。你也可以尝试使用 string.h
中的其他函数,例如 strcat
(连接字符串)、strlen
(获取字符串长度)等。
十一、内存管理
(一)、基础概念
在C语言中,内存管理是一个重要的主题。你可以动态地在运行时分配和释放内存。C提供了几个函数,用于动态内存管理,这些函数在标准库stdlib.h
中定义。
以下是C语言中的四个主要的内存管理函数:
- malloc():
malloc
函数在堆上动态地分配一定数量的连续内存。函数的原型如下:
void* malloc(size_t size);
malloc
接受一个参数,这个参数是你希望分配的内存的字节数,然后返回一个指向新分配内存的指针,或者在无法分配内存时返回NULL。
- calloc():
calloc
函数在堆上动态地分配一定数量的连续内存,并将其初始化为0。函数的原型如下:
void* calloc(size_t num, size_t size);
calloc
接受两个参数,第一个参数是你希望分配的元素的数量,第二个参数是每个元素的大小。calloc
返回一个指向新分配内存的指针,或者在无法分配内存时返回NULL。
- realloc():
realloc
函数更改已分配内存区域的大小。函数的原型如下:
void* realloc(void* ptr, size_t size);
realloc
接受两个参数,一个是先前由malloc
、calloc
或realloc
分配的内存区域的指针,另一个是新的大小。realloc
返回一个指向新分配内存的指针,或者在无法分配内存时返回NULL。
- free():
free
函数释放先前由malloc
、calloc
或realloc
分配的内存区域。函数的原型如下:
void free(void* ptr);
free
接受一个参数,这个参数是先前由malloc
、calloc
或realloc
分配的内存区域的指针。
以下是一个使用malloc
和free
的示例:
#include <stdio.h>
#include <stdlib.h>
int main() {
int* ptr = malloc(sizeof(int)); // 分配内存
if (ptr == NULL) { // 检查是否成功分配内存
printf("Memory allocation failed\n");
return -1;
}
*ptr = 10; // 在分配的内存上赋值
printf("Value: %d\n", *ptr);
free(ptr); // 释放内存
return 0;
}
以上就是C语言中关于内存管理的基本信息。如果你有任何问题,或者想要讨论其他的部分,欢迎随时告诉我。
(二)、巩固练习
1、使用malloc或calloc函数分配一些内存,然后使用free函数释放它。
在这个练习中,你将学习如何使用 malloc
和 calloc
函数分配内存,以及如何使用 free
函数释放内存。以下是一个示例:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr1, *ptr2;
int n = 5;
// 使用malloc分配内存
ptr1 = (int*)malloc(n * sizeof(int));
if (ptr1 == NULL) {
printf("内存分配失败。\n");
return 1;
}
printf("使用malloc分配的内存地址:%p\n", ptr1);
// 使用calloc分配内存
ptr2 = (int*)calloc(n, sizeof(int));
if (ptr2 == NULL) {
printf("内存分配失败。\n");
return 1;
}
printf("使用calloc分配的内存地址:%p\n", ptr2);
// 释放分配的内存
free(ptr1);
free(ptr2);
return 0;
}
在这个程序中,我们首先定义了两个整型指针 ptr1
和 ptr2
,以及一个整型变量 n
,并将 n
初始化为5。然后我们使用 malloc
函数为 n
个 int
类型的变量分配了内存,sizeof(int)
用来获取 int
类型的大小。接着我们检查是否成功分配了内存。如果 ptr1
为 NULL
,则表示内存分配失败。然后,我们使用 calloc
函数也为 n
个 int
类型的变量分配了内存,并进行了同样的检查。最后,我们使用 free
函数释放了之前分配的内存。
注意,malloc
和 calloc
的主要区别在于,malloc
分配的内存中的初始值是未定义的,而 calloc
分配的内存中的初始值都设置为0。
你可以尝试改变 n
的值,看看程序是如何为更多或更少的 int
分配内存的。你也可以尝试不释放内存,看看会发生什么(在实际编程中,忘记释放内存会导致内存泄漏,这是需要避免的)。
十二、综合习题
(一)、基础习题
当然,这是一道结合了以上知识点的综合题:
请编写一个程序,创建一个学生的结构体数组,结构体中包括学生的名字、年龄和分数。然后创建一个函数,接收这个结构体数组和数组的大小作为参数,函数内部为这个数组动态分配内存并对每个学生的信息进行初始化。最后在主函数中调用这个函数,并打印出每个学生的信息。最后,记得释放分配的内存。
以下是一个大概的程序结构:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 学生结构体
struct Student {
char name[50];
int age;
int score;
};
// 初始化函数
void initialize(struct Student* students, int size) {
// TODO: 在这里填写代码,使用malloc或calloc为students分配内存,并初始化学生信息
}
// 打印学生信息函数
void print_students(struct Student* students, int size) {
// TODO: 在这里填写代码,打印每个学生的信息
}
int main() {
int size = 3; // 可以改变学生数量
struct Student* students;
initialize(students, size);
print_students(students, size);
// TODO: 在这里填写代码,释放分配的内存
return 0;
}
这是一个比较复杂的程序,涉及到的知识点有:结构体、动态内存分配、函数和数组。你可以按照程序的要求,填写缺失的代码部分。当你编写代码的时候,不要忘记检查动态内存分配是否成功(即检查返回的指针是否为NULL),并在程序结束时释放分配的内存。
(二)进阶习题
1、题目
当然,这里有一个更具有挑战性且具有实际意义的问题。
假设你正在为一家图书馆编写一个图书管理系统。以下是你需要实现的功能:
- 定义一个"图书"的结构体,每本图书包含属性:书名、作者、出版年份和是否被借出(用一个整型变量表示,0代表没有被借出,1代表已被借出)。
- 创建一个函数,能够向图书馆的藏书中添加新的图书。每次添加新的图书时,需要动态分配内存来保存新的图书信息。
- 创建一个函数,能够根据书名查找图书。如果找到了这本书,就打印出这本书的所有信息。
- 创建一个函数,能够借出图书。该函数接收一个书名作为参数,如果这本书在库存中且没有被借出,就将其标记为已被借出。
- 在主函数中,提供一个菜单给用户,用户可以选择添加图书、查找图书或借出图书。
- 在退出程序前,记得释放所有动态分配的内存。
注意:这个问题需要你使用到结构体、动态内存分配(malloc或calloc)、函数、数组和控制流语句。你可能还需要使用到一些字符串处理函数,比如strcpy和strcmp,以处理和比较书名。
当你设计这个程序时,需要确保所有的用户输入都被正确处理,包括对图书数量的限制和对错误输入的处理。这个问题的挑战在于它需要你整合多个知识点,并设计出一个实际可用的系统。
2、示例答案
以下是我根据上述问题需求编写的一个简单的图书管理系统。这个程序使用了一个结构体数组来存储图书,使用了malloc来动态分配内存,同时使用了几个函数来执行不同的任务:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_BOOK_NAME 50
#define MAX_AUTHOR_NAME 50
// 图书结构体
typedef struct {
char bookName[MAX_BOOK_NAME];
char authorName[MAX_AUTHOR_NAME];
int publicationYear;
int isBorrowed;
} Book;
// 图书数组和图书数量
Book* library = NULL;
int bookCount = 0;
// 添加图书
void addBook(char* bookName, char* authorName, int publicationYear) {
library = (Book*)realloc(library, (bookCount + 1) * sizeof(Book));
strcpy(library[bookCount].bookName, bookName);
strcpy(library[bookCount].authorName, authorName);
library[bookCount].publicationYear = publicationYear;
library[bookCount].isBorrowed = 0;
bookCount++;
}
// 查找图书
void findBook(char* bookName) {
for (int i = 0; i < bookCount; i++) {
if (strcmp(library[i].bookName, bookName) == 0) {
printf("书名:%s\n", library[i].bookName);
printf("作者:%s\n", library[i].authorName);
printf("出版年份:%d\n", library[i].publicationYear);
printf("是否借出:%s\n", library[i].isBorrowed ? "是" : "否");
return;
}
}
printf("找不到名为%s的图书。\n", bookName);
}
// 借出图书
void borrowBook(char* bookName) {
for (int i = 0; i < bookCount; i++) {
if (strcmp(library[i].bookName, bookName) == 0) {
if (!library[i].isBorrowed) {
library[i].isBorrowed = 1;
printf("图书%s已借出。\n", bookName);
} else {
printf("图书%s已被借出。\n", bookName);
}
return;
}
}
printf("找不到名为%s的图书。\n", bookName);
}
// 主函数
int main() {
addBook("书名1", "作者1", 2000);
addBook("书名2", "作者2", 2005);
addBook("书名3", "作者3", 2010);
findBook("书名1");
findBook("书名4");
borrowBook("书名2");
borrowBook("书名2");
borrowBook("书名4");
free(library); // 不要忘记释放动态分配的内存
return 0;
}
注意:这只是一个基础的图书管理系统,它没有提供用户界面,所有的操作都是在代码中直接调用的。如果你想让这个程序更实用,你可以添加一个用户界面,让用户可以在运行时添加、查找和借出图书。你还可以添加更多的功能,比如删除图书、返回图书,等等。
3、答案详解
当然可以,我将一步一步地解释这个程序。
- 首先,我们定义了一个叫做Book的结构体,其中包含了书名、作者名、出版年份以及是否已经被借出的信息。
typedef struct {
char bookName[MAX_BOOK_NAME];
char authorName[MAX_AUTHOR_NAME];
int publicationYear;
int isBorrowed;
} Book;
- 然后我们定义了一个指向Book类型的指针
library
和一个表示图书数量的变量bookCount
。library
将作为一个动态数组,用于存储所有的图书信息。
Book* library = NULL;
int bookCount = 0;
addBook
函数用于添加新的图书到图书库。首先,我们通过realloc
函数扩展library
数组的大小,然后将新图书的信息复制到新的位置,最后增加图书数量。
void addBook(char* bookName, char* authorName, int publicationYear) {
library = (Book*)realloc(library, (bookCount + 1) * sizeof(Book));
strcpy(library[bookCount].bookName, bookName);
strcpy(library[bookCount].authorName, authorName);
library[bookCount].publicationYear = publicationYear;
library[bookCount].isBorrowed = 0;
bookCount++;
}
findBook
函数用于查找图书。它会遍历library
数组,如果找到了匹配的图书,就会打印出图书的信息。
void findBook(char* bookName) {
for (int i = 0; i < bookCount; i++) {
if (strcmp(library[i].bookName, bookName) == 0) {
printf("书名:%s\n", library[i].bookName);
printf("作者:%s\n", library[i].authorName);
printf("出版年份:%d\n", library[i].publicationYear);
printf("是否借出:%s\n", library[i].isBorrowed ? "是" : "否");
return;
}
}
printf("找不到名为%s的图书。\n", bookName);
}
borrowBook
函数用于借阅图书。同样,它会遍历library
数组,如果找到了匹配的图书并且图书没有被借出,就将其标记为已借出。
void borrowBook(char* bookName) {
for (int i = 0; i < bookCount; i++) {
if (strcmp(library[i].bookName, bookName) == 0) {
if (!library[i].isBorrowed) {
library[i].isBorrowed = 1;
printf("图书%s已借出。\n", bookName);
} else {
printf("图书%s已被借出。\n", bookName);
}
return;
}
}
printf("找不到名为%s的图书。\n", bookName);
}
- 在
main
函数中,我们添加了一些图书,并进行了查找和借阅的操作。
int main() {
addBook("书名1", "作者1", 2000);
addBook("书名2", "作者2", 2005);
addBook("书名3", "作者3", 2010);
findBook("书名1");
findBook("书名4");
borrowBook("书名2");
borrowBook("书名2");
borrowBook("书名4");
free(library); // 不要忘记释放动态分配的内存
return 0;
}
- 在程序结束时,我们用
free
函数释放了通过malloc
分配的内存,以防止内存泄漏。
free(library); // 不要忘记释放动态分配的内存
整个程序都基于这些函数来完成对图书的添加、查找和借阅操作,通过动态内存分配,能够根据需要来扩展图书的存储空间。这是一个简单的命令行界面的图书管理系统,如果你想让它变得更复杂和实用,你可以添加更多的功能和改进用户界面。