基于B站比特鹏哥视频学习,基本了解C语言基础知识,对C语言有一个大概认识
1. 什么是C语言
人和计算机交流的语言:C/C++/Java/Python/…
C语言广泛应用于底层开发
电脑是一个硬件,在上面安装操作系统:Windows/Linux/Mac
硬件和操作系统之间有一个驱动层
可以在操作系统上安装应用软件
操作系统之上叫上层软件,之下叫底层软件
计算机语言的发展:低级–>高级
二进制的指令–>汇编指令–>B语言–>C语言
C语言的国际标准:ANSI C、C89、C99、C11
其编译器主要有Clang、GCC、WIN-TC、MSVC、Turbo C等
编辑器-编辑
编译器-编译
C/C++是编译型语言
test.c经过编译、链接变为test.exe
Python是解释型语言
2. 第一个C语言程序
采用VS2022
- 创建项目
- 创建源文件
.h 头文件(函数的声明、类型的声明、头文件的包含)
.c 源文件(函数实现)
#include <stdio.h>
int main()
{
printf("Hello World!");
return 0;
}
C语言代码中一定要有main
函数
main
函数也称主函数,是程序的入口,有且仅有一个
int main()
{
return 0;
}
函数正常返回0,异常返回非0
printf
是一个库函数,用来打印数据
需要调用stdio.h
头文件
- std-标准
- i-input
- o-output
快捷键:
ctrl+f5
运行代码ctrl+k+c
添加注释ctrl+k+u
取消注释
.h 头文件:函数的声明、类型的声明、头文件的包含
.c 源文件:函数实现
3. 数据类型
char //字符数据类型
short //短整型
int //整型
long //长整形
long long //更长的整型
float //单精度浮点数
double //双精度浮点数
计算机中的单位:
- bit-比特位
- byte-字节
- kb
- mb
- gb
- tb
- pb
十进制的世界:0 1 2 3 4 5 6 7 8 9
二进制的世界:0 1
1个bit存储一个二进制数
1byte=8bit
1kb=1024byte
1mb=1024kb
…
数据类型的大小(byte):
sizeof(char) // 1byte
sizeof(short) // 2
sizeof(int) // 4
sizeof(long) // 4
sizeof(long long) // 8
sizeof(float) // 4
sizeof(double) // 8
C语言规定:
sizeof(long)>=sizeof(int)
用类型创建变量,向内存申请空间:
int age = 20;
double price = 66.6;
3. 变量、常量
3.1 定义变量的方法
int age = 100;
float weight = 35.5f;
char ch = 'w';
3.2 变量的分类
- 局部变量
- 全局变量
int a = 20; // 全局变量
int main()
{
int a = 10; // 局部变量
printf("a=%d\n",a); // a=10
return 0;
}
全局变量和局部变量的名字可以相同(不建议),局部变量优先
注:VS环境下使用scanf
函数时报错,需要在代码第一行加#define _CRT_SECURE_NO_WARNINGS
在VS安装路径下有一个文件:newc++file.cpp
在VS工程中创建新的.c或者.cpp文件的时候,都是拷贝newc++file.cpp的,故在该文件中添加#define _CRT_SECURE_NO_WARNINGS
3.4 变量作用域与生命周期
**作用域(scope)**是程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用的
而限定这个名字的可用性的代码范围就是这个名字的作用域。
- 局部变量的作用域是变量所在的局部范围。
- 全局变量的作用域是整个工程(包括所有文件)。
extern
声明外部变量(来自其他文件的变量):extern int a;
变量的生命周期指的是变量的创建到变量的销毁之间的一个时间段
- 局部变量的生命周期是:进入作用域生命周期开始,出作用域生命周期结束。
- 全局变量的生命周期是:整个程序的生命周期。
3.5 常量
C语言的常量分为以下四种:
- 字面常量:
30;
、3.14;
、'w';
、"abc";
const
修饰的常变量,本质是变量,但不能直接修改,有常量的属性:const int a = 20;
#define
定义的标识符常量,不能直接修改:#define MAX 100
- 枚举常量,不能直接修改
enum Color //定义枚举类型 { // 枚举常量 RED, GREEN, BLUE }; int main() { enum Color c = RED; return 0; }
4. 字符串+转义字符+注释
4.1 字符串
"hello world\n"
这种由双引号(Double Quote)引起来的一串字符称为字符串字面值(String Literal),或者简称字符串
C语言中没有字符串类型变量
字符串的结束标志:\0
不完全初始化,剩余的部分默认初始化为0
在计算字符串长度的时候\0
是结束标志,不算做字符串的内容
打印字符串时遇到\0
才停下来
char arr1[] = "abc";
char arr2[] = {'a', 'b', 'c', '\0'};
// 输出相同
求字符串长度的函数strlen()
,头文件是<string.h>
,遇到\0
才停止计算
注意:
- 0——数字0
- ‘0’——字符0,ASCII值是48
- ‘\0’——字符,ASCII值是0
0和’\0’没有本质区别
4.2 转义字符
转义序列 | 含义 |
---|---|
\a | 警报(ANSI C) |
\b | 退格 |
\f | 换页 |
\n | 换行 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\\ | 反斜杠(\) |
\' | 单引号 |
\" | 双引号 |
\? | 问号,在书写连续多个问号时使用,防止被解析成三字母词(古早用法) |
\0oo | 八进制值(oo必须是有效的八进制数,即每个o可表示0~7中的一个数) |
\xdd | 十六进制值(dd必须是有效的十六进制数,即每个d可表示0~f中一个数),如:‘\x63’和’\x063’显示c |
\ddd | ddd表示1~3个八进制的数字,如:‘\130’表示’88’,显示X |
注:
三字母词:??)–>];??(–>[
A是65,a是97,小写和大写之间差32
ASCII码范围0~127
转义字符算一个字符,在字符串长度中为1
"\628"
中,\62
被解析成一个转义字符,八进制数字是0~7,所以长度为2,8是一个单独字符
\t
是一个字符,效果相当于四个字符,但是不能用于长度计算
5. 注释
- 代码中有不需要的代码可以直接删除,也可以注释掉
- 代码中有些代码比较难懂,可以加注释文字
C语言注释风格(/* */)不支持嵌套注释,最好还是用C++风格的(//),可以注释一行或多行
6. 选择语句
- if-else语句
- switch语句
7. 循环语句
- while语句
- for语句
- do-while语句
C语言是“结构化”的程序设计语言
- 顺序结构
- 选择结构
- 循环结构
8. 函数
函数的特点就是简化代码,代码复用。
#include <stdio.h>
int main()
{
int num1 = 0;
int num2 = 0;
int sum = 0;
printf("输入两个操作数:>");
scanf("%d %d", &num1, &num2);
sum = num1 + num2;
printf("sum = %d\n", sum);
return 0;
}
// 上述代码,写成函数如下:
#include <stdio.h>
int Add(int x, int y)
{
int z = x+y;
return z;
}
int main()
{
int num1 = 0;
int num2 = 0;
int sum = 0;
printf("输入两个操作数:>");
scanf("%d %d", &num1, &num2);
sum = Add(num1, num2);
printf("sum = %d\n", sum);
return 0;
}
其中,int
是返回类型,Add
是函数名,(int x,int y)
是函数参数,{}
包含的内容是函数体
9. 数组
9.1 数组定义
int arr[10] = {1,2,3,4,5,6,7,8,9,10};//定义一个整形数组,最多放10个元素
C99之前,数组的大小只能用常量或者常量表达式指定
C99之后,支持变长数组,允许数组的大小是变量,但这种指定方式的数组不能初始化
VS不支持变长数组
9.2 数组的下标
数组的每个元素都有一个下标,下标从0开始
数组可以通过下标来访问
int arr[10] = {0}; //该数组有10个元素,下标范围为0-9
9.3 数组的使用
#include <stdio.h>
int main()
{
int i = 0;
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
for(i=0; i<10; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
注:在线OJ(online judge)
很多互联网公司在笔试环节采用在线OJ的形式,题目一般分为两类:
- IO型:所有代码由自己完成和实现,输入,计算,输出
- 接口型:只需要完成一个函数,其他需要的数据都是准备好的
EOF指end of file,是文件的结束标志,值是-1
下面代码的结果是:随机值
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = {'b', 'i', 't'};
//[b i t ...],在什么时候遇到\0不清楚
printf("%d\n", strlen(arr));
return 0;
}
如果arr[4]
(不完全初始化,剩余值默认为0),则长度为3
10. 操作符
算术操作符
+ - * / %
取模操作符两端只能是整数
移位操作符
>> <<
涉及二进制运算
位操作符
& ^ |
赋值操作符
= += -= *= /= &= ^= |= >>= <<=
单目操作符:只有一个操作数的操作符
操作符 | 功能 |
---|---|
! | 逻辑反操作 |
- | 负值 |
+ | 正值 |
& | 取地址 |
sizeof | 操作数的类型长度(以字节为单位),括号内可以是变量或类型,可以写成sizeof a ,但不可以写成sizeof int |
~ | 对一个数的二进制按位取反 |
– | 前置、后置– |
++ | 前置、后置++ |
* | 间接访问操作符(解引用操作符) |
(类型) | 强制类型转换 |
注:
- C语言中,0表示假,非0表示真
-
int arr[10] = {0}; printf("%d\n",sizeof(arr)); // 40,计算的是整个数组的大小,单位为字节 printf("%d\n",sizeof(arr[0]));// 4 printf("%d\n",sizeof(arr)/sizeof(arr[0]));// 10,数组的元素个数
- 字面浮点数,编译器默认理解为double型
关系操作符
> >= < <= != ==
逻辑操作符:逻辑与、逻辑或
&&、||
条件(三目)操作符:exp1如果为真,计算exp2,否则计算exp3
exp1 ? exp2 : exp3
逗号表达式
exp1, exp2, exp3, …, expN
逗号隔开的一串表达式,特点是从左向右依次计算,整个表达式的结果是最后一个表达式的结果
int main()
{
int a = 10;
int b = 20;
int c = 0;
// c=8 a=28 c=5
int d = (c = a-2, a = b+c, c-3);
printf("%d\n", d); // 5
return 0;
}
下标引用、函数调用和结构成员
[ ] ( ) . ->
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
arr[3] = 20; // []是下标引用操作符,arr和3是[]的操作数
return 0;
}
11. 常见关键字
变量的命名:
- 有意义
int age;
- 名字必须是字母、数字、下划线组成,不能有特殊字符,不能以数字开头
- 变量名不能是关键字
auto
:自动,所有局部变量前都会有auto,会被省略enum
:枚举struct
:结构体union
:联合体(共用体)extern
:声明外部符号return
:函数返回值signed
:有符号的unsigned
:无符号的typedef
:类型重命名void
:无(函数的返回类型,函数参数)volatile
:操作系统中会讲
以下先介绍几个:
11.1 typedef
:类型重命名
typedef unsigned int unit;
将unsigned重命名为unit
11.2 static
:修饰变量和函数
11.2.1 修饰局部变量:称为静态局部变量
#include <stdio.h>
void test1()
{
int a = 1;
a++;
printf("%d ", a);
}
void test2()
{
static int a = 1;
a++;
printf("%d ", a);
}
int main()
{
int i = 0;
while (i < 10)
{
test1();
i++;
}
// 2 2 2 2 2 2 2 2 2 2 2
printf("\n");
i = 0;
while (i < 10)
{
test2();
i++;
}
// 2 3 4 5 6 7 8 9 10 11
return 0;
}
static修饰局部变量时,局部变量出了作用域,不销毁
本质上static修饰局部变量时,改变了变量的存储位置,影响了变量的生命周期,使其生命周期和程序一样。
11.2.2 修饰全局变量
全局变量具有外部链接属性
编译+连接–>可执行程序
全局变量在其他源文件内定义,如果加了static修饰,全局变量的外部链接属性会变成内部链接属性,其他源文件(.c)就不能使用这个全局变量
编译不了,全局变量的作用域变小了
全局变量也放在静态区
11.2.3 修饰函数
函数也具有外部链接属性,可以用extern链接
函数被static修饰时,外部链接属性变成内部链接属性,其他源文件无法使用
11.3 register
:寄存器
电脑上的存储设备有:
- 寄存器(集成到CPU上)
- 高速缓存(cache)
- 内存
- 硬盘
CPU是中央处理器,所有的计算都由CPU处理
创建寄存器变量
int main()
{
// 寄存器变量
register int num = 3; // 建议3存放在寄存器中
return 0;
}
这样对num
的访问速度会更快一些
register
只是建议,不能决定
只有编译器能决定该变量是否被放到寄存器中
12. #define
定义常量和宏
//define定义标识符常量
#define MAX 1000
//define定义宏
#define ADD(x, y) ((x)+(y))
#include <stdio.h>
int main()
{
int sum = ADD(2, 3);
printf("sum = %d\n", sum);
sum = 10*ADD(2, 3);
printf("sum = %d\n", sum);
return 0;
}
define不是关键字,是预处理指令!
宏有参数
ADD
是宏名,x
和y
是宏的参数,参数是无类型,( )
是宏体
int c = ((a)+(b));
替换作用
13. 指针
13.1 内存
内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中进行的
为有效使用内存,把内存划分成一个个的内存单元(一个内存单元的大小:1字节byte)
每个内存单元都有一个编号
一个32位电脑
地址线会产生电信号1/0,32根地址线同时产生电信号:一个
2
32
2^{32}
232的可能序列作为地址
4,294,967,296 byte
4,194,394 kb
4096 MB
4 GB
1字节byte有8比特bit
int main()
{
int a = 10;// 向内存申请4个字节存储10
&a;// 取地址操作符
// a的地址是第一个字节的地址
return 0;
}
10进制:0~9
8进制:0~7
16进制:0~9 a b c d e f
倒着存:看内存的时候倒着往回看,暂时先这么理解
打印地址:%p
printf("%p\n",&a);
用十六进制打印最方便
如果要把地址存起来
int* p = &a;
p是一个指针变量
内存单元有编号,编号就是地址,也被称为指针
把存放指针/地址的变量称为指针变量
int a = 10;
int* p = &a;
*说明p是指针变量,int说明p指向的对象是int类型
char ch = 'w';
char* p = &ch;
*p = 20;
:解引用操作符,通过p中存放的地址,找到p所指向的对象,*p就是p指向的对象。
这里将p指向的对象a的值改为20
口头上说p是个指针,意思是p是个指针变量
13.2 指针变量的大小
不管是什么类型的指针,都是在创建指针变量
指针变量是用来存放地址的,一个指针变量的大小取决于一个地址存放的时候需要多大空间
32位机器上的地址:32bit位——4byte,所以指针变量的大小是4byte
64位机器上的地址:64bit位——8byte,所以指针变量的大小是8byte
printf("%zd\n", sizeof(char*));
printf("%zd\n", sizeof(int*));
printf("%zd\n", sizeof(double*));
结果都是8
结论:指针大小在32位平台是4个字节,64位平台是8个字节
14. 结构体
结构体把一些单一类型组合在一起,构成新类型
例:描述一个人
struct Stu
{
char name[20];
int age;
char sex[10];
char tele[12];
};
void print(struct Stu* ps)
{
printf("%s %d %s %s\n", (*ps).name, (*ps).age, (*ps).sex, (*ps).tele);
// 结构体指针变量->成员名
printf("%s %d %s %s\n", ps->name, ps->age, ps->sex, ps->tele);
}
int main()
{
struct Stu s = {"Zhangsan", 20, "nan", "110"};
// 结构体对象.成员名
printf("%s %d %s %s\n", s.name, s.age, s.sex, s.tele);
print(&s);
return 0;
}