今天上完课,来更新第二弹,今天主要的内容就是初阶的数组,操作符和指针,结构体和调试技巧的知识,这篇文章应该就把C语言初阶的东西描述完了,接下来的计划就是更新进阶内容和敲的代码题,我觉得有意思的都会定期送上来。
一.数组
1.数组[]内部不准使用变量
2.数组初始化时可以使用字符串来初始化,但注意\0也会放在字符串的最后。
3.数组的访问使用下标访问,从0开始。
4.数组在内存中的存储是从低地址到高地址连续存放。
5.数组存在越界问题。
5.数组名在传参的时候是作为首个元素的地址传过去,本质上是一个指针,简而言之,数组名就是首元素地址,但这里有两个例外:
6.这里利用数组名可以设计三子棋和扫雷游戏,这两个游戏初步地体现了一个小程序的构思和写法,我会选择的写一篇出来,敬请期待。
(其实数组的知识还是比较简单的)
二.操作符
操作符这一章的内容特别细碎,从操作符分类开始,包括各个操作符的详细含义,还是很多很杂的,我挑一些常用的写出来。
1.算术操作符其实需要注意的就是除法运算和取模运算的符号不要搞混了。
2.移位操作符:
2.1左移操作符,左移右补0,这个操作符可以用来计算2的n次方,只需要1向左移动n位就可以。
2.2右移操作符,这个没太大意义,如图
3.位操作符,
这里需要注意的就是按位异或操作符,他的规则是相同取0,相异取1, 这里有一道面试题供参考,题目:不能创建临时变量(第三个变量),实现两个数的交换。
练习:编写代码实现:求一个整数(-1)存储在内存中的二进制中1的个数。
方法1:这里注意的就是n&(n-1)的运用
方法2:这里的方法就是用输入和1左移i位做按位与运算
4.逻辑操作符:&&逻辑与。|| 逻辑或
下列程序输出的结果是1 2 3 4 ,当执行第二句时,a为0导致后面的表达式不再执行。
5.逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
6.对于结构体成员访问,有两种情况,第一就是:结构体名.变量名,或者结构代表的指针名—>变量名。
7.表达式求值的顺序一部分是由操作符的优先级和结合性决定。(不能确定唯一值)
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。
8.整型提升:
8.1整型提升的意义:C的整型算术运算总是至少以缺省整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
8.2整型提升的规则:
1.整形提升是按照变量的数据类型的符号位来提升的
2.无符号整形提升,高位补0
9.算术转换:
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类 型,否则操作就无法进行。下面的层次体系称为寻常算术转换,使用下图规则。
10.复杂表达式求值要考虑操作符的优先级,结合性和是否控制求值顺序,这个很麻烦,最好时自己在书写代码的时候使用括号将优先和结合规定好。
三.初阶指针
1.指针就是地址,口语中说的指针通常指的是指针变量
指针是用来存放地址的,地址是唯一标示一块地址空间的。
指针的大小在32位平台是4个字节,在64位平台是8个字节。
2.指针类型
char* 类型的指针是为了存放 char 类型变量的地址。
short* 类型的指针是为了存放 short 类型变量的地址。
int* 类型的指针是为了存放 int 类型变量的地址。
2.1指针+-整数:指针的类型决定了指针向前或者向后走一步有多大(距离)。如下图,pc指针为char类型,pc+1只向前访问了一个字节,而整型指针pi向前访问了四个字节。
2.2指针的解引用
3.野指针,首先野指针的成因有两个,第一是指针未初始化,第二是指针越界访问
其次解决野指针的办法就是
1. 指针初始化
2. 小心指针越界
3. 指针指向空间释放即使置NULL
4. 避免返回局部变量的地址//即写出这种 return &a;这种代码。
5. 指针使用之前检查有效性
4.指针运算
4.1指针加减整数:上面已讲
4.2指针减指针:得出的是两个指针所指向地址中间的元素个数
4.3指针的关系运算
5.指针运算可以放到数组里面来进行,可以遍历数组的每一个内容。
6.二级指针的本质也是指针,他是指针变量的地址。
7.指针数组的定义,本质上是数组
arr3是一个数组,有五个元素,每个元素是一个整形指针
四.结构体
结构体的初阶还是非常简单的,这里就简单说一下,下面这个代码应该差不多了
还有一点就是结构体传参的问题,先上结论,函数传参的时候,参数是需要压栈的。 如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。所以结构体传参的时候,要传结构体的地址。代码演示如下。
五.VS2019调试技巧
1.调试就是debug的过程,通常代码分为两种版本,一种是debug,一种是release
Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。
Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优 的,以便用户很好地使用。比较以下两图代码,在第一张图release版本中,代码没有陷入死循环,但在debug版本中,代码会陷入死循环,这是因为优化导致的。
死循环原因:局部变量在栈区中创建,而栈区的使用习惯是从高地址向低地址使用,首先创建了i变量,再创建了arr数组,由于数组的访问是从下标0开始从低到高访问,如图二所示,正好访问第12时,将i置0,陷入死循环。
2.调试的快捷键及其含义
F5 启动调试,经常用来直接跳到下一个断点处。
F9 创建断点和取消断点 断点的重要作用,可以在程序的任意位置设置断点。 这样就可以使得程序在想要的位置随意停止执行,继而一步步执行下去。
F10 逐过程,通常用来处理一个过程,一个过程可以是一次函数调用,或者是一条语句。
F11 逐语句,就是每次都执行一条语句,但是这个快捷键可以使我们的执行逻辑进入函数内部(这是最 长用的)。
CTRL + F5 开始执行不调试,如果你想让程序直接运行起来而不调试就可以直接使用
3.调试的时候查看程序当前信息
其中有查看临时变量,查看内存,查看调用堆栈,查看汇编信息,查看寄存器信息五种,分别都能在调试的窗口中找到。
4.常用的coding技巧
1. 使用assert 2. 尽量使用const 3. 养成良好的编码风格 4. 添加必要的注释 5. 避免编码的陷阱。
assert放在下面的最后模拟实现strlen和strcpy讲,首先讲const
const修饰指针变量的时候:
1. const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。但是指针变量本身的内容可变。
2. const如果放在*的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变
5.编程常见的错误有三种,分别是编译型错误,链接型错误和运行时错误
6.模拟实现strcpy (这里会讲到assert和const)
6.1const此处能够实现代码的鲁棒性,为了防止*src被修改,一旦代码中写出修改了源的代码,就会直接报错。
6.2对于strcpy函数,返回值需要返回目标文件的起始地址,这里就是dest,但是dest一步步被修改过,所以开头必须定义char*cp=dest,最后返回cp
6.3这里使用assert就是为了防止dest和src是空指针,即dest!=NULL
7.模拟实现strlen,这里只要参考strcpy的实现即可。
今天的内容就到这里了,有什么问题可以留言哟!