本次课堂的内容为字符串相关的几个函数还有结构体。程序员
在此以前的课程中,输入主要都是使用scanf
这个函数。而在这节课上,冯老师讲解了字符串获取函数gets
。在不须要控制符的状况下,gets
函数均可以取代scanf
。数组
strcpy
函数:实现一个字符串到另外一个字符串的拷贝strcat
函数:实现两个字符串链接为一个新的字符串strcmp
函数:实现两个字符串大小的比较(基于 ASCII 码)strlen
函数:得到一个字符串的长度数据(须要知道的是,字符串的实际长度还须要加一个尾缀\0
)冯老师在课堂上讲了一些结构体的知识,可是结构体中有一些细节的东西没有涉及,我在这里稍做整理,部分同窗后期的课程会遇到(主要针对学习目标为嵌入式的同窗)。函数
以前在课堂上,冯老师会告诉你们,结构体的内容是基本数据类型或者是结构数据类型,这里其实还有一种类型就是位域化的基本数据类型:学习
struct { int a; int b; }
先考察上面的这个结构体的内容,它包含有两个基本数据类型的变量a
和b
。在内存中,他们将会占用2 * sizeof(int)
个内存空间。在某些状况下,可能仅想要用一位来表示一个数据是否被使用(即真或者假),那么对于上面的结构体,空间就浪费的过于厉害。这时能够修改以上的结构体:设计
struct { int a :1; int b :1; }
这时候,这个结构体的内存占用就变成了一个sizeof(int)
,虽然仍是2
个int
类型的变量,可是每个变量都仅占用一个位的内存。指针
虽然结构体和数组都是一种“固实”的数据类型(表如今内存中为连续存储)。可是稍有不一样是的,数组的确是一种真的“固实”数据——arr[1]
和arr[2]
之间插不进去一个任意的数据类型。可是结构体就不一样了。code
这里先看一个例子:对象
struct { int a; // 假设 int 是 32 位的数据 char b; // 假设 char 是 8 位的数据 int c; }
对这个结构体进行sizeof
操做就会发现结果是12。这时你会好奇的发现,两个 int
加一个 char
不该该是 9 吗,这里为何变成 12 了呢。其实在结构体中,有这样的一个要求:结构体内部的数据,老是按照最大的那个数据类型进行对齐(可是当最大的数据类型已经超过 4 个字节,则始终按照 4 个字节对齐)。这里,能够看到数据类型最大的是 int
,它占用 4 个字节,那么第二个类型 char
只能按照它进行对齐(见下图,每个方块都是一个 sizeof(char)
)。这里,第二行存在 3 个位置的空洞,这些空洞是理论上能够存储数据,可是被浪费掉的blog
也许你会好奇这有什么用处呢。考虑如下的结构体:内存
struct{ int a; char b; int c; char d; int e; char f; int g; char h; }
在对以上的知识有了解后,你必定就能够知道这样的结构体的设计是不合理的,最好是将几个char
类型都归并到一块儿。
同窗们如今学习的 C 语言语法大多数都是按照 C89 标准来的,C99 中稍微增长了一点其余的东西,和结构体相关的有如下这个方法:
在 C89 标准中,若是想要对结构体中的成员变量进行初始化,你可能会那么作:
struct ABCD abcd; abcd.a = 10; abcd.b = 20; abcd.c = 30; abcd.d = 40;
而在 C99 标准中,提供了一种新的初始化方法:
struct ABCD abcd = { .a = 10; .b = 20; .c = 30; .d = 40; }
经过查看同窗们这次做业的内容,主要发现如下的 3 个问题。
虽然同窗们在以前的总结中都一再的强调说指针变量须要先明确的指向一个对象,而后才能使用,可是在实际的代码中,的确是屡次发现有同窗没有初始化指针变量就使用它(或者是指针变量赋值为 null 后使用它)。对于指向 null 的指针变量,虽然使用通常不会出错,可是仍是不建议使用,毕竟它自己没有什么意义。何况假如将多个功能不一样的指针,都指向同一个地址,稍微用几回,也都会出现问题。
这个错误很典型:在同窗们的成绩管理系统中,添加新成员的时候,不少同窗首先在函数中定义一个结构体对象,而后对对象中的成员变量赋值,在最后将这个结构体对象的地址直接赋值给形参。
出现这个错误,不怪你们,由于你们都尚未学习过计算机原理相关的知识,不知道在内存中有堆内存和栈内存的概念。这里先跟你们简单的介绍下:
在计算机系统中,程序使用的内存,分为不少种,其中和变量相关的通常是如下两种:堆内存(heap)和栈内存(stack)。如今,将内存想象为一个可乐的瓶子,堆内存就是堆在瓶底的内存,它会一点点的向上生长;栈内存就是在瓶顶的内存,它会一点点的向下生长。
在脑海中存在这样的两个简单的概念后,咱们返回到程序代码中去。这里先理解这个概念“程序员分配和释放”,它是什么意思呢。通常来讲在不特殊指明的状况下,咱们均可以将程序员分配假想为函数malloc
或者是 C++
中过的操做符 new
。而将释放假想为free
函数。到这里,你们就能够发现了,至今本身都尚未学习过与此相关的函数,因此到目前为止全部的变量都是分配在栈上的。栈上的内存有什么特色呢——它是由编译器自动分配和释放。
先将这个概念稍微放置下,回忆我以前总结中说起的子函数的特色:
在 C 语言中,一切的传参都是值传递。
假想存在一片广袤的土地。子函数就是在须要调用它是时候,在这片土地上圈了一块地,在其中挖坑也罢,种树也好,可是一旦这个函数结束,将会有一辆看不见的推土机轰隆而来,将这圈起来的土地从新归于广袤。
以上,就是子函数的特色。如今,开始看代码:
#include <stdio.h> void return_func(int *arr){ int a = 10; arr = &a; } int main(){ int aa = 0; return_func(&aa); printf("%d\n",aa); return 0; }
在这个代码中存在一个名为return_func
的函数,这个 函数的目标就是将传入的变量的值改成 10。这个函数,和同窗们此次做业要作的内容极其类似,只是我这里将它简化成上面的简单版本。
按照同窗们的理解,这里最终应该可以实现输出 10 这一结果。可是实际上,最终的输出依旧是 0。为何函数不能生效呢?!如今,用上面形容子函数的方法来看这个题目。
首先,请同窗们不要将我概括的“在 C 语言中,一切的传参都是值传递”奉为金圭玉臬。由于到如今都未曾在一本 C 语言书中看到相似的话。
// 从主函数开始看起 int main(){ int aa; // 假设变量 aa 的地址是 0x0000 0001 return_func(&aa); // 开始调用子函数,由于须要传递参数 // 且参数为一个地址,因此经过 & 得到 aa 的地址 0x0000 0001 后传入函数 // 调用函数后进入子函数体,由于传入的参数是一个指针,因此先须要构造一个变量存放 aa 的地址 // 因而,子函数至关于变成: // void return_func(){ // int *arr = 0x0000 0001; // int a = 10; // arr = &a; // } printf("%d\n",aa); return 0; }
继续以上的子函数,子函数已经变形为上面的样子了,下面开始学说二:清空学说:
void return_func(){ int *arr = 0x0000 0001; int a = 10; // 假设 a 的地址是 0x0000 0002 arr = &a; // 那么这里的 arr 存储的内容就变成为 0x0000 0002 }
能够看到这里的 arr 是一个局部的变量,在这个函数中,它后来被赋值为 0x0000 0002。看到这里应该就明白了吧——明明是想要将地址 0x0000 0001 对应的内容修改成 10,可是最后却将这个地址指向了 0x0000 0002。最最最关键的是,按照清空学说,最后这个变量的地址尚未了。到这里,错在哪里就很显然了。
本次成绩统计说明:本次成绩,一旦发现做业抄袭状况,则代码部分直接清零(冯老师以前和同窗有约定),抄袭状况暂时尚未整理完毕,做业的完整总结明天给出。
编号 | 学号 | Cnblogs昵称 | 代码 | 总结 | 加权得分 | 备注 |
---|---|---|---|---|---|---|
1 | 160809401 | 付胤 | 0 | 60 | 24 | 代码没有按照题目要求编写,没有第一题和第二题,第三题没有实现需求 |
2 | 160809402 | 张博洋 | 0 | 59 | 23.6 | 没有写代码,总结敷衍 |
3 | 160809403 | 董宇豪 | 75 | 59 | 68.6 | 变量名不可以见名知意 |
4 | 160809404 | 朱念齐 | 35 | 59 | 44.6 | 使用指针数组解决问题,代码2是有问题的代码,没法经过编译 |
5 | 160809405 | 芦彦儒 | 0 | 80 | 32 | 代码提交时间:2016-12-15 16:42,同11号 |
- | ------ | ------ | --- | --- | --- | ------------------------------------- |
7 | 160809407 | Leonardo#* | -100 | -100 | -100 | |
8 | 160809408 | iL.linker | 90 | 85 | 88 | |
9 | 160809409 | gdcs16_409 | -100 | -100 | -100 | |
10 | 160809410 | 无声的梦 | 80 | 85 | 82 | 代码提交时间:12-12 12:57 |
11 | 160809411 | 刘悦 | 0 | 60 | 24 | 代码提交时间:2016-12-15 13:59,同10号 |
12 | 160809412 | 张磊 | 85 | 75 | 81 | |
13 | 160809413 | 王洪烨 | 0 | 85 | 34 | 代码提交时间:2016-12-12 11:47,同26号 |
14 | 160809414 | 纪柏如 | 85 | 80 | 83 | |
15 | 160809415 | 闫墨杰 | 70 | 90 | 78 | 做业3实现不完整 |
16 | 160809416 | 史航 | 60 | 75 | 66 | 做业3没有写,做业1编写正确 |
17 | 160809417 | 狂欢 | -100 | -100 | -100 | |
18 | 160809418 | 水母Jam | -100 | -100 | -100 | |
19 | 160809419 | 朱钰铖 | -100 | -100 | -100 | |
20 | 160809420 | 虞小生 | 40 | 70 | 52 | 代码3没有写,代码1实现错误,使用strcpy没法实现长度判断 |
21 | 160809421 | 饮冰少年1 | 60 | 60 | 60 | |
---- | ---------- | --------- | --- | --- | --- | ----------------------------------------- |
23 | 160809423 | 李筱 | -100 | -100 | -100 | |
24 | 160809424 | Xzy! | 30 | 65 | 44 | 做业3没有写,做业1实现错误 |
25 | 160809425 | 刹那神华 | 70 | 70 | 70 | 做业3仅实现增长同窗的函数 |
26 | 160809426 | zlt.Santorini'Ly | 60 | 80 | 68 | 代码提交时间:2016-12-11 21:28 |
27 | 160809427 | 江超民 | 0 | 90 | 36 | 代码提交时间:2016-12-14 22:14,同26号 |
28 | 160809428 | zxkai | 90 | -100 | 14 | 没有总结 |
29 | 160809429 | 王鑫沐 | 70 | 85 | 76 | |
30 | 160809430 | 茉妍 | 65 | 85 | 73 | |
31 | 160809431 | 茉莉雨 | 95 | 85 | 91 |