Bootstrap

完全入门C语言

1.C语言

C语言是一门通用的计算机编程语言,广泛应用于底层开发。其设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。有良好的跨平台的特性。
同时C语言还是面向过程的编程语言,这与JAVA C++等面向对象的编程语言有所不同。
目前主流的C语言编译器有:GCC Clang MVSVC等

2.第一个C语言程序

初学者建议使用VS2022

#include <stdio.h> //头文件 
int main()//程序执行的开始 主函数
{
	printf("hello world");//库函数printf
	return 0;
	//这是你的第一个C语言程序。
	//运行后将会在你的屏幕上打印hello world
}

3.数据类型

在C语言当中我们会定义很多的数据变量,每个变量都要有他们的类型,这样才能方便我们为我们定义的变量赋予特定的性质。
系统自带的类型有:

char           //字符类型
short          //短整型
int            //整型
long           //长整型
long long      //更长的整型  
float          //单精度浮点型
double         //双精度浮点型

在C语言当中每一个数据类型都是有大小的,也就是说可以存储的数据是有限的。
有一个操作符可以计算数据类型的存储大小—sizeof + (类型)
比如

printf("%d",sizeof (int));
//运行结果:4

重复计算我们就可以知道每一个数据类型的大小了
数据类型 | 数据类型大小
char | 1
short | 2
int | 4
long | 4
long long | 8
float | 4
double | 8
这里的数字表示的是字节大小。1为1字节
在计算机中的单位:bit(比特) Byte(字节) Kb Mb Gb Tb Pb
一个Byte = 8bit
1Kb = 1024Byte 1Mb = 1024Kb 1Gb = 1024Mb …

2.1类型的使用:

所谓的类型就是为了更加丰富的表达生活中的各种值

char ch = 'w';//姓
int weight = 120;//体重
int salary = 20000;//工资

3.变量与常量

变量创建的本质:就是在内存当中开辟一块空间,来存放数据。
变量顾名思义就是可能会变的量,就像数学中的经常定义x为变量。
C语言中也可以定义变量,不过是有格式的。

3.1定义变量

变量名的要求:
1.数字不能开头
2.长度小于等于63
3.区分大小写
4.不能用关键字(后面讲)
5.只能由字母(大小写)数字 下划线_ 组成

//数据类型 变量名 = 赋值
int age = 18;
float weight = 45.5f;//最后的f的意思是,浮点型数的意思
char ch = 'y';

3.2变量的分类

局部变量与全局变量
从名字可以看出局部只在一个小区域起作用,而全局会在任何地方起作用
确定局部变量区域

int main()
{
	int a = 1;
	{
	int b = 1;
	}
	return 0;
}
//所谓的局部作用域就是变量被{}扩起来的区域,就是说变量只会在{}内起作用,当然{}内的变量也能{{}}内起作用。

全局变量

int a = 0;//全局变量
int main()
{
	int a = 1;//当变量范围起冲突时,优先使用的局部变量。
	printf("%d",a);
	return 0;
}
//输出结果为1

3.3变量的使用

简单的加法计算器

#include <stdio.h>
int main()
{
	int a = 0;
	int b = 0;
	scanf("%d %d",&a,&b);
	printf("%d",a+b);
	return 0;
}

简单结介绍:scanf与printf
scnaf:标准输入函数
作用是输入数据,数据从键盘读取到内存当中
格式

scanf("占位符",地址)

printf:标准输出函数
输出数据,数据从内存中取出,然后打印到屏幕

printf("占位符/字符串",变量名)

3.4变量的作用域和生命周期

作用域

作用域(scope)是程序设计概念,通常来讲,一段代码中所用到的名字并不总是有效/可用的
而限定这个名字的可用性的代码范围就是这个名字的作用域

1.局部变量的作用域就是变量所在的局部范围。
2.全局变量的作用域就是整个工程。
生命周期

变量的生命周期指的是变量的创建带变量的销毁之间的一个时间段
ps:销毁指的是把变量申请的空间还给操作系统。

1.局部变量的生命周期是:进入作用域为开始,出作用域为结束。
2.全局变量的生命周期是:整个程序的生命周期。
如果你想要使用其他文件的全局变量需要用到声明外部符号extern

extern 类型 变量名;
//假设你在其他文件当中定义了一个全局变量
//int a = 100;你就可以在当前文件中写:
extern int a;

3.5常量

C语言当中的常量分为以下几种:

  • 字面常量
  • const修饰的常变量
  • #define 定义的标识常量
  • 枚举常量
