目录
一、C语言
C语言:一门通用计算机编程语言,广泛应用于底层开发。
C语言的设计目标:提供一种能以简易的方式编译,处理低级存储器,产生少量的机械码以及不需要任何运行环境支持便能运行的编程语言。
二十世纪八十年代,为了避免各开发厂商用的C语言语法产生差异,由美国国家标准局为C语言制定了一套完整的美国国家标准语法称ANS1C作为C语言最初的标准。
衡量一门计算机语言好与否?可以看其是否有国际标准。
C语言早期有C89,再后来出现了C90,C99,C11…但从C90之后就不再流行,因此主要谈的语法标准就是C89,C90啦。
二、数据类型
char //字符数据类型
short //短整型
int //整型
long //长整型
long long //更长的整型
float //单精度浮点数
double //双精度浮点数
先看图进行理解:
由图我们需要注意以下几点:
- 字符要用单引号表示,例字符a--‘a'
- %d表示打印一个整数
- sizeof:关键字-操作符-计算类型或者变量所占空间的大小(后边会讲)
- sizeof的单位是什么?-字节 byte
由此引申,我们需要学习到计算机中的单位:
- bit-最小单位:比特位-1个比特位用来存放一个二进制位
- byte-字节=8bit
- kb=1024byte
- mb=1024kb
- tb=1024mb
- pb=1024tb
再继续看上图,我们对每种数据类型所占空间的大小有了初步的概念。
可是,为什么long(长整型)没有比int(整型)占据的空间大呢?原因如下,
C语言标准:sizeof(long)>=sizeof(int).
三、常量与变量
C语言中,称不能改变的量为常量,能被改变的量为变量。
1.变量
创建一个变量,格式为:"类型 变量的名字=xxx".如图所示:
- 注:%d---int整型 %f---float %lf---double
(1)变量的分类:局部变量与全局变量
- 局部变量---{}内部定义的
- 全局变量---{}外部定义的
- 当局部变量和全局变量冲突的情况下,局部变量优先
- 不建议将局部变量和全局变量的名字写成一样的
具体如图所示:
(2)变量的作用域和生命周期
(1.2.1)作用域(scope)
程序设置概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用的。而限定这个名字的可用性的代码范围就是这个名字的作用域。
- 局部变量的作用域:变量所在的局部范围
- 全局变量的作用域:整个工程
可是不在一个源文件也可以吗?用"extern"声明一下即可。具体操作如图所示:
(1.2.2)生命周期
变量的生命周期指的是变量的创建到变量的销毁之间的一个时间段。
- 局部变量的生命周期:进入局部范围生命周期开始,出局部范围生命周期结束
- 全局变量的生命周期:整个程序的生命周期
2.常量
C语言中的常量和变量的定义形式有所差异。
(1)字面常量
如图为一些字面常量(无意义但存在)的示例:
(2)const修饰的常变量
首先咱们先来看一个程序:
我们可以了解到num是一个变量。
那么如果我们不想让它变呢?在"int num=45"前面加上"const"就可以了,但这时会出现报错:
因为已经被const进行了固定,此时num自然是无法进行改变了。
现在的num叫常变量---具有常属性(不能改变的属性)
注意:num依然还是一个变量!本质不变!如何证明呢?
咱们再看一个例子:
根据图中所示的错误,我们思考在"int n=10"的前面加上“const”能不能行呢?
也就是说,常变量行不行呢?咱们继续看图:
错误依然如此,于是我们得出了结论:这里需要常量的地方它们都不行。“n”的本质属性依然是变量!(包括上面的“num”!)只不过被“const”修饰了一下。
(3)#define 定义的标识符常量
同样通过例子进行理解,首先我们将“Max”定义为300,如图:
此处“Max”是标识符常量,Max无法中途继续更改,否则会出错。
(4)枚举常量
可以一一列举的常量。
咱们依然通过一个例子进行切入了解。枚举常量的性别方式如何定义性别?如图:
注意枚举关键字enum,还有枚举常量。
我们再打印出来看看:
于是我们了解到了:枚举常量是常量,它的值默认第一个从“0”开始,持续下一个+1。
我们可以给第一个的"MALE"赋初值(不是改变它的值!)。
操作和结果如图:
注意赋初值的位置,别弄错啦,不然就运行不起来嘞。
四、字符串
字符串就是一串字符---用双引号括起来的一串字符。
例:"hello world.\n"
这种由双引号(Double Quote)引起来的一串字符称为字符串字面值(String Literal),或者简称字符串。
字符串的结束标志是一个'\0'的转义字符。在计算字符串长度的时候'\0'是结束标志,不算作字符串内容。如何证明呢?
我们先拟一个字符串“hello”,它有多个字符,如果要将它存起来,这里我们就要引入字符数组的概念了(数组:一组相同类型的元素),用字符数组将它存起来。
我们观察一下需要几个元素,先一直按“F10”进行调试,然后通过下图所示路径进行监视:
监视结果如图:
由图可知:字符串在结尾的位置隐藏了一个'\0'的字符,'\0'是字符串的结束标志。
关于'\0'是字符串的结束标志,接下来咱们进行新的验证。看图:
对比这两个数组元素监视的结果,可以看出arr1比arr2多了一个'\0'。
打印出来的结果如图:
为什么arr2的结果出现乱码呢?这时我们结合前面的对比,可以大胆猜测问题是不是出现在'\0'上。
于是我们进行改变,并重新打印。结果如图:
不难发现,改变前的arr2,没有‘\0’的结束标志,于是就形成了乱码,'\0'的重要性得以凸显。
还有一种验证方法,我们以“求字符串的长度”进行切入。
先认识一个相关函数:strlen(string length)
咱们用它来求“abc”的字符串长度。如图:
图中出现的警告,其实是告诉我们没有引用头文件,咱们在前面开头加上"#include<string.h>"就可以啦。
通过前面显示结果为”3“我们也得知,当我们求字符串长度的时候,'\0'是不算在里边的。
我们继续求前面的字符串长度,如图:
实际上运行出来的结果,第一个的"3"是准确的结果,第二个的"42"是随机值。
为什么会这样呢?我们再次想到前面对两组数组对比的结果---'\0'的差异。
再次进行修改,结果如图:
再次得到证实:'\0'是字符串的结束标志。
五、转义字符
我们先看一个程序:
为什么会打印出图中框起来的三个结果呢?因为转义字符转变了它原来的意思!
转义字符大致有下面这些:
我们继续看一些实际应用的例子,加深理解:
- 注:%c---char字符 %s---字符串(printf在打印数据类型的时候,可以指定打印的格式)
有两种打印字符非常特殊,我们来重点了解:
如图所示:
- 八进制的126是十进制的86,ASCII码值为88的字符为'V'
- 十六进制的54是十进制的84,ASCII码值为84的字符为'T'
这里我们引入ASCII的概念:ASCII(American Standard Code for Information Interchange,美国信息互换标准代码)是一套基于拉丁字母的字符编码,共收录了 128 个字符,用一个字节就可以存储,它等同于国际标准 ISO/IEC 646。
六、注释
1.注释的用途
A.代码中有不需要的代码可以直接删除,也可以注释掉
B.代码中有些代码比较难懂,可以加一下注释文字,用来解释复杂代码
2.注释的分类及方法:
(1)普遍适用
选中行,按快捷键"CTRL+K,CTRL+C",或者按图中所示图标:
(2)C++注释风格
在代码前加上'//',如图:
(3)C语言注释风格
在代码开头前加上'/*',结尾后加上'*/',如图:
注意:C语言和C++的注释风格哪种更好呢?答案是C++。举个例子,在中间已有内容被注释的情况下,我们不想要那一整段代码了,用C语言注释风格会怎样呢?如图:
我们发现,遇到第一个'*/',注释就会结束---因为C语言注释风格不支持嵌套注释。
七、选择语句与循环语句
1.选择语句
选择语句是一种控制结构,用于在程序中根据条件的真假来选择不同的执行路径。它让程序有了智能,可以根据输入或条件来做出不同的响应。
最常见的选择语句包括:if语句、if-else语句和switch语句。举例:
2.循环语句
我们将实现循环结构这种功能的语句称之为循环语句。
最常见的循环语句包括for语句、while语句、do...while语句。举例:
八、函数与数组
1.函数
函数是指将一组能完成一个功能或多个功能的语句放在一起的代码结构。在C语言程序中,至少会包含一个函数,即主函数main()。
来一起稍微细致地了解一下吧!如果我们要完成两个数的相加,可能会写出下面的代码:
如果用函数的方式相加呢?看下图:
正如上图所示,我们的运行程序引入了新的函数Add(),看似变复杂了,但我们需要知道,如果运行的是一个很复杂的函数,需要多次不连续的相加运算,那么第二种方式更能提高效率。
2.数组
定义:一组相同类型元素的集合。
举例:将10个整型1-10存起来,字符数组。如图:
- 不完全初始化,剩余的默认为0
- 数组是用下标来访问的
数组如果初始化,可以不指定数组的大小,如图:
如何运用数组呢?我们写一个循环并调试,如图:
- 注:不断按"F10"调试,图中所框数值会根据循环,变化,增加
九、操作符
1.算术操作符
+ - * / %
假设我们计算12除以5的结果,程序如图:
- 注:除号“/”两端,至少一个数是小数,执行的就是小数除法,反之则反
“%”---取模(取余),运算如图:
2.移位操作符
>> << ——移动的是二进制位
我们先看如图所示的程序:
结果是如何运算出来的呢?这里需要我们回忆一下前面数据类型中所提及的:a是一个整型,1个整型占4个字节,1个字节等于8个比特位。也就是说,"int a=8"的二进制序列为:
">>2"表示把'a'的二进制序列向右移动两位,过程及结果如图:
转换成十进制数,也就是我们最先开始所得到的结果“2”啦!
3.位操作符
& 按位与 | 按位或 ^ 按位异或
4.赋值操作符
= += -= *= /= &= %= ^= |= >>= <<=
修改变量的原始值,理解示例如图:
5.单目操作符
单目操作符只有一个操作数
- 举例:"a+b"中的'+'有两个操作数,为双目操作符
单目操作符大致有下面这些:
- '&'与'*'在后面一点详细讲
(1)关于'!'的大致运用如图:
- C语言规定,0表示假,非0就是真
(2)"sizeof"是一个操作符,计算类型或者变量的大小,单位为字节
当"sizeof"计算一个变量大小时,如图:
- "sizeof"求变量时后面的括号可以省略,求类型时括号不能省略,通常都不会省略
- "sizeof"求变量时后面的括号可以省略,因此"sizeof"不是函数
当"sizeof"计算数组大小时,如图:
(3)'~'表示按(二进制)位取反,把所有二进制中的数字,1变成0,0变成1
一个整数的二进制表示有3种(原码、反码、补码),整数在内存中储存的是补码。
正整数的原码、反码、补码相同。
针对于负整数,我们看图:
- 符号位(最左边的位),1表示负数,0表示正数
- 反码:原码符号位不变,其他位按位取反
- 补码:反码的二进制序列+1
关于按位取反的运算,接下来看几个例子。
(3.1)正整数9,按位取反结果为1
运算过程如图:
(3.2)负整数-5,按位取反结果为4
运算过程如图:
(3.3)0,按位取反结果为-1
运算过程如图:
(4)'++'与'--':i++表示先使用i,再执行i=i+1;++i表示先执行i=i+1,再使用i。'--'同理。
示例如图:
(5)(类型)表示强制类型转换
先看一个会出现警告的例子,如图:
如果不想看到警告,这时可以采取强制类型转换,具体如图:
- 一般不推荐使用强制类型转换
6.关系操作符
> >= < <= != 不等于 ==
7.逻辑操作符
&& 逻辑与 || 逻辑或 ——判断真与假的逻辑运算
(1)逻辑与&&,是一种逻辑运算,它要求所有参与运算的条件(或称为操作数)都为真(true)时,整个运算结果才为真(1);否则,只要有一个条件为假(false),结果就为假(0)。
(2)逻辑或||,是一种逻辑运算,它要求参与运算的条件中至少有一个为真时,整个运算结果就为真(1);只有当所有条件都为假时,结果才为假(0)。
可以看一下如何运行,如图:
8.条件(三目)操作符
exp1 ? exp2 : exp3
- exp1成立,exp2计算,整个表达式的结果是:exp2的结果
- exp1不成立,exp3计算,整个表达式的结果是:exp3的结果
看一个例子帮助理解吧,如图:
9.逗号表达式
逗号表达式:逗号隔开的一串表达式。
同样,先看一个例子,如图:
- 逗号表达式是从左往右依次计算的
- 整个表达式的结果是最后一个表达式的结果
10.下标引用、函数调用和结构成员
[ ] ( ) . ->
- '.'与'->'在结构体中讲
(1)下标引用操作符 '[ ]'
使用示例如图:
- 数组的下标是从0开始的
(2)函数调用操作符 '( )'
使用示例如图:
- 可以传任意数个参数
十、常见关键字
- C语言提供的,不能自己创建关键字
- 变量名不能是关键字
下面谈谈部分关键字
1.extern——用来声明外部符号
2.signed——有符号的、unsigned——无符号的
3.void——无、空
4.auto
auto 在C语言中主要用于强调变量是自动存储的,但在实际编程中,这个关键字很少被显式使用,因为所有局部变量默认就是自动的——是自动的,每个局部变量都由auto修饰。
如图:
- 此处"num"自动创建,自动销毁——自动变量
5.break
(5.1)在循环中使用 break 语句时,它会导致循环立即终止,不再执行循环体中剩余的代码,也不再进行循环条件的判断,而是直接跳出循环,执行循环之后的代码。简例如图:
(5.2)在 switch 语句中,break 语句用于防止代码自动地继续执行到下一个 case 标签。如果不使用 break,则一旦某个 case 的条件满足,就会执行该 case 下的所有语句,直到遇到 break 或 switch 语句的末尾。简例如图:
6.register——寄存器关键字
大量/频繁被使用的数据,放在寄存器中,可以提升效率。简例如图:
- 通常编译器会自动将合适的数据存储到寄存器中
这里我们对寄存器进行一些了解,如图:
寄存器容量小但速度非常快,能够支持CPU的高速指令执行和数据处理。
7.static——静态的
(7.1)static修饰局部变量
先理解如图所示的程序:
- 'a'为局部变量——自动创建,自动销毁
如果用static修饰'a',结果如何呢?如图:
这里我们引入了解:static修饰局部变量,改变了局部变量的生命周期(本质上是改变了变量的存储类型)。如图:
- 栈区、堆区和静态区是程序内存管理中的三个重要部分
(7.2)static修饰全局变量
先理解如图所示的程序:
- 全局变量在整个工程中都可以使用
加上static修饰的结果如图:
由此我们得出结论:static修饰全局变量,使得这个全局变量只能在自己所在的源文件(.c)内部可以使用,其他源文件不能使用。
- 全局变量,在其他源文件可以被使用,是因为全局变量具有外部链接属性,但是被static修饰之后,就变成了内部链接属性,其他源文件就不能链接到这个静态的全局变量了。
(7.3)static修饰函数
同样,先理解如图所示的程序:
加上static修饰的结果如图:
由此我们得出结论:static修饰函数,使得函数只能在自己所在的源文件内部使用,不能在其他源文件内部使用。
- 本质上,static是将函数的外部链接属性变成了内部链接属性(和static修饰全局变量一样)。
8.typedef——类型重定义
typedef 用于为现有的数据类型定义一个新的名字(别名)。typedef 可以增加代码的可读性,使得复杂的类型声明变得简单明了。使用 typedef 可以避免在代码中重复冗长的类型声明,尤其是在使用结构体(struct)、联合体(union)、枚举(enum)等复合数据类型时。简例如图:
9.union——联合体(共用体)、volatile暂时跳过
10.注意define、include均不是关键字,而是预处理指令。这些指令在编译之前由预处理器处理,用于执行一些特殊的操作,如宏定义、文件包含等。
十一、#define定义常量和宏
"define"是一个预处理指令
1.define定义符号
简例如图:
2.define定义宏
简例如图:
- 为了避免潜在的优先级问题,通常建议将宏的参数和整个宏体都放在括号中
十二、指针
指针是一种数据类型,它存储的是变量的内存地址,而不是变量的值本身。指针提供了一种访问和操作内存地址的操作,使得程序能够直接访问和操作内存中的数据。
- 指针就是地址
1.思考——内存是如何编号的以及内存所占空间大小
内存:内存是电脑上特别重要的存储器,计算机中所有程序的运行都是在内存中进行的。
内存单元:所以为了有效地使用内存,就把内存划分成一个个小的内存单元,每个内存单元的大小是一个字节。
地址:为了能够有效地访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地址。
地址线:计算机通过地址线来生成地址。例如:在32位计算机中,有32根地址线,每根地址线可以表示0或1(即二进制中的一位),因此总共可以表示2^32(即4,294,967,296)个不同的地址;在64位计算机中同理。
电信号:地址线通过通电(正电为1,负电为0)来产生电信号,这些电信号被转换成由1和0组成的二进制序列,从而生成具体的地址。
以32位计算机为例,内存编号如图:
内存所占空间大小计算如图:
- 同理:64位计算机内存大小为8GB
2.相关基本概念
内存地址:计算机内存中的每个字节都有一个唯一的编号,这个编号就是内存地址。
指针变量:指针变量是存储内存地址的变量。指针变量的类型决定了它所指向的内存地址中存储的数据的类型。
解引用:通过指针访问它所指向的内存中的数据的过程被称为解引用(或称为间接访问)。解引用指针通常使用 '*' 操作符。
观察一下内存情况,先一直按“F10”进行调试,然后通过下图所示路径操作:
还可以打开监视的窗口,如图:
- "int a"在内存中要分配空间——4个字节
- 每一个字节都有一个地址,但"&a"只会取第一个字节的地址
继续了解,如图:
- "%p"---打印地址
- "pa"为指针变量,用于存放地址
- '*' 说明"pa"是指针变量,"int"说明"pa"指向的对象为int类型
- "&a"运算符用于获取变量'a'的内存地址,并将该地址存储在指针变量"pa"中
再来看一个例子,如图:
- '*'---解引用操作:"*pa"就是通过"pa"里边的地址,找到a
3.指针变量的大小
指针的大小是相同的。因为指针变量是用来存放地址的,指针需要多大空间,取决于地址的存储需要多大空间。
以32位平台为例,如图:
- 32位:32(bit)/8=4(byte) 64位:64(bit)/8=8(byte)
十三、结构体
在C语言中,结构体是一种用户自定义的数据类型,它允许你将不同类型的数据项组合成一个单一的类型。结构体是一种复合数据类型,可以用来表示具有多个属性的复杂对象。通过使用结构体,你可以将相关的数据项组织在一起,形成一个有意义的整体,从而便于数据的管理和操作。
- 结构体使得C语言有能力描述复杂类型,可以让C语言创建新的类型出来
假设我们创建“一只小狗”,具体理解如图:
- "struct dog* " 为结构体指针,"(*pd)"=="n_1"
- 访问结构体成员变量时,需要使用点号 '.' 操作符(对于结构体变量)或箭头 '->' 操作符(对于指向结构体的指针)。
本文到此结束啦,如有错误求指正!同时也感谢每一位耐心阅读至此的朋友,共勉之!✧٩(ˊωˋ*)و✧