C 语言学习 第12次做业总结

做业总结

本次课堂的内容为字符串相关的几个函数还有结构体。程序员

字符串相关函数

在此以前的课程中,输入主要都是使用scanf这个函数。而在这节课上,冯老师讲解了字符串获取函数gets。在不须要控制符的状况下,gets函数均可以取代scanf数组

  • strcpy函数:实现一个字符串到另外一个字符串的拷贝
  • strcat函数:实现两个字符串链接为一个新的字符串
  • strcmp函数:实现两个字符串大小的比较(基于 ASCII 码)
  • strlen函数:得到一个字符串的长度数据(须要知道的是,字符串的实际长度还须要加一个尾缀\0

结构体

冯老师在课堂上讲了一些结构体的知识,可是结构体中有一些细节的东西没有涉及,我在这里稍做整理,部分同窗后期的课程会遇到(主要针对学习目标为嵌入式的同窗)。函数

位域的概念

以前在课堂上,冯老师会告诉你们,结构体的内容是基本数据类型或者是结构数据类型,这里其实还有一种类型就是位域化的基本数据类型:学习

struct {
  int a;
  int b;
}

先考察上面的这个结构体的内容,它包含有两个基本数据类型的变量ab。在内存中,他们将会占用2 * sizeof(int)个内存空间。在某些状况下,可能仅想要用一位来表示一个数据是否被使用(即真或者假),那么对于上面的结构体,空间就浪费的过于厉害。这时能够修改以上的结构体:设计

struct {
  int a :1;
  int b :1;
}

这时候,这个结构体的内存占用就变成了一个sizeof(int),虽然仍是2int类型的变量,可是每个变量都仅占用一个位的内存。指针

对齐和补齐

虽然结构体和数组都是一种“固实”的数据类型(表如今内存中为连续存储)。可是稍有不一样是的,数组的确是一种真的“固实”数据——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类型都归并到一块儿。

C99 中的结构体初始化的方式

同窗们如今学习的 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)。如今,将内存想象为一个可乐的瓶子,堆内存就是堆在瓶底的内存,它会一点点的向上生长;栈内存就是在瓶顶的内存,它会一点点的向下生长。

  • 堆内存(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
相关文章
相关标签/搜索