#include <stdio.h>
//#define的标识符常量演示
#define MAX 100 
//枚举常量
enum week
{
	Monday,
	Tuesday,
	Wedneday
};
//{}内的Monday,Tuesday,Wednesday为枚举常量
//可以初始化赋值,如果不赋值就从0开始递增
int main()
{
	//字面常量
	3.141000//const 修饰的常变量
	const float pai = 3.14f//pai为const修饰的常变量 不能被修改
	
	return 0;
}

上面的例子中pai被const修饰的常变量,const 修饰的常变量在C语言中只是语法层面限制了变量pai的不能被改变,都是par的本质还是一个变量,所以叫常变量。

4.字符串/转义字符

4.1字符串

由双引号(“”)引起的一串字符称为字面值(String Literal),或者叫字符串。

"hello world";

值得注意的是:字符串的结束标志是一个[\0]的转义字符。在计算字符串长度时的[\0]是结束标识,不能作为字符串内容。

#include <stdio.h>
int main()
{
	char arr1[] = "hello";//字符数组
	char arr2[] = {'h','e','l','l','o'};
	char arr3[] = {'h','e','l','l','o','\0'};
	printf("%s\n",arr1);//printf打印%s是从你给的地址开始打印直到遇到\0停止
	printf("%s\n",arr2);
	printf("%s\n",arr3);
	return 0;
	//运行结果为
	//5
	//随机数
	//5
}

4.2转义字符

转义字符就是转变了意思的字符

转义字符释义
\?在书写连续多个问号时使用,防止被编译器解析为三字母词
\’用于表示字符常量’
\"用于表示一个字符串内部的双引号
\\用于表示一个反斜杠,防止它被解释为一个转义序列符
\a警告字符,发出蜂鸣声
\b退格符
\f进纸符
\n换行
\r回车
\t水平制表符
\v垂直制表符
\dddddd表示3个八进制数字。如:\130
\xdddd表示2个十六进制数字。如:\x30
#include <stdio.h>
int main()
{
	//q1:如何打印单引号'
	//q2:如何打印双引号"
	printf("%c\n",'\'');
	printf("%s"\n","\"");	
	return 0;
}

5.注释

1.不要的代码可以直接注释掉
2.利用注释对代码进行解释

注释的两种风格

  • C语言风格 /*xxxxxx*/
    • 缺点:不能嵌套注释
  • C++风格//xxxxxx

6.选择语句

在生活中经常有人说如果怎么这么样,就会怎么这么样。这就是一个选择语句,如果满足这个条件就会进入相应的事件。就像galgame当中选择合适的选项就可以进入相应人物的故事线,将其攻略。这就是选择!
常用的选择语句:

if else
switch

最简单的选择语句
最简单的选择语句

#include <stdio.h>
int main()
{
	int intput = 0;
	scanf("%d",&input);
	if(input==1)
	{
	//...
	}
	else
	{
	//...
	}
	return 0;
}

7.循环语句

有些事情就是会一直重复的做,吃饭,睡觉啊每天都在进行。这就是循环!
在C语言中有三种循环的模式:

  • while循环
  • for循环(以后讲)
  • do while循环 (以后讲)
//while循环示范
#include <stdio.h>
int main()
{
	int week = 1;
	while(week<6)
	{
		week++;
	}
	if(week == 6)
	{
		printf("已经周末了,该休息了!")}
	return 0;
}

8.函数

函数的特点就是简化代码,使得代码可以复用

#include <stdio.h>
//返回类型 int
int Add(int x,int y)//参数
{
	return x+y;//返回值
}
int Sub(int x,int y)
{
	return x-y;
}
int main()
{
	int input = 0;
	int a = 0,b = 0;
	do
	{
		scanf("%d",&input);
		scanf("%d,%d",&a,&b);
		switch(input)
		{
			case 1:
			Add(a,b);
			break;
			case 2:
			Sub(a,b);
			break;
			default:
			//...
			break;
		}
	}while(input);
	return 0;
}

9.数组

在生活中我们常要存储一群人的信息,就比如一个班的信息什么的。
所以为了存储一连串的信息,C语言就有了数组。
对于数组的定义有:一组相同元素的集合

9.1数组的定义

int arr[10] = {1,2,3,4,5,6,7,8,9,10};//定义一个整型数组,最多存放10个元素
//注意数组的下表是从0开始的。如果我要访问数组的第1个元素,就是arr[0].

9.2数组的下标

C语言规定:数组的每个元素都有下标,下标是从0开始的。
数组可以通过下标来访问。
比如

int arr[10] = {0};//一个10个元素的数组,下标范围为0~9

