哈佛大学CS50课程笔记(基础)

xiaoxiao2021-02-28  12

1.当函数加载的时候,main函数最先被加载(函数被加载的时候先加载参数如果有的话然后再为函数内部的变量分配空间),然后再依次加载main函数中调用的其他函数,调用完毕,释放空间,最后只剩下main函数,main函数执行完,释放main函数的空间,举个例子,当main中调用了notMain0函数的时候,内存中为notMain0创建空间,notMain0函数调用完毕,空间释放,然后main函数中调用了notMain1函数,内存为notMain1函数创建空间,notMain1函数中调用了notMain2函数,内存为notMain2函数创建空间,notMain2调用完毕,内存释放notMain2的空间,然后内存释放notMain1的空间,最后只剩下main函数的空间, 释放内存的意识不是擦除这个内存里的信息,也不是将这个内存里的东西全部置为0,而计算机忘了这里面是什么,标记这个地方为未使用的内存, 每个函数都有自己的内存块,其他函数看不见,除非用指针操作 所以在一个函数(A)中调用另一个函数(B)时,函数B会在内存中申请单独的内存块,但是传参的时候,函数B是不能使用函数A中的内存的,这两个函数的内存是隔离开的,所以实际上,传参的时候只能将参数复制一份放到函数B的内存空间中,                    内存( C语言的内存体系,不适用与Java )     ——————————     |             text                 |    编译好的二进制文件,加载到内存中用于运行    ,程序的运行地址     ——————————     |      initialized data     |    初始化的数据  包括  全局变量,静态变量等     ——————————     |   uninitialized data   |    未初始化的数据     ——————————     |             heap               |     堆,向下创建空间 ,     |            (down)             |     |                                    |     |                                    |     |                                    |     |               (up)               |     |              stack              |    栈,向上创建空间 实际上这里是高地址,当main中调用别的函数的时候是在低地址分配空间     ——————————     | environment variables |     环境变量     ——————————     关于内存中的的空间的分配,见 http://blog.csdn.net/hairetz/article/details/4141043 栈区(stack)—   由编译器自动分配释放   ,存放函数的参数值,局部变量的值等。其     操作方式类似于数据结构中的栈。     栈中存放 参数 局部变量 返回地址 等 堆区(heap)   —   一般由程序员分配释放,   若程序员不释放,程序结束时可能由OS回     收   。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。  全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的     全局变量和静态变量在一块区域,   程序结束后由系统释放。 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。   -   程序结束后由系统释放。 堆是可以动态内存分配的地方 当你预先不知道程序运行的时候需要分配多少内存的时候就会使用堆(比如要存储用户的输入),而且可以由程序员来申请释放 缓冲区溢出攻击 : 当你不检查数组的边界的时候,系统中一个函数调用另一个函数,在这个函数的内存块中分派了一个空间存储应该返回的主程序的地址,然后又分派了一个长度为10的整形数组(在存贮应该返回的主程序的地址的空间的地址上面,即低地址),这个时候用户输入了一个长度为12的数组,因为没有做边界判断,这个时候很有可能就会覆盖返回的主程序的地址,这个时候如果用户蓄意输入一些数值让这个存储着程序结束后改返回到哪个程序的空间里覆盖上自己希望的地址来运行自己想运行的程序,这样就改变了电脑里程序运行的走向,就达到了控制电脑的目的 2.定义数组可以一次性从内存中拿到多个独立的内存空间 3.当返回一个数组的时候,实际上返回一的是数组的首地址,这样效率更高 4.在c语言中,当访问不属于你的内存的时候,会报错 例如 数组a的长度为5,访问a[5]的时候,已经越界,a[5]代表的是这个数组的最后一个元素的地址的下一个相连位置的地址,如果这里有数据,报错,如果这里没有数据,则返回 (返回空,代表数字0) 5.对于线性搜索,可以先排序,再搜索,这样可以提高效率 6.算法(algorithm)是和硬件无关的,算法表示的是完成一个目标需要多少执行的步骤,一般只看最高此项的次数,不看系数和低次项(因为当样本N很大的时候这些都可以被忽略) 用Ο(关于N的步骤数表达式)表示最多需要多少步骤,用Ω(关于N的步骤数表达式)表示最少需要多少步骤,如果最多的次数和最少的次数一样,可以用θ(关于N的步骤数表达式)表示 7.递归 (recursion)可以很优雅的解决问题,当你从算法中抽象出可以循环的东西的时候就可以用递归实现他,递归可以大大的减少代码量 试试如何实现 合并排序(MERGE SORT)是又一类不同的排序方法 on input of n elements if n<2    return; else    sort left half of elements    sort right half of elements    merge sorted halves 递归思想:将一个问题分成两半,然后再分成两半,直到达到确定的条件让自己可以很轻松的处理,我们可以一边又一遍做相同的事情,但是每次规模都变小,这是一种可以忽略问题所包含的样本的解决问题的思想,映射到代码上就是递归的思想,每一次都可以调用自身我们只用改变参数就行了 看一看这篇文章 http://www.cnblogs.com/shudonghe/p/3302888.html 和自己写的代码对比一下,看区别,改进自己的代码 8. 指针就像一所房子的地址,我们可以到这个地址去做我们想做的事 &  * 在大括号里的时候是以下的意思, &是获取这个地址的意思     *是去这个地址的意思,获取这个地址里的信息的意思 在声明一个变量为指针的时候,如果这个指针里的值是int 那么我们就要用 int *a;(*和a也可以分开  int * a;不过一般都连在一起,这样比较有标示性) 注意 int *是连在一起的,指针的地址在a中,在后文的使用中*a表示a指向的地址中的信息 如果这个指针里的值是char 那么我们就要用 char *a; int a="132"; printf("%d :",&a);//输出的就是a的地址的意思 int *b=&a; printf("%d :",*b);//输出的就是地址a中的值的意思 指针的大小是根据计算机来确定的,32位或者64位(一般都是32位的),不会因为它指向的内容的大小而变化,所以别看int *a; char *a; 这样的声明语句,指针a的大小是一样的。 这一集值得反复观看 第10集,讲解指针和内存 http://open.163.com/movie/2010/3/Q/J/M6U6LS8CV_M6U6O02QJ.html null表示这个指针指向了错误的地方 null在底层代表地址为 0X00 16进制,表示0 ,但是地址0是操作系统专用的,正常情况下是不能操作这些地址的,程序也是不会返回这些地址的,所以如果返回0,就是出错了 在c语言中  string s ="123"; 实际上等于 char *a="132";//a 是字符串数组中第一个元素的地址 同时它也可以代表整个字符串 printf("%s",a); 当内存地址指向的是一个内存数组的时候, int *a=malloc(4*sizeof(int)); a[0]=45;//这样实际上就是往这个内存地址里面的内容赋值 实际上s就是字符串数组中第一个元素的地址(同时它也可以代表整个字符串),它可以直接赋值给 char *a  free(a);//释放变量的内存 a为指针 9 在C语言中用 typedef struct 来声明对象的数据结构 当你声明一个对象的时候,内存为每一个属性和方法分配连续的内存 unsign int a ;//a为一个只会为正数的整形变量 分配了内存,就得思考一个问题,回收内存,在栈中,系统会自动回收内存,但是在堆中,我们得记得回收内存,不然堆会越来越大,可用的内存会越来越小,当我们使用计算机的时候,任何软件都会在内存中留下痕迹,包括图片,文字,视频和其他形式的痕迹,这些内存被占用了之后如果没有及时回收,系统会越来越卡 数组这种数据结构 0.支持随机访问(当想访问第3个元素的时候不用从第一个元素开始访问,用[3]的形式可以直接访问) 1.长度是确定的(在内存中申请的空间是固定的,在声明数组之后不可改变,不可越界) 链表(单向链表:每一个节点都存储着这个节点的信息和下一个节点的指针) 0.长度任意,且内存空间也不一定连在一起(因为每一个节点的内存空间不一定是同时申请的),而且想要知道链表有多长也得遍历整个链表 1.不支持随机访问 但是支持随意插入(你可以在任何一个你想插入的地方插入数据), 冒泡排序,选择排序,合并排序 适用于数组 并不适用链表(不知道哪里是中间),可见算法是依赖数值结构的 10 sftp ftp 安全文件传输协议 11. 栈和队列都是长度有限的,所以可以用数组来实现 先进后出的数据结构 LIFO(last in first out )栈 stack         (可用于多层级的调用中) 代码实现 typedef struct{     int top;     int numbers[CAPACITY];     int size; } stack; 先进先出的数据结构 FIFO(first in first out )队列 queue 代码实现 typedef struct{     int head;     int numbers[CAPACITY];     int size; } queue; hash Table 哈希表 (第一级的长度肯定也是有限的,所以可以用数组来表示) 散列表(Hash table,也叫哈希表),是根据键(Key)而直接访问在内存存储位置的数据结构 哈希表如果只是一个数组的话(Linear probing  线性探测),因为数组总有填满的的一天,所以最后一定会冲突, 这个时候,如果我们将固定数组的每一个元素都作为一个链表的开端(Separate chaining 分离链接),当两个元素处在数组的 同一个位置的时候,我们可以把这两个元素存在这个位置下的链表里。 树形结构 typedef struct node {     int n;     struct node *left;     struct node *right; } node; 二叉树 对于每一个节点来说,只有两个叶子结点,而且左边的叶子结点都比右边的叶子结点小 当每个节点的叶子节点的个数不定的时候,树形结构将变得非常灵活和庞大 树形结构的搜索算法(一层一层地搜索)的时间复杂度为θ(N),N为搜索的元素的长度,注意这个复杂度和样本的数量无关,就算有一亿个单词,查找一个只有5个字母的单词也都只需要5步 树形结构的 插入,删除都比较繁琐,可能这就是数据结构的取舍 12. 位运算符,可直接操作一个字节8位中的0和1 << , >> , & ,| , ^  , ~ 与(&)、非(~)、或(|)、异或(^) 20. 取余运算可以让你得到一定范围内的数 例如    x 可以得到0到11范围内的数 13.写代码的时候只能写英文注释,在每一个编码文件的头部都要写注释,在重要方法重要变量的的头部也要写注释 (中文在不同的操作系统不同的编码下很有可能乱码) 14.各种类型数字所占字节 一个字节有8位 char 1 在 Java 中 char 占2个字节,因为 Java 是 Unicode 编码,任何字符都是两个字节。 int 4   -20亿到正20亿 float 4 double 8
转载请注明原文地址: https://www.6miu.com/read-2450125.html

最新回复(0)