🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
前言
在计算机考研中,C语言的重要性不可小觑。作为一种基础且强大的编程语言,C语言不仅是许多高校计算机专业本科生必修的课程之一,也是研究生入学考试中的重点考察内容。掌握C语言能够帮助考生深入理解程序设计的基本概念和原理,比如数据类型、控制结构、函数、指针等,并能培养良好的编程习惯与问题解决能力。此外,C语言还是学习操作系统、编译原理等高级课程的基础,这些课程的知识点往往会在研究生入学考试中出现。因此,具备扎实的C语言功底不>仅有助于提高考研成绩,也为将来从事计算机科学研究或工程实践打下坚实的基础。
第一章-初识C语言
什么是C语言
- 简介
C语言是一种较早的程序设计语言,诞生于1972年的贝尔实验室。1972 年,Dennis Ritchie 设计了C语言,它继承了B语言的许多思想,并加入了数据类型的概念及其他特性。 尽管C 语言是与 UNIX 操作系统一起被开发出来的,但它不只支持UNIX。
C是一种通用(广泛可用)的编程语言。
广泛应用于底层开发。C语言能以简易的方式编译、处理低级存储器。C语言是仅产生少量的机器语言以及不需要任何运行环境支持便能运行的高效率程序设计语言。尽管C语言提供了许多低级处理的功能,但仍然保持着跨平台的特性,以一个标准规格写出的C语言程序可在包括类似嵌入式处理器以及超级计算机等作业平台的许多计算机平台上进行编译。
- 计算机语言的发展
过程:低级>>>高级
二进制指令(10010)>>>汇编指令(助记符)>>>B语言>>>C语言(高级语言)
-
C语言的国际标准
- ANSI C
- C89
- C99
- C11
-
C语言编译器
- Clang
- GCC
- WIN-TC
- MSVC
- Turbo C
Hello World
- 环境准备 编译器:Microsoft Visual Studio Community 2022 (64 位) - Current
版本 :17.11.1
编译程序快捷键:F5
c
/*
include:表示包含某一个文件
stdio:标准输入输出
-- std表示标准,i表示input(输入),o表示output(输出)
int : 为整数类型
main(){}: 表示程序的主入口,有且只有一个
printf();输出函数
*/
#include <stdio.h>
int main(){
printf("Hello World\n");
return 0;
}
数据类型
- 前置扩展知识 计算机中的单位:bit(比特)>>byte(字节)>>KB(千字节)>>MB(兆字节)>>GB(千兆字节)>>TB(太字节)...
换算关系:
1 字节(byte) = 8 位(bits)
1 千字节(KB) = 1024 字节(bytes)
1 兆字节(MB) = 1024 千字节(KB)
1 千兆字节(GB) = 1024 兆字节(MB)
1 太字节(TB) = 1024 千兆字节(GB)
数据类型 | 中文名称 | 长度(单位:字节) |
---|---|---|
char | 字符型 | 1 |
short | 短整型 | 2 |
int | 整型 | 4 |
long | 长整型 | 4 |
long long | 更长整形 | 8 |
float | 单精度浮点数 | 4 |
double | 双精度浮点数 | 8 |
- sizeof()函数
c
/*
sizeof()函数可用于查询数据类型的长度
*/
#include <stdio.h>
int main() {
printf("%d\n", sizeof(char));
printf("%d\n", sizeof(short));
printf("%d\n", sizeof(int));
printf("%d\n", sizeof(long));
printf("%d\n", sizeof(long long ));
printf("%d\n", sizeof(char));
printf("%d\n", sizeof(float));
printf("%d\n", sizeof(double));
return 0;
}
变量、常量
变量:定义的值在程序中会发生变化的。如人的体重,薪资等。
常量:定义的值在程序中不会发生变化的。如人的身份证号、血型等。
建议:在定义变量时,都给变量进行初始化,因为如果定义了变量后没有给变量进行初始化,系统将会随机给变量赋一个随机值,这个随机值可能会影响到程序运行结果。
- 变量
变量又分为局部变量和全局变量
局部变量:在大括号{}的内部定义的变量
全局变量:在大括号{}的外部定义的变量
注意点:定义变量时,在同一范围内不能多次定义相同变量名的变量
c
// 变量的定义与使用
#include <stdio.h>
int a = 10; // 全局变量
int main() {
short age = 20; // 年龄(局部变量)
int height = 180; // 身高
float weight = 45.4; // 体重
printf("年龄:%d,身高:%d,体重:%.2f",age,height,weight); // .2f表示保留2位小数
return 0;
}
c
/*
局部变量和全局变量
1:正常情况下,如果{}内和和{}外,有相同变量名的变量时,程序编译时优先使用局部变量
*/
#include <stdio.h>
int a = 10; // 全局变量
int main() {
int a = 1; // 局部变量
printf("a=%d\n",a);
return 0;
}
- scanf()函数
c
/*
scanf()函数作用:接收用户通过键盘输入的值
注意点:如使用VsCode调用scanf()函数时,编译器会提示安全警告,并建议使用scanf_s函数,而scanf_s函数
并不是C语言自带的函数,而是VsCode编辑器自编译的,所以不建议使用scanf_s()
*/
#define _CRT_SECURE_NO_WARNINGS // 预处理安全警告
#include <stdio.h>
int main() {
// 求和功能示例
int num1 = 0;
int num2 = 0;
scanf("%d %d", &num1, &num2);
printf("和为%d\n", num1+num2);
return 0;
}
- _CRT_SECURE_NO_WARNINGS 快速添加方式
1、在VsCode软件中,创建后缀为点C的文件,其中都是复制一个名为newc++file.cpp文件的来的。所以只要将需要的格式预处理警告格式写入在该文件中,下次在创建C语言文件时,VsCode就会自动生成。
2、文件添加内容为:#define _CRT_SECURE_NO_WARNINGS
3、注意:如果在添加完成后,保存存在权限问题,可以将该文件复制到某处,添加内容后,在将文件替换即解决。
- 文件路径
- 添加内容
- 预览
变量的作用域和生命周期
- 作用域 作用域(scope)时程序设计概念。一般来说,一段程序代码中所用到的名字并不总是有效/可用的,而限制这个名字的可用性的代码范围就是这个名字的作用域范围。
局部变量的作用域范围:时变量所在的局部范围
全局变量的作用域时整个工程
c
#define _CRT_SECURE_NO_WARNINGS // 预处理安全警告
#include <stdio.h>
int a = 10;
int main()
{
{
int a = 101;
printf("a=%d", a); // 打印局部
}
printf("a=%d", a); // 打印全局
return 0;
}
- 生命周期
变量的生命周期是指:变量的创建到变量的销毁之间的一个时间段
局部变量的生命周期:进入作用域生命周期开始,出作用域生命周期结束
全局变量的生命周期:整个程序的生命周期
常量
> C语言中的常量可分为: > > 字面常量 > > const修饰的常变量 > > #define定义的标识符变量 > > 枚举常量 >
- 枚举常量
c
enum colors {
red,
yellow,
black
};
- 字面常量
c
#define _CRT_SECURE_NO_WARNINGS // 预处理安全警告
#include <stdio.h>
int main()
{
3;
3.33;
'a';
return 0;
}
- const修饰的常变量
c
#define _CRT_SECURE_NO_WARNINGS // 预处理安全警告
#include <stdio.h>
int main()
{
const int age = 10;
printf("age=%d", age);
age = 11; // 提示错误,原因:常量是不能修改的
return 0;
}
- #define定义的标识符变量
c
#define _CRT_SECURE_NO_WARNINGS // 预处理安全警告
#include <stdio.h>
#define MAN 1
int main()
{
printf("MAN=%d",MAN);
return 0;
}
字符串
由双引号引起来的一串字符串称为字符串字面值,简称字符串。
注意:字符串的结束标志是一个\0的转义字符,在计算字符串的时候\0是结束标志,不算做字符串内容。
c
// 注意:C语言中没有字符串数据类型,只有char字符类型
#define _CRT_SECURE_NO_WARNINGS // 预处理安全警告
#include <stdio.h>
int main()
{
char str1[] = "abcf";
char str2[] = { 'a','b','c','d','\0'}; // 字符结尾是不会以\0结束的,所以需要自己添加,不然会产生乱码
printf("str1=%s", str1);
printf("str2=%s",str2);
return 0;
}
转义字符
转义字符 | 含义 |
---|---|
? | 在书写连续多个问号时使用,防止它们被解析成三字母词 |
' | 用于表示字符常量' |
'' | 用于表示一个字符串内部的双引号 |
\ | 用于表示反斜杠 |
\a | 警告字符(触发电脑蜂鸣) |
\b | 退格符 |
\f | 换页符 |
\n | 换行 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ddd | ddd表示1~3个八进制的数字,如:\130 X |
\xdd | dd表示2个十六进制数字,如 \x30 0 |
- 打印占位符
占位符 | 打印含义 |
---|---|
%d | 整数 |
%c | 字符 |
%s | 字符串 |
%f | float类型 |
%lf | double类型 |
%zu | sizeof的返回值 |
- 例题
c
/*
存在转义:
\t
\62 >>> 表示2
\t
*/
注释
单行注释格式:// 注释体
多行注释格式:/注释体/
操作符
算术操作符
减减乘除取余(只能是整数)
注意:整数除法结果为整数,浮点数除法结果为浮点数
移位操作符:
>> <<
位操作符
& ^ |
赋值操作符=
单目操作符(只有一个操作数的操作符)! (0为false,非0为true)
- (负值)
++ (后置:,先使用在++;前置:先++,在使用)
--
(类型) 强转 如:int a = (int )3.11;
sizeof (变量占内存空间大小)
c
/*
数组arr站内存空间为40:
初始化数组时分配了10个空,数组中赋值为0(整数),占4个字节
4*10=40
*/
#include <stdio.h>
int main(){
int arr[10] = {0};
printf("%d",sizeof(arr));
return 0;
}
关系操作符
=
<=
!= 不等于
== 等于
=== 全等
逻辑操作符:&& 逻辑与
|| 逻辑或
条件操作符(三目操作符):expl? true:false
逗号表达式(从左向右以此计算):,,(最后一个表达式结果,就是赋值)
常见关键字
![](https://img-blog.csdnimg.cn/img_convert/ad59982e68ad4d198e5b4cc264040cec.png)
- typedef
简介:类型定义,理解为类型重命名
c
#include <stdio.h>
// 将int 类型取别名为 a
typedef int a;
int main(){
a num=11;
printf("%d",num);
return 0;
}
- static
作用:
修饰局部变量:static修饰局部变量时,变量不销毁
修饰全局变量::使用static修饰全局变量,全局变量不能被其他文件引用,(理解:作用域变小了)
修饰函数:被static修饰时,外部链接属性变为内部链接属性。
注意:函数本身具有外部链接属性
c
#include <stdio.h>
void test(){
int a = 1;
a++;
printf("%d",a);
}
int main(){
int i =0;
while(i<=10){
test();
i++;
}
return;
}
c
#include <stdio.h>
void test(){
static int a = 1; // 修饰局部变量
a++;
printf("%d\t",a);
}
int main(){
int i =0;
while(i<=10){
test();
i++;
}
return;
}
- register(寄存器)
寄存器集成在CPU上
扩展:
执行速度:寄存器》高速缓存》内存》硬盘
c
#include <stdio.h>
int main(){
// 寄存器变量
register int num = 33; // 建议将整型33存放在寄存器中
printf("%d",333);
return 0;
}
#define定义标识常量和宏
> 宏:有参数 >
c
#include <stdio.h>
// 标识常量
#define NUM 100
// #define 定义宏
#define ADD(num1,num2)((num1)+(num2))
int main(){
printf("%d",NUM);
printf("\n");
printf("%d",ADD(1,1));
return 0;
}
指针
> 扩展: > > 内存:计算机中的程序都是在内存中运行的。 > > 内存会划分为一个个内存单元(一个单元是1字节),每个内存单元都有一个编号。 >
c
// 取地址操作符:&
// %p ,展示变量的内存地址
#include <stdio.h>
int main(){
int num = 10;
printf("%p",&num);
return 0;
}
- 指针变量
c
// 编号==地址==指针
/*
int*解释:
*说明变量是指针变量,
int说明指向的变量是int类型
解引用操作符:
*变量:通过变量中存放的地址,找到p所指向的对象
*/
int main(){
int num = 10;
// num2为指针变量,num2中存储的为num的内存地址
int* num2 = #
*num2 = 100;
printf("%d",num);// 此时的num值就为100了。
}
- 指针变量的大小
指针变量的大小取决于电脑的位数:
32位:4字节
64位:8字节
结构体
> 结构体:定义多个类型的集合。 >
c
#include <stdio.h>
// 定义结构体
struct Stu{
char name[20];
int age;
};
int main(){
// 创建Stu结构体
struct Stu stu = {"张三",11};
//打印
printf("%s,%d",stu.name,stu.age);
return 0;
}
- ->符号
结构体指针变量->成员名
c
#include <stdio.h>
// 定义结构体
struct Stu{
char name[20];
int age;
};
int main(){
// 创建Stu结构体
struct Stu stu = {"张三",11};
struct Stu* stu1 = &stu;
printf("%s,,,,%d",stu1->name,stu1->age);
return 0;
}
第二章-C语言初阶
什么是语句
C语言可以分为以下五类:
表达式语句
函数调用语句
控制语句
复合语句
空语句
扩展:
结构化分为:顺序结构、选择结构、循环结构
分支语句
if语句
格式:
if(表达式){
执行语句
}
注意:
if和else最近匹配。
c
#include <stdio.h>
int main(){
int num = 10;
if(num==10){
printf("true");
}else{
printf("false");
}
return 0;
}
c
// 结果为:不输出任何信息
int main(){
int a = 1;
int b = 10;
if(a==11)
if (b==10)
printf("1");
else
printf("2");
return 0;
}
- 练习:1到100的奇数
c
#include <stdio.h>
int main(){
for(int i =1;i<=100;i++){
if(i%2==1)
printf("%d",i);
}
return 0;
}
switch语句
格式:
switch(变量){
case 变量值: 执行语句; break; default: 执行语句;
}
c
#include <stdio.h>
int main(){
int num = 1;
switch (num)
{
case 1:
printf("true");
break;
default:
printf("false");
break;
}
return 0;
}
循环语句
break :退出循环
continue:跳出循环
while
格式:
while(表达式){
执行语句;
}
c
#include <stdio.h>
int main(){
int num = 1;
while(num <=10){
printf("%d",num);
num+=2;
}
return 0;
}
- getchar()
理解:通过键盘输入连续的字符,getchar()会一个一个字符读取,直到读取到回车结束。(通过循环实现)
循环结束条件:(ch = getchar()) != EOF
用于获取键盘输入的字符,
接收类型使用int
注意:
EOF为-1,可以结束键盘输入字符过程
- putchar()
打印键盘输出的字符
c
#include <stdio.h>
int main(){
int num = getchar();
putchar(num);
return 0;
}
- 理解
for
语法:
for(变量;表达式;表达式){
执行语句;
}
c
// 02468
#include <stdio.h>
int main(){
for (int i = 0;i<10;i+=2){
printf("%d",i);
}
return 0;
}
- for循环的变形
c
// 死循环
for(;;){
// 执行语句;
}
do while
执行次数:至少执行一次
语法:
do
{
循环体;
}
while(表达式);
c
#include <stdio.h>
int main(){
int num = 1;
do{
printf("%d",num);
num++;
}
while(num<=10);
return 0;
}
二分查找
字符替换练习
密码判断练习
GOTO语句
作用:主要用于跳出深层次循环体
语法:
标记:
语句;
goto 标记;
- 电脑关机案例
cmd:
60秒关机: shutdown -s -t 60
取消关机:shutdown -a
c
#include <stdio.h>
// system函数引用的库文件
#include <stdlib.h>
int main(){
char input[20] = {0};
system("shutdown -s -t 150");
tip:
printf("输入v后,取消关机");
scanf("%s",input);
// strcmp()函数用于判断两个字符串是否相等
if(strcmp("v",input)==0){
system("shutdown -a");
}else{
goto tip;
}
return 0;
}
函数
什么是函数
什么是函数:
由一个或多个语句块组成,它负责完成某项特定任务。
函数的分类:
库函数
自定义函数
语法:
返回类型 函数名(参数){
函数体;
return 返回值;
}
- 找最大值例子
c
int get_max(int x, int y ){
return (x>y?x:y);
}
函数调用
传值调用:形参为实参的真实值,对形参的修改不会影响实参
传址调佣:形参为实参的内存地址,形参可以直接修改实参的实际值
函数的嵌套调用和链式访问
嵌套调用
说明:一个函数中调用另一个函数:
实例语法:
int add1(int a ,int b){
return a+b;
}
int add2 (int a ,int b){
return add1(a,b); // 调佣调用add1函数,从而获取到a+b的和
}
链式访问
理解:把一个函数的返回值,作为另一个函数的参数
例如:
printf("%d",strlen("333")) ; // 求字符串的长度
c
// pringf()函数的返回值为:字符的个数
#include <stdio.h>
int main(){
printf("%d",printf("%d",printf("%d",43)));
return 0;
}
函数的声明和定义
- 声明 理解:
函数的声明一般出现在函数的使用之前, 要满足先声明后使用
场景:函数如果定义在main主函数之下的话,程序编译时会发出警告。
如果想要去掉警告,就可以提前声明函数。
例子:
int add(int num1,int num2);
int main(){
printf("%d".add(1,23));
return 0;
}
int add(int num1, int num2){
return num1+num2;
}
c
#include <stdio.h>
// 函数的声明
int add(int num1,int num2);
int main(){
printf("%d",add(1,2));
return 0 ;
}
// 函数的定义
int add(int num1,int num2){
return num1+num2;
}
- 定义
函数定义:指函数的具体实现,交待函数的功能实现。
函数递归
程序调用自身的编程技巧
通解:函数自己调用自己
递归使用的必要条件:
存在限制条件,当满足这个限制条件的时候,暂停递归
每次递归之后越来越接近这个限制条件
- 例题1:接收一个整型值(无符号),按照顺序打印他的每一位。
c
/*
扩展:
无符号,没有正负号的正数
类型标识:unsigned int
打印格式:%u
*/
#include <stdio.h>
void print(unsigned int num){
if(num >9){
print(num/10);
}
printf("%u\n",num%10);
}
int main(){
unsigned int num = 1234;
print(num);
return 0;
}
- 例题2:编写不适用临时变量,求字符的长度
c
// 使用临时变量版本
#include <stdio.h>
int my_strlen(char* str){
int count = 0;
while (*str != '\0'){
count ++;
str ++; // str是指针变量,因为数组的内存地址是连续,所以可以通过++来取出char数组的每一个值
}
return count;
}
int main(){
char arr[] = "a33333bc";
printf("%d",my_strlen(arr));
return 0;
}
c
// 使用递归
#include <stdio.h>
int my_strlen(char* str){
if(* str != '\0'){
return 1+my_strlen(str+1);
}else{
return 0;
}
}
int main(){
char arr[] = "123456789";
printf("%d",my_strlen(arr));
return 0;
}
- 例题3:求n的阶乘法
c
#include <stdio.h>
int factorial(int num){
if(num == 1){
return 1;
}else{
return num* factorial(num -1);
}
}
int main(){
printf("%d",factorial(5));
return 0;
}
- 例题4:求第n项的斐波那契数列
c
#include <stdio.h>
// 求第n项的斐波那契数列
int fib(int num) {
if(num<=2) {
return 1;
}else {
return fib(num-1) + fib(num-2);
}
}
int main(void) {
int num = 0;
scanf("%d", &num);
int result = fib(num);
printf("%d", result);
return 0;
}
c
// 使用循环
int main(void) {
int num = 0;
scanf("%d", &num);
int arr[num];
arr[0] = 1;
arr[1] = 1;
printf("%d\n", arr[0]);
for (int i = 2; i < num; i++) {
arr[i] = arr[i - 1] + arr[i - 2];
}
printf("%d\n", arr[num - 1]);
return 0;
}
- 例题5:三个整数从小到大排序
c
#include <stdio.h>
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main(void) {
int a =0;
int b = 0;
int c =0;
scanf("%d %d %d",&a,&b,&c);
if(a>b) {
swap(&a,&b);
}
if(b>c) {
swap(&b,&c);
}
if(a>b) {
swap(&a,&b);
}
printf("%d %d %d",a,b,c);
return 0;
}
- 例题6:求两个数的最大公约数
c
#include <stdio.h>
/*
*辗转相除法:
*两个数取余数,当余数为0时,取当前算式除数为最大公约数。
*
*/
int main(void) {
int num1 =0;
int num2 =0;
int num3 = 0;
scanf("%d %d",&num1,&num2);
while(num3 = num1 % num2) {
num1 = num2;
num2 = num3;
}
printf("%d",num2);
}
- 例题7:九九乘法表
c
int main(void) {
for(int i =1;i<10;i++) {
for(int j =1;j<=i;j++) {
printf("%d*%d=%d\t",j,i,i*j);
}
printf("\n");
}
return 0;
}
函数练习
待完成
c
// 素数判断
#include <stdio.h>
int demo1 (int num) {
if (num > 1) {
for (int i = 2; i <num; i++) {
if (num % i == 0) {
return 0;
}
}
return 1;
}
return 0;
}
int main(void) {
int num = 0;
scanf("%d", &num);
if (demo1(num)) {
printf("yes");
}else {
printf("no");
}
return 0;
}
c
// 闰年
#include <stdio.h>
int demo2 (int num) {
if(num %400==0 ||(num % 100!=0 && num % 4==0)) {
return 1;
}else {
return 0;
}
}
int main(void) {
int num = 0;
scanf("%d", &num);
if (demo2(num)) {
printf("yes");
}else {
printf("no");
}
return 0;
}
c
// 二分法顺序查找
#include <stdio.h>
int main(void) {
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int num = 2;
int arrlen = sizeof(arr) / sizeof(arr[0]);
int index = 0;
int min = 0;
int max = arrlen - 1;
while (min <= max) {
index = (min + max) / 2;
if (arr[index] == num) {
printf("%d\t", index);
break;
}else if (arr[index] > num) {
max = index - 1;
}else {
min = index + 1;
}
}
return 0;
}
c
// 调用一次函数,自增加1
#include <stdio.h>
void demo4 (int* num) {
*num +=1;
}
int main(void) {
int num = 0;
demo4(&num);
printf("%d\n", num);
return 0;
}
数组
> 简介:数组是一组相同类型元素的集合 >
- 数组的创建及初始化
例;
c
int arr[10]= {1,2,3}; // 不完全初始化,后续的值以0填充
char ch[3];
double data2[11];
一维数组
- 一维数组在内存中的存储 结论:数组在内存中是连续存放的。
c
#include <stdio.h>
int main(void) {
int arr[11]= {1,23};
for (int i = 0; i < 11; i++) {
printf("%p\n", &arr[i]);
}
return 0;
}
二维数组
- 二维数组的创建和初始化 注意点:在二维数组初始化时,可以省略行的个数,不能省略列的个数
c
#include <stdio.h>
int main(void) {
int arr2[2][2]={1,2,3,4}; // 这样初始化也是可以的
int arr[][2]={{1,2}};
printf("%d\n",arr[0][0]);
return 0;
}
选择排序
c
#include <stdio.h>
// 选择排序
int main(void) {
int arr[]= {8,7,9,6,5,2,3,1,4,534,5,345,24,23,4,2,4};
int arrLength = sizeof(arr)/sizeof(arr[0]);
for (int i = 0; i < arrLength-1; i++) {
for (int j = i; j < arrLength; j++) {
if (arr[i] >= arr[j]) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
for (int i = 0; i < arrLength; i++) {
printf("%d ", arr[i]);
}
return 0;
}
一维数组的数组名
数组名确实能表示首元素的地址
但是存在2个例外:
1 sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节
2 &数组名,这里的数组表示整个数组,取出来的是整个数组的地址。
扩展-数组传参:
数组作为参数将值传递给函数时,默认传递的是数组下标为0的内存地址。
c
#include <stdio.h>
int main(void) {
int arr[]= {8,7,9,6,5,2,3,1,4,534,5,345,24,23,4,2,4};
printf("%p\n",arr);
printf("%p\n",&arr[0]);
return 0;
}
- 例
二维数组的数组名
int arr [3][3] ;
arr表示: arr[0]的内存地址(想象为一维数组)
sizeof(arr)值为:36
计算二维数组的行列大小:
行:sizeof(arr)/sizeof(arr[0])
列:sizeof(arr[0]/sizeof(arr[0][0]))
c
#include <stdio.h>.
int main(void) {
int arr[3][4];
printf("arr:%d\n",sizeof(arr)/sizeof(arr[0]));
printf("arr:%d\n",sizeof(arr[0])/sizeof(arr[0][0]));
return 0;
}
案例-三子棋
- 项目结构
- main.c文件
c
#include "game.h"
void menu() {
printf("*********************************\n");
printf("********** 1.Play ************\n");
printf("********** 0.Exit ************\n");
printf("*********************************\n");
}
void game() {
char board[ROW][COL]= {0};
char result= 0;
initBoard(board, ROW, COL);
printf("初始化棋盘\n");
displayBoard(board,ROW,COL);
while(1) {
// 玩家下棋
playerMove(board,ROW,COL);
// 判断玩家结果
result = isWin(board,ROW,COL);
if(result !='c' ) {
break;
}
displayBoard(board,ROW,COL);
// 电脑下棋
computerMove(board,ROW,COL);
result = isWin(board,ROW,COL);
if(result !='c' ) {
break;
}
displayBoard(board,ROW,COL);
// 判断电脑结果
}
if(result == '*') {
printf("player win!\n");
}else if(result == '#') {
printf("computer win!\n");
}else {
printf("平局!\n");
}
}
int main() {
srand(time(NULL)); // 生成随机数的起点
int input = 0;
do {
menu();// 打印菜单
scanf("%d", &input);
switch (input) {
case 1:
// 逻辑
game();
break;
case 0:
printf("Exit\n");
break;
default:
printf("选择错误,请重新选择.\n");
}
}while (input);
return 0;
}
- game.h文件
c
//
// Created by varin on 2024/9/16.
//
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
/**
*
*初始化棋盘
*/
#define ROW 3
#define COL 3
// 初始化数组
void initBoard(char board[ROW][COL],int row,int col);
// 初始化并打印棋盘
void displayBoard(char board[ROW][COL],int row,int col);
// 玩家下棋
void playerMove(char board[ROW][COL],int row,int col);
// 电脑下棋
void computerMove(char board[ROW][COL],int row,int col);
// 判断输赢
/**
*
* @param board
* @param row
* @param col
* @return
* 玩家赢返回:*
* 电脑赢返回:#
* 平局:p
* 继续:c
*/
char isWin(char board[ROW][COL],int row,int col);
- game.c文件
c
//
// Created by varin on 2024/9/16.
//
#include "game.h"
#include <stdlib.h>
/**
*
* @param board
*初始化棋盘为空格
*
* @param row
* @param col
*/
void initBoard(char board[ROW][COL],int row,int col) {
int i =0;
int j = 0;
for(i=0;i<row;i++) {
for(j=0;j<col;j++) {
board[i][j]=' ';
}
}
}
/**
*初始化棋盘
* @param board
* @param row
* @param col
*/
void displayBoard(char board[ROW][COL],int row,int col) {
int i = 0;
for(i=0;i<row;i++) {
// 第一行格式: 空格|空格|空格
int j = 0;
for(j=0;j<col;j++) {
printf(" %c ",board[i][j]);
if(j < col - 1) {
printf("|");
}
}
printf("\n");
// 第二行格式: ---|---|---
if(i < row - 1) {
int j = 0;
for(j=0;j<col;j++) {
printf("---");
if(j < col - 1) {
printf("|");
}
}
printf("\n");
}
}
}
void playerMove(char board[ROW][COL],int row,int col) {
int x = 0;
int y = 0;
printf("玩家下棋\n");
while(1) {
printf("玩家请输入坐标>");
scanf("%d %d",&x,&y);
if(x>=1 && x<=row && y>=1 && y<=col) {
if(board[x-1][y-1] == ' ') {
board[x-1][y-1] = '*';
break;
}else {
printf("坐标被占用,请重新输入\n");
}
}else {
printf("坐标非法,请重新输入\n");
}
}
}
void computerMove(char board[ROW][COL],int row,int col) {
int x = 0;
int y = 0;
printf("电脑下棋\n");
while(1) {
x = rand()%(row);
y = rand()%(col);
if(board[x][y] == ' ') {
board[x][y] = '#';
break;
}else {
printf("坐标被占用,请重新输入\n");
}
}
}
int is_Full(char board[ROW][COL],int row,int col) {
int i = 0;
int j = 0;
for(i=0;i<row;i++) {
for(j=0;j<col;j++) {
if(board[i][j] == ' ') {
return 0;
}
}
}
return 1;
}
char isWin(char board[ROW][COL],int row,int col) {
// 行对齐
int i = 0;
for(i=0;i<row;i++) {
if(board[i][0] ==board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ') {
return board[i][1] ;
}
}
// 列对齐
int j = 0;
for(j=0;j<col;j++) {
if(board[0][j] ==board[1][j] && board[1][j] == board[2][j] && board[0][j] != ' ' ) {
return board[1][j] ;
}
}
// 左对角线对齐
if(board[0][0] ==board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ' ) {
return board[1][1] ;
}
// 右对角线对齐
if(board[0][2] ==board[1][1] && board[1][1] == board[2][0] && board[0][2] != ' ' ) {
return board[1][1] ;
}
// 平局
if(is_Full(board,ROW,COL)) {
return 'p';
}
// 继续
return 'c';
}
案例-扫雷
- 项目结构
- main.c
c
#include "game.h"
void menu() {
printf("*********************************\n");
printf("********** 1.Play ************\n");
printf("********** 0.Exit ************\n");
printf("*********************************\n");
}
void game() {
char mine[ROWS][COLS] = {0}; // 存放布置好的雷的信息
char show[ROWS][COLS] = {0}; // 存放排查雷的信息
// 初始化数据
initBoard(mine,ROWS,COLS,'0');
initBoard(show,ROWS,COLS,'*');
// 设置雷
setMine(mine ,ROW,COL);
//打印棋盘
displayBoard(show,ROW,COL);
// 排雷
checkWin(mine,show,ROW,COL);
}
int main() {
srand((unsigned int )time(NULL)); // 生成随机数的起点
int input = 0;
do {
menu();// 打印菜单
printf("请选择>");
scanf("%d", &input);
switch (input) {
case 1:
// 逻辑
game();
break;
case 0:
printf("程序退出\n");
break;
default:
printf("选择错误,请重新选择.\n");
}
}while (input);
return 0;
}
- game.h
c
//
// Created by varin on 2024/9/16.
//
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 10
// 初始化棋盘数据
void initBoard(char board[ROWS][COLS],int rows,int cols,char set);
// 设置雷
void setMine(char board[ROWS][COLS],int row,int col);
// 打印棋盘布局
void displayBoard(char board[ROWS][COLS],int row,int col);
// 排雷
int checkWin(char mine[ROWS][COLS],char show[ROWS][COLS],int row,int col);
- game.c
c
#include "game.h"
void initBoard(char board[ROWS][COLS],int rows,int cols,char set) {
int i =0;
int j =0;
for(i=0;i<rows;i++) {
for(j=0;j<cols;j++) {
board[i][j]=set;
}
}
}
void displayBoard(char board[ROWS][COLS],int row,int col) {
int i =0;
int j =0;
printf("-----------mine game---------\n");
for(i=0;i<=row;i++) {
printf("%d ",i);
}
printf("\n");
for(i=1;i<=row;i++) {
printf("%d ",i);
for(j=1;j<=col;j++) {
printf("%c ",board[i][j]);
}
printf("\n");
}
printf("-----------mine game---------\n");
}
void setMine(char board[ROWS][COLS],int row,int col) {
int count = EASY_COUNT;
while (count) {
int x = rand()%(row)+1;
int y = rand()%(col)+1;
if (board[x][y]=='0') {
board[x][y]='1';
count--;
}
}
}
// 统计xy坐标周围雷的个数
int get_mine_count(char mine[ROWS][COLS],int x,int y ) {
return
mine[x-1][y]+
mine[x-1][y-1]+
mine[x][y-1]+
mine[x+1][y-1]+
mine[x+1][y]+
mine[x+1][y+1]+
mine[x][y+1]+
mine[x-1][y+1]-
8*'0'
;
}
int checkWin(char mine[ROWS][COLS],char show[ROWS][COLS],int row,int col) {
int x =0;
int y = 0;
int win = 0;
while(win<row*col-EASY_COUNT) {
printf("请输入排查坐标>");
scanf("%d %d",&x,&y);
// 限制范围
if(x>=1 && x<=row && y>=1 && y<=col) {
if(show[x][y]!='*') {
printf("坐标已排查,请重新输入\n");
}else {
if (mine[x][y]=='1') {
// 踩雷
printf("很遗憾,你被炸死了!\n");
displayBoard(mine,ROW,COL);
break;
}else {
win++;
// 统计mine数组中xy坐标周围雷的个数
int count = get_mine_count(mine,x,y);
show[x][y]=count + '0';
displayBoard(show,ROW,COL);
}
}
}else {
printf("输入坐标错误,请重新输入");
}
}
if(win==row*col-EASY_COUNT) {
printf("排雷成功!\n");
displayBoard(mine,ROW,COL);
}
}
操作符详解
算符操作符
加减乘除取余
注意点1:除法分为整数除法和浮点数除法
整数除法:1/1=1
浮点数除法:1/1.0 = 1.0
注意点2:%取模操作符的两端必须是整数
移位操作符
扩展:
存在内存中的是补码
<<
<<(左移:左边丢弃,右边补0):
例:7<<1 =14
0111==>1110
右移>>:
算术移位:右边丢弃,左边补原符号位
逻辑移位:右边丢弃,左边补0
注意:右移是算术移位还是逻辑移位,取决于编译器(大部分编译器使用算术移位)
位操作符
- & &:按位与(有0为0,全一为1)
案例:3 & -5
3的补码:0000 0000 0000 0000 0000 0000 0000 0011
-5的补码 1111 1111 1111 1111 1111 1111 1111 1011
&结果: 0000 0000 0000 0000 0000 0000 0000 0011
3 & -5 等于3
- |
|:按位或(有1为1,全0为0)
案例:3 | -5
3的补码:0000 0000 0000 0000 0000 0000 0000 0011
-5的补码 1111 1111 1111 1111 1111 1111 1111 1011
| 结果: 1111 1111 1111 1111 1111 1111 1111 1011(补码需要转原码:=》减一,取反)
3 | -5 等于-5
^:按位异或(相同为0,不同为1)3 ^ -5 等于-8
- 异或案例:两个整数交换
c
#include <stdio.h>
int main() {
int a =1;
int b= 2;
a = a ^ b;
b = a ^ b;
a = a ^ b;
printf("a = %d, b = %d\n", a, b);
return 0;
}
赋值操作符
=
复合赋值符
+=
-=
*=
/=
%=
=
<<=
&=
|=
^=
单目操作符
> 单目:只有一个操作数 > > 注意:sizeof时操作符,在某些情况下可以省略括号 >
关系操作符
逻辑操作符
条件操作符
逗号表达式
下标操作符,函数操作符、结构体操作符
- 下标操作符 []
- 函数操作符
()
- 结构体操作符
表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定。
同样,有些表达式的操作符在求值的过程中可能需要转换为其他类型。
隐式类型转换
指针
什么是指针
- 指针是内存中一个最小单元的编号,也就是地址
总结:指针就是地址,口语中说的指针是指指针变量
- 指针变量是用来存放地址的,地址是唯一标识一块地址空间的,
- 指针的大小在32位平台上是4个字节,在64位平台上是8个字节
指针和指针类型
结论1:指针类型决定了指针在被解引用的时候访问了几个字节
如果是int*的指针,解引用访问了4个字节
如果是char*的指针,解引用访问了1个字节
结论2:指针类型决定了指针+1操作的时候,跳过几个字节
野指针
概述:野指针是指指针指向的位置是不可知的(随机的、不正确的,没有明确限制的)
野指针的成因:变量没有初始化
防止野指针的方法:
- 尽量初始化
- 实在不知道赋什么值,就写NULL
指针运算
- 指针加减整数
- *p++等于地址加一
- 指针减指针
- 指针减指针:得到的绝对值是指针之间元素的个数
:::info
打印的结果为:-9
:::
二级指针
二级指针:二级指针变量是用来存放一级指针变量地址的。
扩展:
一级指针:解引用一次
二级指针:解引用二次
指针数组
简介:存放指针的数组
- 模拟二维数组
结构体
结构体定义和创建
c
#include <stdio.h>
// 结构体声明
struct Per {
char name[20];
char tel[12];
char sex[5];
int height;
};
int main() {
// 结构体变量创建
struct Per person= {"John","Doe",12,20};
printf("%s\n",person.name);
return 0;
}
结构体传参
传参的两种形式:
- 将自己传递到函数中
- 将自己的地址传递到函数中
注意:结构体传参的时候,参数是需要压栈的,如果传递的结构体对象过于大的话,建议传递结构体地址。
c
#include <stdio.h>
// 结构体声明
struct Per {
char name[20];
char tel[12];
char sex[5];
int height;
};
// 传递本身
void print1(struct Per per) {
printf("Name: %s\n", per.name);
}
// 传递地址
void print2(struct Per* per) {
printf("Name: %s\n", per->name);
}
int main() {
// 结构体变量创建
struct Per person= {"John","Doe",12,20};
print1(person);
print2(&person);
return 0;
}