9.3数组的使用

//计算公元年对应的月份天数
#include <stdio.h>
int main()
{
	int year = 0,month = 0;
	int month_1[13] = {0,31,29,31,30,31,30,31,31,30,31,30,31};//润年月份
	int month_2[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};//平年月份
	scanf("%d",&year);
	scanf("%d",&month);
	if(year%4==0&&year&100!=0||year%400==0)//为闰年
	{
		printf("%d\n",month_1[month]);
	}
	else
	{
		printf("%d\n",month_2[month]);
	}

	return 0;
}

10.操作符

简单介绍操作符

算术操作符

+ - * / %

位移操作符

>> <<

位操作符

& ^ |

赋值操作符

= += -= *= /= &= ^= |= >>= <<=
单目操作符

! 取反

  •                 负值
    
  •                正值
    

& 取地址
sizeof 操作数的类型大小(以字节位单位)
~ 对一个数的二进制位进行取反
– 前置、后置–
++ 前置、后置++

  •                 间接访问操作符(解引用操作符)
    

(类型) 强制类型转换

关系操作符

>
>=
<
<=
!= 用来判断不相等
== 用来判断相等

逻辑操作符

&& 逻辑与
|| 逻辑或

三目操作符

exp1?exp2:exp3 exp(表达式)

逗号表达式

exp1,exp2,exp3,…expn

从左往右依次计算。
赋值的结果取最后一个

下标引用、函数调用和机构成员

[] () . ->

11.常见关键字

C语言提供了丰富的关键字,这些关键字都是语言本身预先设定好的,用户自己是不能创作关键字的。

auto break case char const continue default do double else enum
extern float for goto if int long register return short signed
sizeof static struct switch typedef union unsigned void volatile while

11.1关键字的分类

修饰变量循环分支数据类型修饰类型函数计算机变量/类型 大小自定义类型
autobreak(退出循环)breakcharconstreturn(返回)sizeofenum(枚举)
extern(声明外部符号)continueswitch caseshortsigned(有符号的)struct(结构体)
register(寄存器)do whiledefaultintunsigned(无符号的)union(联合体)
staticforif elselongtypedef(类型重定义)
whilegotolong long
float
double
void(无)
C语言当中的auto一般写在类型的前面,如 auto int a = 1
auto 的作用是使局部变量自动创建与销毁,绝大多数的情况都直接省略了,所以我可以不写出来。

11.2关键字typedef

typedef是类型定义,可以理解为类型重命名

//将long long 重命名成LL
typedef long long LL;
int main()
{
	LL a = 1;
	long long b = 2;
	return 0;
}

利用typedef对类型重命名可以方便我们书写复杂的类型。

11.3关键字static

在C语言中
static是用来修饰变量和函数的
1.修饰局部变量-称为静态局部变量
2.修饰全局变量-称为静态全局变量
3.修饰函数-称为静态函数

11.3.1修饰局部变量

static修饰局部变量改变了变量的生命周期和存储类型(位置)
让静态局部变量出了作用域依然存在,直到程序结束,生命周期才结束。
但是所修饰的局部变量仍受作用域的限制
static所修饰的局部变量在编译时就已经创建,后续程序运行时不会在走初始化那一步。

//代码1
#include <stdio.h>
void test()
{
	int i = 0;
	i++;
	printf("%d ",i);
}
int main()
{
	for(int i = 0;i<10;++i)
	{
		test();
	}
	return 0;
}
//打印结果1 1 1 1 1 1 1 1 1 1

//代码2
#include <stdio.h>
void test()
{
//static修饰局部变量
	static int i = 0;
	i++;
	printf("%d ",i);
}
int main()
{
	for(int i = 0;i<10;++i)
	{
		test();
	}
	return 0;
}
//打印结果1 2 3 4 5 6 7 8 9 10

局部变量的生命周期的延长本质是局部变量的的存储位置由栈区变为了静态区
当前我们只需要知道:
栈区是存放局部变量和形式参数的。临时作用的变量都在栈区
特点:进入作用域创建,出作用域销毁
静态区存放全局变量,静态变量
特点:创建好后直到程序结束才销毁。

11.3.2修饰全局变量

一个全局变量被static所修饰,会使得这个全局变量只能在本地源文件内使用,不能在其他源文件内使用。

//代码1
//add.c
int g = 2024;
//test.c
extern int g;
int main()
{
	printf("%d\n",g);
	return 0;
}
//可以打印

//代码2
//add.c
static int g = 2024;
//test.c
extern int g;
int main()
{
	printf("%d\n",g);
	return 0;
}
//无法打印,连接性错误

