一、 计算机基础知识
1. 文件
1.1 可执行文件
在windows系统中,扩展名为:*.exe,*.bat,*.com等文件是可执行文件;其由指令和数据构成。
1.2 不可执行文件
其内容是由数据构成的
如:在C/C++语言中*.c/*.cpp源文件(文本),*.h头文件(文本),*.i预编译文件(文本),*.s汇编文件,*.o/*.obj二进制目标文件,*.exe可执行文件
1.3 C语言的编译链接过程
2. 进制及其转换
计算机常用的进制有二进制、八进制、十六进制
二进制转换为八进制 三位一合
二进制转换为十六进制 四位一合
举个例子,这里面为啥要三位一合呢?
因为对于八进制其每一位无非就是8种情况,0~7,将这一范围用二进制表示出来就是000~111一共7种情况
二、什么是C语言
C语言的标准有C89、C99、C11、C17
C源程序的特点:
一个C语言源程序可以由一个或者多个源程序组成
每个源文件可以由一个或者多个函数组成
一个源程序不论由多少个文件组成,都有一个且只能有一个main函数,即主函数
三、数据类型
C语言中的重中之重就是数据类型
主要分为以下几种数据类型
char、short、int、long int、long long 这些可以归结为整型,至于这里面的char为什么归到整型里面,因为字符都是以ASCII码值存在计算机里面的,所以它也被归结到整型里面
float、double、long double 这些可以归结为浮点型
bool是布尔类型
void为空类型,可以表示任何东西
那么在C语言中这些数据类型分别占多少个字节呢
我们可以通过下面的代码来观察其各自所占的字节空间
int main() {
//整型
printf("char size: %d\n", sizeof(char));
printf("short size: %d\n", sizeof(short));
printf("int size: %d\n", sizeof(int));
printf("long int size: %d\n", sizeof(long int));
printf("long long size: %d\n", sizeof(long long));
//浮点
printf("float size: %d\n", sizeof(float));
printf("double size: %d\n", sizeof(double));
printf("long double size: %d\n", sizeof(long double));
//布尔
printf("bool size: %d\n", sizeof(bool));
return 0;
}
其运行结果为:
四、 变量、常量,标识符
变量和常量
变量是以某标识符为名字 起名规则 值可以改变 可读可写
常量其值不可改变 只可读不可写
定义和声明
定义就是为这个变量分配一块内存并且给它取上一个名字,这个名字就是我们经常所说的变量名,注意不能重复定义同一变量或者对象
声明有两重含义:
第一重:这个名字已经匹配到一块内存上了
第二重:名字已经先预定,其他地方不能用它重复定义
声明和定义最重要的区别
定义创建了对象并为这个对象一块内存,而声明的时候是没有分配内存空间的
变量分为
局部变量:在函数中定义的变量
全局变量:在函数外定义的变量
常量分为
字面常量、用#define定义的宏常量、用const关键字修饰的变量(称为常变量)、枚举常量、字符常量和字符串常量
这里面注意枚举常量是有限制的整型常量
单引号和双引号
单引号是字符的定界符
双引号是字符串的定界符
记住一些常见的ASCII码值
'a' 97 'A' 65 '0' 48
空格 38 换行 10 回车 13
注意转义字符 \
有两层含义:
第一,将一个字母转化为一个特殊含义,如\n、\t
第二,将 ' 、\ 字符转化为其本来的字符含义,如' \' ' ——> ',又如' \\ ' ——> \
使用转义字符时需要注意以下问题:
- 转义字符只能使用小写字母,否则会失效
- 在双引号里面使用单引号不需要转义字符
- \?就是指的是? 没有其他意思
总结:
- 转义字符既可以用于单个字符,也可以用于字符串,并且一个字符串中可以同时使用八进制和十六进制形式
- 转义字符取值有限,\ddd最大取值是\177,表示8进制,\xdd最大取值是\x7f,表示十六进制
- 单引号、双引号、反斜杠这些特殊字符如果想要表示其本来含义,需要转移字符
字符常量、字符串常量
单引号是字符的定界符
双引号是字符串的定界符
五、顺序语句、选择语句、循环语句
5.1 选择语句
//单选泽语句
if () {
}
else {
}
//复选择语句
if () {
}
else if () {
}
...
else {
}
//多分支选择语句
switch (int_condition) {
case int_num1:
break;
case int_num2:
break;
...
default:
...
}
这里面注意switch选择语句中的判断条件变量必须是整型,一定要记住这一点,否则会错误
5.2 循环语句
//while循环
while () {
}
//for循环
for (初始条件; 跳出条件; 每次循环完所做动作) {
}
//do while循环
do {
}while(跳出条件)
对于死循环可以有以下几种实现方式
//while循环
while (1) {
}
//for循环
for (初始条件; ; 每次循环完所做动作) {
}
//do while循环
do {
} while (1)
六、函数
函数就是一个个小功能单元,有返回类型、有参数、有函数名称
6.1 可见性和生存期
6.1.1 可见性
指的是某一标识符所能够被使用的范围;只有在作用域内标识符才能使用
按照我的理解,作用域是一个非常重要的概念,因为很多时候需要考虑到这一点,否则就会出错。
作用域这一个概念,针对C语言程序三大过程的编译连接过程,在编译链接的时候,需要考虑到变量的作用域,需要给他们分别链接相应的值
根据上面的变量概念,变量其可以分为全局变量和局部变量
6.1.2 生存期
生存期指的是变量的生存周期,其针对的是程序的执行过程
对于局部变量,其存储在.stack区域,在函数执行开始时,分配空间,到函数结束,释放函数栈空间,所以对于有些在函数体中定义的变量,如果在函数外要对其操作,比如指针指向函数体中的一个数组,那么在函数执行的时候,可以对这个数组赋值,但是在函数执行完毕的时候,数组空间就被释放,其中的值变成随机值,这是一个特别重要的点,在一般的笔试题中都有涉及。
7. 数组
数组是在内存中连续存储的一系列空间,其具有三大特征:数组名、数组元素个数、数组类型
注意数组元素的类型一样,另外我们对于一个数组的解释时,需要从右向左解释,比如下面这个
int (s*)[3];
其代表的是指向一个大小为3个整型元素的数组的指针
而下面这个
int* s[3]
其代表的是大小为3个整型指针的数组
7.1 运算符
一般运算符具有优先级,对于一些笔试中也少不了对优先级的考察,比如下面这个
int* p;
int x = *++p;
int y = *p++;
对于x代表的是先对p指针后移一位,再取其值
对于y代表的是先取p的值,然后对指针p后移一位
上面这个例子就极大地代表了优先级的重要性,如果还不能理解,那就看下面这个例子,这个是对于string.h中的strcat函数的实现
char* my_strcat(char* dist, char const* src)
{
char* p = dist;
while (*++dist) {
//dist++;
}
while (*dist++ = *src++);
*dist = '\0';
return p;
}
可以对这个进行运行测试,发现对于dist和src都非空的情况下,都可以成功链接,但是对于dist是空字符串的时候就不能够实现,其原因其实也很好理解,其原因就是字符串都是以 ' \0 ' 结尾的,第一个循环,如果dist是空字符串的时候,自动跳到下一个空间,这样其第一个空间存储的是\0 ,这样就算下面的链接循环成功链接,但是由于其dist第一个空间为 \0 ,就算对dist进行打印,其也是空字符串,因为字符串都是以 '\0'结尾,所以优先级是一个特别重要的点,利用好了可以简化代码,利用不好会带来不必要的错误。
7.2 运算符举例
- 取模、取整
- *=,-=,+=
- 三目运算符
- 前置++,和后置++
7.2.1 余数
int x = y % 2;
cpp中对于余数是通过运算符%实现的,上面这行代码是求y对2的余数,将其值赋值给x
7.2.2 三目运算符
条件表达式 ? 表达式1:表达式2;
其代表意思就是,如果条件表达式成立,那么就是表达式1执行,否则就是表达式2执行
如在求两个数的最大值的时候,为了简洁就用到了这个表达式
int max(int x, int y) {
return x > y ? x : y;
}
可以看到拿三目运算符实现可以让这些条件判断语句简单很多
7.2.3 前置++和后置++
这是个特别重要的概念,正如上面在函数生存期里面提到的那个例子一样,对于这两个运算符,需要仔细思考其运算过程,弄清楚到底是怎么走的
int a = 0, b = 0, c = 0;
c = a++;
c = ++b;
对于下面这个程序,我们如果观察其低端汇编语言就会发现,这两个符号都做了哪些工作
从上面汇编语言可以看出,首先将a中的值取出放到eax寄存器中,然后将eax寄存器中存储的值赋值给c,然后将a中的值取出放到寄存器ecx中,然后对ecx中的值执行加1操作,然后将ecx中的值放到a中,从这一过程可以很清楚看到后置++就是先取值,然后再++,前置++也非常容易分析,这里面就不分析啦。
8. 指针
8.1 指针是什么
在计算机内存中存储地址的变量叫做指针变量。在C语言中指针就是地址
8.2 指针的大小
指针的大小对于不同位数的操作系统是不一样的,对于x86即32位系统其大小为4,对于x64系统即64位系统,其大小为8
因为指针指向一个变量地址,那么在计算机操作系统中给变量地址分配的位数,即系统最大支持字长,这也就是为什么指针在不同操作系统中其size不一样的原因
9. 结构体
在数据库中一个一个的表,表中有一条一条的记录,在C语言中就拿结构体来封装一条记录,结构体设计对于一个比较大的项目来说是十分重要的,其在设计阶段的重要性是举足亲重。
9.1 结构体是什么
在C语言中结构体是一种数据类型,是由程序员自己设计的一种数据类型
比如下面这个结构体就是代表一条学生的记录:
struct Student {
char s_id[10];
char s_name[10];
char s_sex[5];
int s_age;
};
int main() {
struct Student stud = { "20200201","shshf","m",21 };
printf("id: %s", stud.s_id);
return 0;
}
对于上面这个结构体,对于其初始化直接按照顺序给结构体中每一个变量进行赋值,取值直接结构体变量名.结构体中变量名
10. 文件
10.1 数据流 DataStream
程序与数据之间的交流并不是像我们理解的那样,直接就能识别数据,然后将数据精准拿到,程序与数据之间的交互是以流的形式进行的
比如在进行C语言文件的存储时,都会先进行“打开文件操作”,此时就会打开数据流,将数据先写到数据流之中,这一数据流与文件对接,写入文件当中,当“关闭文件”时就会关闭数据流
10.2 缓冲区 BufferSpace
比如cpu和键盘的速度根本就不在同一水平线上,这个时候就可以借用缓冲区的作用,用户从键盘输入的数据,存放在缓冲区内,当CPU时间戳跑到这个缓冲区的时候就从缓冲区内读取一个字符,直到读取完毕为止
10.2.1 什么是缓冲区
缓冲区就是在程序执行的时候,在内存中所提供的一块存储空间,可用来暂时存放准备执行的数据,其设置提高了存取效率,因为解决了速度不匹配问题
C语言的文件处理功能最好是使用缓冲区来实现,当时用标准I/O函数,其在stdio.h库中,系统就会自动设置缓冲区,并且通过数据流来读写文件。当进行文件读取时,不会直接对磁盘进行读取,而是先打开数据流,将磁盘上的文件信息拷贝到缓冲区中,然后程序再从缓冲区中读取所需数据
10.3 文件类型
分为文本文件和二进制文件
文本文件是以字符编码的方式进行保存的。二进制文件将内存中数据原封不动存放到文件中,适用于非字符为主的数据。如果以记事本打开,只会看到一堆乱码
除了文本文件,所有的文件都可以进行二进制存储。二进制文件的有点在于存储速度快,占用空间小,以及可以随机存取
11. 关键字
C语言的关键字共有32个,根据关键字的作用,可分为数据类型关键字,控制语句关键字
11.1 数据类型关键字
void char short int long float double
signed unsigned truct union enum auto
static register
//比较重要的关键字
typedef sizeof extern const volatile
11.2 流程控制关键字
if else switch case default for do while
return continue break goto
11.3 sizeof
计算类型的大小或者变量的大小,也就是字节个数
比如以下的sizeof的使用
int ar[3] = { 1,2,3 };
int size_ofAr = sizeof(ar);//这里面的ar不是代表指向数组首元素的指针,在这里面指的是数组ar的大小
//结果为3*4=12
//如果想要计算数组元素个数,需要下面这样计算
int num_ofAr = sizeof(ar) / sizeof(ar[0]);
//结果为3
char br[4] = { "wew" };
int size_ofBr = sizeof(br);
//结果为4
11.4 typedef关键字
其使用规则如下
typedef 存在的数据类型 自定义的数据类型名字
例如下面几句代码
typedef unsigned char unint_8;
typedef unsigned short unint_16;
typedef unint_16 size_t;
从上面这几条代码中,我们将无符号char定义为unit_8
我们将定义的unit_16定义为size_t,从这里可以看出已经被typedef定义过的新称号,还能被再次定义为另一个新称号
11.5 extern关键字
引用别处定义的函数声明或者全局变量
11.6 static关键字
修饰分以下几种情况:
11.6.1 局部变量
下面的例子可以很好说明:
void fun() {
//静态局部变量
static int a = 0;
//局部变量
int b = 0;
a += 1;
b += 1;
printf("a = %d b = %d\n", a, b);
}
int main() {
int i = 0;
while (i < 5) {
fun();
++i;
}
return 0;
}
对于上面这段代码其运行结果为
a = 1 b = 1;
a = 2 b = 1;
a = 3 b = 1;
a = 4 b = 1;
a = 5 b = 1;
因为每次调用调用fun函数的时候局部变量和静态局部变量是不一样的,对于静态局部变量,其存放在.data区域,其只能初始化一次,也就是说第一次的时候被初始化为0,后面就不会再对其初始化了,而对于b由于其是局部变量,所以对于其的初始化每次都可以进行,也就是每次都赋值为0,然后+1操作,而对于a,由于第一次初始化为0成功,后面逐渐对其累加,所以每次打印结果都比上一次+1
11.6.2 全局变量 和 函数
对于两个.c文件,假设是A.c和B.c两个文件,在A.c文件中定义了一个静态全局变量,在A.c中定义了一个静态函数,在B.c中调用extern关键字,想要把A.c中定义的这些静态变量获取到B.c中,这是不能实现的,在编译链接过程中会报错:无法识别的外部符号xxxx。
上面这种情况的代码如下:
test1.cpp
#include<stdio.h>
//全局变量
int g_max = 10;
//静态全局变量
static int g_min = 0;
//函数
void fun() {
}
//静态函数
static void print() {
}
test_extern.cpp
#include<stdio.h>
extern int g_max;
extern int g_min;
extern void fun();
extern void print();
int main() {
fun();
print();
}
其运行结果如下图: