【前言】
本篇文章为初始C语言部分,C语言是编程的入门语言,所以也说是编程入门;
学好C语言的入门内容,才能真正的入门编程,而C语言的学习对于刚入门的同学还是有一些难度的,需要踏踏实实的自己去理解。
在此之前,我了解到有些计算机专业的同学不知道自己学什么,感到很迷茫,其实真正的迷茫来自自己,感到迷茫的时候可以去学一些基础、理解起来简单的内容;此刻的你如果实在不知道学什么,就来和博主一起学习C语言吧!
1.什么是C语言?
1.1 C语言
C语言是一门通用的计算机编程语言,广泛应用于底层开发(Linux操作系统就是C语言写的)。C语言的设计目标是提供一种能够以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。
C语言是母体语言,是人体交互接近底层的桥梁;
近50年间,在TIOBE排行榜中,C/C++位置长期霸占前三名,没有丝毫寒冬,可谓经典永不过时!
1.2 如何学好C语言?
1.鼓励自己,为自己叫好。
学习知识,一定要有自信,坚信自己能够学好,能够熟练掌握。
2.挤时间学习
欲戴王冠,必承其重。
如果你总和别人走一样的路怎么才能保证超越别人,那就要付出不一样的努力。
3.拒绝做伸手党
遇到问题,先尝试自己解决。
4.利用好学习资源
现在网上有很多的学习资源,像B站(被称为哔哩哔哩大学)、还有一些语言的官网,都是可以进行学习的。
2.第一个C语言程序
#include <stdio.h>
int main()
{
printf("hello world\n");
return 0;
}
> 第一个C语言程序的分析:
> main函数是程序的入口,一个程序main函数有且仅有一个;
> printf是一个库函数,专门用来按照某种格式来打印文件;库函数的使用需要包含对应的头文件 -> #include<stdio.h>
> std i o分别对应 标准 输入 输出
3.数据类型
> char //字符型
> short //短整型
> int //整型
> long //长整型
> long long //更长的整型
> float //单精度浮点型
> double //双精度浮点型
接下来我们思考两个问题:
- 为什么出现这么多类型?
- 每种类型的大小是多少?
> C语言由于需要描述不同类型的数字或者字符等,所以出现了多种多样的数据类型。
> 要想了解每种类型的大小,需要我们借助一个程序;
#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(float));
printf("%d\n",sizeof(double));
return 0;
}
4.变量 &常量
生活中有些值是不变的(比如:圆周率,性别,身份证号码,血型等等),有些值是可变的(比如:年龄,体重,薪资等等);
不变的值,C语言中用常量的概念来表示,变量的值C语言中用变量来表示。
4.1 定义变量的方法
int age = 150;
float weight = 45.5f;
char ch = 'w';
4.2 变量的分类
- 局部变量
- 全局变量
看下面这段程序,输出结果是什么?
int a = 2021;
int main()
{
int a = 2022;
printf("%d\n", a);
return 0;
}
注意:局部变量a的定义其实没有什么问题;只是局部变量和全局变量同名的时候,局部变量优先使用。
4.3 变量的使用
关于变量的使用,我们同样使用一段程序来更好的理解:
int main()
{
int a, b;
printf("请输入两个操作数:>");
//从键盘读取两个数存入num1和num2的地址中
scanf("%d %d", &a, &b);
int sum = a + b;
printf("sum = %d\n", sum);
return 0;
}
分析这个程序:
> %d 表示十进制的数字,用于格式化输出
> scanf函数是一个输入函数
> 输入的%d -> &num1和&num2(&是一个取地址操作符)
> 程序执行:从键盘输入两个数字 -> 打印两个数字之和
4.4 变量的作用域和生命周期
作用域:通常来说,一段程序代码中所用到的名字并不总是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
- 局部变量的作用域是变量所在的局部范围
- 全局变量的作用域是整个项目工程
生命周期:变量的创建到变量的销毁之间的时间段。
- 局部变量的生命周期是:进入作用域周期开始,出作用域生命周期结束。
- 全局变量的生命周期是:整个项目工程的生命周期。
看下面一段程序:
int main()
{
{
int a = 10;
printf("%d\n", a);
}
printf("%d\n", a);
return 0;
}
定义和初始化变量a的时候在{ }作用域内,出了作用域之后,不能再使用。
而全局变量在{ }作用域内和作用域外且main函数中都是可以使用的。
int a = 10;
int main()
{
{
printf("%d\n", a);
}
printf("%d\n", a);
return 0;
}
4.5 常量
C语言中的常量分为以下几种:
- 字面常量
- const修饰的常变量
- #define定义的标识符常量
- 枚举常量
3.14;//字面常量
const float pai = 3.14f; //const修饰的常变量
#define MAX 100 //#define定义的标识符常量
//枚举常量
enum Sex
{
MALE,
FEMALE,
SECRET
};
注意:const 修饰的常变量只是在语法层面限制了变量不能直接被修改,但是本质上还是一个变量,所以叫常变量。
5.字符串 &转义字符
5.1 字符串
字符串:由双引号引起来的一串字符。
"hello world\n"
"hello C语言\n"
注意:字符串结束的标志是" \0 ", 在计算字符串长度的时候也是以" \0 "结束。
下面通过一段代码来更好的理解:”
int main()
{
char arr1[] = "bit";
char arr2[] = { 'b', 'i', 't' };
char arr3[] = { 'b', 'i', 't', '\0' };
printf("%s\n", arr1);
printf("%s\n", arr2);
printf("%s\n", arr3);
return 0;
}
字符串的后面默认是有一个" \0 "的,而" \0 "又是字符串的结束标志,在数组arr2中由于没有" \0 ",所以字符串arr2打印时后面是随机值。
5.2 转义字符
假设我们要在屏幕上打印一个目录:c:\code\test.c
int main()
{
printf("c:\code\test.c\n");
return 0;
}
运行结果打印的为什么不是一个完整的目录?
那是因为转义字符的存在
一些常见的转义字符:
\n 换行
\' 字符常量 '
\" 字符串内部的双引号
\\ 反斜杠
\b 退格符
\r 回车
\t 水平制表符
\v 垂直制表符
\ddd ddd表示一个八进制的数字
\xdd dd表示一个十六进制的数字
再来举个栗子:
int main()
{
// \t、\62是一个转义字符
printf("%d\n",strlen("c:\test\628\test.c")); // 14
return 0;
}
strlen也是一个库函数(在string.h的头文件中),用来字符串" \0 "之前的的长度。
6.注释
- 不需要的代码可以直接删除,也可以注释掉
- 有些比较难懂的代码,可以加注释文字
注释有两种风格:
1. 使用 //
//int main()
//{
// int a[10];
// a[10] = 1;
// return 0;
//}
2.使用 /* */
/*int main()
{
int a[10];
a[10] = 1;
return 0;
}*/
7.选择语句
何为选择?如果你好好学习,校招时拿一个好offer,走上人生巅峰;
如果你不学习,毕业等于失业,回家卖红薯。
int main()
{
int choice = 0;
printf("你会好好学习吗?:>");
if (choice == 1)
{
printf("坚持敲代码,有好offer\n");
}
else
{
printf("放弃,回家卖红薯\n");
}
return 0;
}
8.循环语句
何为循环?比如我们日复一日的学习、每天吃饭睡觉都是一种循环。
while循环的使用:
int main()
{
int i = 0;
while(i < 10)
{
printf("这是一个循环语句\n");
}
return 0;
}
9.函数
一个工程中只有一个main函数,那么函数又是什么呢?
函数:程序中的某部分代码,由一个或多个语句组成;它负责完成某项特定的任务,相较于其他代码,具备相对的独立性。
int Add(int x, int y)
{
return x + y;
}
int main()
{
int a = 10, b = 20;
scanf("%d %d", &a, &b);
int sum = Add(a, b);
printf("%d\n", sum);
return 0;
}
关于函数,在这里只做简单的使用,后续章节会做详细的讲解。
10.数组
数组:相同类型元素的集合。
10.1 数组的定义
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
//定义了整型数组(数组名为arr),最多存放十个元素
10.2 数组的下标
数组的每个元素都有一个下标,下标是从0开始的。
int arr[10] = {0};
10.3 数组的使用
//遍历
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
11.操作符
C语言中有很多操作符,有单目操作符(一个操作数),双目操作符(两个操作数),三目操作符(三个操作数);此外还有很多的分类。
11.1 算数操作符
+ - * / %
int main()
{
int a = 10, b = 5;
printf("%d\n", a + b); //15
printf("%d\n", a - b); //5
printf("%d\n", a * b); // 50
printf("%d\n", a / b); // 2
printf("%d\n", a % b); // 0
return 0;
}
注意:1. 3 / 2数学中的结果是1.5; 在这里3和2两个整型相除的结果还是一个整型(取整数部分)。
2. %操作符:求一个数除以另一个数的余数 。例如:3%2的余数为1。
11.2 赋值操作符
= += -= *= /=
除了 = ,其余都是符合运算符:变量的值在自身的基础之上进行运算。
int main()
{
int a = 10, b = 20;
a += b; //相当于a = a + b; a的结果为30
a -= b; //相当于a = a - b;a的结果为10
return 0;
}
//另外两个操作符的使用也是一样的
11.3 单目操作符
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
11.4 关系操作符和逗号表达式
> >= < <= != ==
exp1, exp2, exp3, ......expn 从左往右依次计算,整个表达式的结果是最后一个表达式的结果
11.5 逻辑操作符和条件操作符
&& 逻辑与
|| 逻辑或
exp1 ? exp2 : exp3
11.6 下标引用、函数调用操作符
[] () . ->
12.常见关键字
在我们平时写代码的时候,有一个非常令人头疼的问题——我们并不知道C语言中的关键字以及用途,这就导致程序的部分功能无法实现。
C语言中常见的关键字(简单认识):
> auto break case char const continue default double else
> enum extern float for goto if int long register return short
> signed sizeof static struct switch typedef union unsigned
> void volatile while
上面的关键字首先简单认识一下,下面重点介绍 typedef 和 static 的用法。
12.1 typedef关键字的使用
typedef:对类型进行重命名。例如:
typedef int zwx;
typedef struct Node
{}Node; //对自定义类型进行重命名
通过VS2019的调试窗口可以观察一下,类型的重命名:
typedef int zwx;
int main()
{
int a = 10;
zwx b = 20;
return 0;
}
通过调试可以看到,通过类型 int 和 zwx定义的变量类型同为 int 。
12.2 static关键字的使用
static修饰变量和函数的三个作用:
12.2.1 static修饰局部变量
void test()
{
static int a = 1;
printf("%d ", a++);
}
int main()
{
for (int i = 0; i < 10; i++)
{
test();
}
return 0;
}
看下程序的运行结果:
static修饰局部变量时,局部变量变为静态局部变量,出了局部范围,不会自动销毁,下次进入局部范围时,依然存在;
其实是因为static修饰的局部变量是存储在静态区的;static修饰局部变量实际上改变了变量的存储位置(栈区 -> 静态区)。
12.2.2 static修饰全局变量
不同源文件,一个源文件中声明的static变量在另一个源文件中不可用。
总结:一个全局变量本来是有外部链接属性的(不加static是可以在另一个源文件中使用的),但是被static修饰后外部链接属性变为内部链接属性,只能在自己所在的源文件内部使用,不能在其他源文件使用。使用上感觉作用域变小了;
12.2.3 static修饰函数
同样,一个函数是有外部链接属性的,被static修饰后外部链接属性变为内部链接属性,在另一个源文件中不能使用。
13. #define定义常量和宏
#define定义常量和宏可以对复杂代码进行简化。
比如说用函数实现两个数相加需要在函数内部相加,并且需要返回值;但如果使用#define定义宏来实现就很方便了。
注意:#define定义的常量和宏时命名通常使用大写,便于和变量名区分。
//#define定义的常量
#define MAX 100
//#define定义宏
#define ADD(x, y) (x)+(y)
#include <stdio.h>
int main()
{
printf("%d\n", MAX); //100
printf("%d\n", ADD(2, 3));//5
int sum = 10 * ADD(2, 3);//宏会直接替换掉标识符 sum = 10 * (2) + (3) = 23
printf("%d\n", sum);//23
return 0;
}
14.指针
提到指针就不得不讲内存,那么内存是什么呢?
>内存:电脑上特别重要的存储器,计算机中程序的运行都是在内存中运行的。
为了有效的使用内存,就把内存划分为一个个小的内存单元,每个内存单元的大小是一个字节。
地址 = 内存单元编号 = 指针变量
14.1 内存
内存:计算机的重要部件,也称内存储器和主存储器,它用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。它是外存与CPU进行沟通的桥梁,计算机中所有程序的运行都在内存中进行,内存性能的强弱影响计算机整体发挥的水平。只要计算机开始运行,操作系统就会把需要运算的数据从内存调到CPU中进行运算,当运算完成,CPU将结果传送出来。
为了有效的使用内存,把内存空间划分为了一个个内存单元,每个单元的大小是一个字节。
为了能够访问这些内存单元,对每个,单元进行了编号,这些内存单元编号也叫地址。(地址 = 内存单元编号 = 指针变量)
变量是创建在内存中的(在内存中分配空间),每个内存单元都有地址,每个变量都有对应的地址编号。
int main()
{
int num = 10;
#//&是一个操作符 取出num的地址
//注:这里num的4个字节,每个字节都有地址,取出的是第一个字节的地址(较小的地址)
printf("%p\n", &num);//打印地址,%p是以地址的形式打印
return 0;
}
14.2 指针
想要储存地址,需要定义指针变量。
int num = 10;
int *p;//p为一个整型指针变量
p = #//把变量num的地址取出来放到指针变量p中
注意:指针存储的地址中变量的类型要和指针类型相匹配。
char ch = 'w';
char* pc = &ch;
下面通过一段程序对指针存储的地址空间的内容进行修改:
int main()
{
int num = 10;
int* p = #
*p = 20;//*指针变量(地址),可以拿到地址空间中的内容,进行修改
return 0;
}
14.3 指针大小
指针变量就是地址,都是由32/64(32/64位机器)个二进制位组成,也就是4/8个字节。
不同类型的指针在相同的平台下大小是一样的,都是4/8个字节。
下面在32位平台下进行验证:
int main()
{
printf("%d\n", sizeof(char*));
printf("%d\n", sizeof(int*));
printf("%d\n", sizeof(long*));
printf("%d\n", sizeof(long long*));
printf("%d\n", sizeof(float*));
printf("%d\n", sizeof(double*));
return 0;
}
图中的x86表示的就是32位平台,x64表示64位平台。
15.结构体
我们平时使用的内置类型有些场景并不适用,如描述一个学生,包含:姓名+年龄+性别+学号信息。
这时就要用到自定义类型——结构体。
代码实现:
struct Stu
{
char name[20];//姓名
int age; //年龄
char sex[5]; //性别
char id[20]; //学号
};
结构体成员的两种访问方式
1.结构体变量 . 结构体成员
int main()
{
//初始化
struct Stu s = { "张三", 18, "男", "20211554" };
//打印
printf("%s %d %s %s\n", s.name, s.age, s.sex, s.id);
return 0;
}
2.结构体指针 -> 结构体成员
void Print(struct Stu* s)
{
printf("%s %d %s %s\n", s->name, s->age, s->sex, s->id);
printf("%s %d %s %s\n", (*s).name, (*s).age, (*s).sex, (*s).id);
//先对结构体指针解引用找到结构体变量,再使用 . 操作符进行访问
}
int main()
{
struct Stu s = { "张三", 18, "男", "20211554" };
Print(&s);
return 0;
}
【结语】
当一个人的才华支撑不起野心的时候,只能安静读书。到这里初识C语言部分的学习就结束了,觉得博主写的还不错的来个三连吧,此外博主还创建了进阶的C语言的专栏,喜欢的铁汁们可以点一下订阅,感谢支持。