要知道的是,全局变量是具有外部链接属性的,在方法正确的情况下,其他文件是可以使用的,但是static会改变这种外部链接属性,使得变成看内部链接属性。

11.3.3修饰函数

一个函数被static所修饰,会使得这个全局变量只能在本地源文件内使用,不能在其他源文件内使用。

//代码1
//add.c
int add(int x,int y)
{
	return x+y;
}
//test.c
extern int add(int int);
int main()
{
	printf("%d\n",add(2,3));
	return 0;
}
//可以打印

//代码2
//add.c
static int add(int x,int y)
{
	return x+y;
}
//test.c
extern int add(int int);
int main()
{
	printf("%d\n",add(2,3));
	return 0;
}
//无法打印,连接性错误

12.#define定义宏常量和宏

//define定义标识符常量
#define MAX 10000
//define定义宏
#define Add(x,y) ((x)+(y))

#include <stdio.h>
int main()
{
	int sum = Add(2,3);
	printf("%d\n",sum);
	return 0;
}

13.指针

内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中进行的。
所以为了有效的使用内存,就要把内存划分成一个个小的内存单元,每个内存单元的大小都是一个字节。
为了能够有效的访问到内存的每个单元,就要给内存单元进行编号,这些编号被称为内存单元的地址。
内存表示

变量是创建内存中的(在内存中分配空间的),每个内存单位都有地址,所以变量也是有地址的。
可以利用&来取出变量的地址

#include <stdio.h>
int main()
{
	int a = 1;
	&a;//取出a的地址
	//要注意的是,因为a是整型有4个字节,每个字节都要自己对于的地址,这个取出的地址的第一个字节的地址(较小的地址)
	printf("%p\n",&a)//打印a的地址,%p是以地址的形式打印
	
	return 0;
}

a的地址表示

13.1指针变量

知道了地址以后,我们要怎么存储它呢?这个时候就需要指针变量了
指针变量顾名思义就是存放指针(地址)的变量
实例

#include <stdio.h>
int main()
{
	int a = 0;
	int* pa = &a;
	*pa = 10;//*就是根据a的地址取找到a
	//这样我们就可以间接的改变a的值
	printf("%d\n",a);
	return 0;
}
//打印结果:10

指针变量的地址

以整型指针为例子的话,我们也可以推广到其他类型

#include <stdio.h>
int main()
{
	char ch = 'w';
	char* pc = &ch;
	*pc = 'y';
	//...
	return 0;
}

13.2指针变量的大小

在计算机上,有地址线,物理的电线高低电平的信号,转化数字信号:1/0
32位机器上面有32根地址线。
指针的大小就和机器的位数有关。

//指针变量的大小取决于地址的大小
//32位平台下地址为32个bit位(即4字节)
//64位平台下地址位64个bit位(即8字节)
#include <stdio.h>
int main()
{
	int a = 1;
	int* pa = &a;
	char c = 'w';
	char* pc = &c;
	printf("%d ",sizeof(pa));
	printf("%d\n",sizeof(pc));
	return 0;
}
//打印结果:
//在32位的计算机上:4 4
//在64位的计算机上:8 8

由此可以看出,指针大小在32位平台是4个字节,64位平台是8字节。

14.结构体

结构体是C语言中特别重要的知识点,因为结构体的存在C语言才能有能力描述复杂的类型。比如在描述学生时:学生就包括了,名字,年龄,性别等等。
就只能利用结构体来描述了

#include <stdio.h>
struct stu//struct stu为类型名
{
	char name[20];//名字
	int age;//年龄
	char sex[6];//性别
};
int main()
{
	//结构体的初始化
	struct stu s = {"yui",17,"female"};
	//.为结构成员访问操作符
	printf("name:%s,age:%d,sex:%s\n",s.name,s.age,s.sex);
	//->操作符,用来访问结构体的地址对应的数据
	struct stu* ps = &s;
	printf("name:%s,age:%d,sex:%s\n",ps->name,ps->age,ps->sex);
	//当然还有一种写法,利用*操作符
	printf("name:%s,age:%d,sex:%s\n",(*ps).name,(*ps).age,(*ps).sex);//括号的目的是因为.操作符的优先级高于*,为了完成目标,所以打上了括号。
	return 0;
}
//在实际写代码的时候可能会为了简化代码而进行typedef进行重命名。
typedef struct stu
{
	char name[20];//名字
	int age;//年龄
	char sex[6];//性别
}stu;
//这样我们在下面定义变量的时候就可以直接用stu来定义了。

;