[Powered by Google Translate] [评论] [测验] [亚力克西斯·罗斯,汤米MacWilliam,卢卡斯·弗雷塔斯,王阳乐] [哈佛大学] 这是CS50。[CS50.TV] 嘿,大家好。 欢迎测验0,这是本周三举行的审查会议。 我们要做的今晚,我和其他3个转录因子, 和我们一起去了什么,我们做的过程中,到目前为止,通过审查。 它不会是100%全面,但它应该给你一个更好的主意 你已经有什么,你仍然需要研究(星期三)前。 而且感觉我们一起去提高你的手的问题, 但请记住,我们还会有一点点的时间结束时, 如果我们通过几分钟业余做一般的问题, 所以记住这一点,所以我们要开始在0周开始。 [问答共0] [第0] [亚力克西斯·罗斯]但在此之前,让我们谈论 物流的测验。 [物流] [测验发生在10/10(星期三)代替演讲] (见的详细信息http://cdn.cs50.net/2012/fall/quizzes/0/about0.pdf)]这是10月10日(星期三)。 这是本周三,如果你去到这个URL, 这也是从CS50.net的访问的链接 去哪里的基础上,你可以看到信息 您的姓氏或所属学校以及 它讲述了什么测验将涵盖和类型的问题,你会得到。 请记住,你也有机会审查测验部分, 所以你的的TFS应超过一些实际问题, 这是另一个很好的机会看到你仍然需要学习测验。 让我们从一开始位'N'字节。 请记住一个位是0或1, 和一个字节是8的那些位的集合。 让我们来看看位在此集合在这里。 我们应该能够找出有多少位。 如果我们指望有8人,8个0或1个单位。 而且,由于有8位,这是1个字节, 让我们把它转换为十六进制。 十六进制的基数为16,这是很容易转换 一个二进制数,这是这是一个十六进制数。 我们要做的就是我们期待在4组, 我们将其转换为相应的十六进制数字。 我们从最右边的4组,所以0011。 这将是一个1和一个2,这样在一起,使得3。 然后让我们来看看其他4块。 1101年。这将是一个1,一个4,一个8。 在一起,将是13,这使得D。 我们会记住,在十六进制中,我们不只是从0到9。 我们去到F 0,所以在9,10对应到A, 11到B,等,其中F是15。 其中,图13是一个D, 所以将其转换为十进制,我们所做的一切是我们实际上 对待每一个位置为2的幂。 这是一个1,一个2,零4秒,零8秒,一个16,等等, 这是一个有点难以计算在你的脑袋,但如果我们去到下一张幻灯片 我们可以看到这个问题的答案。 从本质上讲,我们要跨越右到左, ,我们将每个数字所对应的功率为2。 请记住,十六进制表示,这些数字以0x开始 因此,我们不要混淆一个十进制数。 继续,这是一个ASCII表, 我们使用ASCII是从字符映射到的数值。 请记住,在密码学的pset,我们广泛使用的ASCII表 为了使用密码学的各种方法, 凯撒和维琼内尔的密码,不同的字母转换 在一个字符串中,根据由用户给定的关键。 让我们来看看在一点点的ASCII数学。 寻找在“P”以字符的形式,将是Q,+ 1, 记得,'5'≠5。 我们究竟如何转换之间的2种形式? 它实际上不是太硬。 为了让我们减去'0' 因为有5个地方之间的'0'和'5'。 为了走另外一条路,我们只是添加了0, 所以这有点像普通的算术。 请记住,有些事情时,周围的引号字符 ,从而对应于ASCII码表中的一个值。 移动到更一般的计算机科学课题。 我们了解到的算法是什么,以及我们如何使用编程 实现算法。 算法的一些例子是很简单的东西如 检查是否一个数是偶数还是奇数。 为此,还记得我们国防部的数字2,检查如果结果是0。 如果是这样,它甚至。如果没有,这是奇怪的。 这是一个非常基本的算法的一个例子。 一点点的一个更棘手的是二进制搜索, 后来我们就去了审查会议。 编程是长远来说,我们使用的算法 和将其转换为代码的计算机可读取。 2实例编程是零起步, 这是我们在0周。 即使我们没有实际键入的代码,它是一​​种实施 算法,这是印刷数字1到10, 在这里,我们做同样的在C编程语言。 这些功能相同,只是在不同的语言或语法编写的。 然后,我们了解的布尔表达式, 和一个布尔值,或真或假的, 这里常常布尔表达式 里面去的条件下,所以如果(x≤5), 好了,我们已经设置X = 5,所以这个条件会评估为true。 如果这是真的,无论代码是下面的条件 要由计算机进行评价,使字符串要打印 到标准输出,和工作条件 是指无论是在括号内的if语句。 记住所有的运营商。 记住它的&&和| |,当我们试图结合2个或更多的条件, ==不检查,是否事情都是平等的。 请记住,=是赋值,而==是一个布尔运算符。 ≤,≥,然后在最后2是不言自明的。 这里的布尔逻辑的一般审查。 和布尔表达式在循环中也是很重要的, 现在我们就去了。 我们学会了3种类型的循环迄今为止,CS50,同时,做,而。 重要的是要知道,对于大多数用途 我们实际上可以使用任何类型的循环 有一定的目的或共同的模式 在编程中特别呼吁这些循环 ,使它成为最有效或最优雅的代码以这种方式。 让我们在这些循环往往是最常见的。 在循环中,我们已经知道我们要重复多少次。 这就是我们提出的条件中。 对于,= 0,<10,例如。 我们已经知道,我们想要做的东西10倍。 现在,一个while循环中,我们一般不必然 我们要知道有多少次的循环运行。 但我们知道某种条件下,我们希望它是 始终是真实的,永远是虚假的。 例如,虽然被设定。 比方说,这是一个布尔变量。 虽然这是真的,我们要评估的代码, 有那么一点点更多的可扩展性,多一点点普遍比一个for循环, 但任何的for循环也可以转换为一个while循环。 最后,执行while循环,这可能是最棘手的理解, 经常使用,当我们要评估的代码 之前我们第一次检查的条件。 一个常见的​​用例的do while循环 当你想获取用户输入的,你知道你要问的用户 输入至少一次,但如果他们不给你良好的输入 你要不断地问他们,直到他们给你良好的输入。 这是最常见的使用whil​​e循环, 让我们来看看在这些循环的实际结构。 他们通常总是按照这些模式。 在fo​​r循环内,你有3个组成部分: 初始化,通常的东西,如int i = 0,其中i是计数器, 条件,在这里我们想说的运行循环,只要这种情况下仍持有, 像我<10,然后在最后,更新,这是我们增加 在循环中的每个点的计数器变量。 看到有一个共同的东西就是我+ +, 这意味着每次递增i。 你也可以做类似的东西我+ = 2, 这意味着加2我每次去通过循环。 然后这样做只是指实际运行循环的一部分的任何代码。 一个while循环,这个时候,我们其实有外循环的初始化, 例如,让我们说,我们正在试图做的,因为我刚才所描述的相同类型的循环。 我们会说INT I = 0,在循环开始前。 然后,我们可以这样说,而我做到这一点, 所以相同的代码块之前, 而这一次的更新部分的代码,例如,我+ +, 其实里面的循环。 最后,对于一个这样做的同时,它类似于while循环, 但我们必须记住,代码将评估一次 前检查条件,所以它使很多更有意义 如果你看它在从上到下的顺序。 在while循环中的代码进行评估之前,你甚至看while条件, 而一个while循环,它会首先检查。 报表和变量。 当我们要创建一个新的变量,我们首先要对其进行初始化。 例如,int酒吧初始化变量的酒吧, 但它并没有给它一个值,所以现在栏的值是什么? 我们不知道。 这可能是以前存储在内存中有一些垃圾的价值, 我们不想使用该变量 直到我们真正给它一个值, 因此,我们在这里声明。 然后,我们把它初始化为42。 现在,当然,我们知道这是可以做到一条线,酒吧= 42。 但仅仅是明确的多个步骤,是怎么回事, 声明和初始化分别在这里发生。 它发生在一个步骤,下一个,巴兹=酒吧+ 1, 这以下声明,递增的的巴兹,所以在这个代码块的结束 如果我们要打印的价值巴兹这将是44 因为我们声明并把它初始化为1>酒吧, 然后我们增加一次+ +。 我们去了这个漂亮的简要,但它有一个大致的 了解线程和事件是什么。 我们主要是在刮, 所以你可以把多个线程的代码序列 在同一时间运行。 实际上,它可能是运行在相同的时间, 但形式的抽象,我们可以认为它以这种方式。 从无到有,例如,我们有多个精灵。 它可以在同一时间执行不同的代码。 人们可以一边走一边说的东西 在一个不同的,在屏幕的一部分。 事件是另一种方式分离出来的逻辑 不同元素之间,你的代码, 在Scratch中,我们能够模拟活动,利用广播, 这实际上是当我收到,而不是当我听到, 但实质上,它是一个传递信息的方法 从一个精灵到另一个地方。 例如,您可能要传输游戏, 另一个精灵,当比赛结束, 它以某种方式回应。 这是一个重要的编程模型,以了解。 刚去超过基本的周0,我们已经讨论了这么远, 让我们来看看这个简单的C程序。 文字可能是从这里开始的一点点小,但我会去非常快的。 我们包括2头文件的顶部,cs50.h的和stdio.h中。 然后,我们定义一个常数,称为限制为100。 然后,我们执行我们的主要功能。 由于我们没有使用命令行参数,在这里,我们需要把作废 作为主要的参数。 我们看到上述主要的诠释。这是返回类型,因此返回0,在底部。 我们使用的是CS50库函数得到诠释 要求用户输入,我们将其存储在这个变量x, 因此,我们宣布上述X,和我们对它进行初始化,其中x =调用getInt。 然后我们检查,看是否用户给了我们很好的输入。 如果我们要≥LIMIT返回一个错误代码1和打印错误消息。 最后,如​​果用户已经给了我们很好的输入 我们要正视的数量和打印出来,结果。 只是为了确保这些全部命中回家 你可以看到这里的代码的不同部分的标签。 我提到不变,头文件。 哦,诠释x。请一定要记住,这是一个局部变量。 对比从一个全局变量,我们将讨论 一点点后,在审查会议, 和我们调用库函数printf, 因此,如果我们没有包含stdio.h头文件 我们将无法调用printf。 我相信,进行了切断箭头指向的%d, 这是在printf的格式化字符串。 它说一个数字,第%d打印出这个变量。 这是它第0周。 现在,卢卡斯继续下去。 嘿,伙计们。我的名字是卢卡斯。 我是一个大二学生在校园里,奥美最好的房子, 我要谈一点,约1周和2.1周。 [1周和2.1周] [卢卡斯·弗雷塔斯] Lexi的说,当我们开始从头开始你的代码翻译到C ,我们注意到的事情之一是,你不能只是 写你的代码,并运行它使用了一个绿色的标志。 其实,你必须使用一些步骤,以使你的C程序 成为一个可执行文件。 基本上你做了什么时,你所编写的程序是, 你翻译成一种语言,编译器能够理解你的想法, 所以,当你正在编写一个程序,在C 你在做什么,实际上是写东西,你的编译器是怎么回事了解, 那么编译器会翻译的代码 到的东西,你的电脑就会明白。 的事情,您的计算机实际上是非常愚蠢的。 你的电脑只能理解“0”和“1, 所以实际上在第一台计算机的人通常编程 用“0”和“1秒,但现在不是了,感谢上帝。 我们没有记忆0和1的序列 for循环或一个while循环等。 这就是为什么我们有一个编译器。 编译器只是它基本上是翻译的C代码, 在我们的例子中,您的计算机就会明白的语言, 这是目标代码,我们正在使用的编译器, 被称为铛,所以这实际上是铛的符号。 当你有你的程序,你必须做两件事情。 首先,你必须编译程序,然后你运行你的程序。 编译你的程序,你有很多的选择这样做。 第一个是做铛program.c 在该程序是你的程序的名称。 在这种情况下,你可以看到,他们只是说“嘿,编译我的程序。” 你不是说:“我想我的程序”或其他任何名称。 第二个选项是给你的程序的名称。 你可以说铛-O和你想要的名称 可执行文件被命名为,然后program.c。 而且你还可以做程序,以及如何在第2例 我把C,我只有在第三个节目吗? 是啊,你不应该付诸表决。C时使用。 否则,编译器实际上是要骂你的。 而且,我不知道,如果你们还记得, 但很多时候,我们也使用lcs50或-LM。 这就是所谓的链接。 它只是告诉编译器,你将使用这些库就在那里, 所以如果你想使用cs50.h,你必须输入 铛program.c,lcs50。 如果你不这样做,编译器不会知道 你使用了这些功能cs50.h. 当你要运行你的程序,你有2个选择。 如果你没有铛program.c你没有给你的程序的名称。 您必须运行使用。/ a.out的。 a.out是一个标准的名称,铛使您的程序,如果你不给它一个名字。 否则,你要做的/计划,如果你在你的程序出了名的, ,如果你还做程序的名称,程序会得到 已经进行编程的C文件相同的名称。 然后,我们谈到数据类型和数据。 数据类型基本上是同样的事情,他们使用的小盒子 存储值,所以数据类型实际上是一样的小宠物。 他们有各种规模和类型。 我不知道这个比喻是有道理的。 数据的大小实际上是依赖于机器的体系架构。 ,我在这里要告诉所有的数据大小 实际上是一个32位的机器,这是我们的设备的情况下, 但如果你是真正的编码您的Mac或Windows 可能你有一个64位的机器, 所以请记住,数据的大小,我要在这里展示 是为32位的机器上。 第一个,我们看到的是一个int, 这是非常简单的。 您可以使用int来存储一个整数。 我们也看到了字符,字符。 如果你想使用一个字母或一个小符号,你可能会使用一个char。 一个char 1个字节,这意味着8位,如乐喜说。 基本上我们有一个ASCII表有256个 可能的0和1的组合, 然后当你输入一个字符,它的翻译 的字符输入你一个数字,你有ASCII表中,如乐喜说。 我们也有浮动,我们用它来存储的十进制数。 例如,如果你想选择3.14,你要使用浮动 或双具有更高的精度。 一个浮动有4个字节。 双有8个字节,所以,唯一的区别是精度。 我们也有很长的,用于整数, 你可以看到一个int和一个长为一个32位的机器具有相同的大小, 所以它不会真正有意义的使用在一个32位的机器。 但是,如果你使用的是Mac和64位的机器,实际上是一个长有大小8, 所以它确实依赖于体系结构。 对于32位的机器,它没有意义的真正使用长。 再长长,另一方面,有8个字节, 所以这是非常好的,如果你想有一个较长的整数。 最后,我们有字符串,它实际上是一个char *, 这是一个指向字符。 这是很容易想到的字符串的长度将是 的字符数,你必须有, 但实际上本身的char * 有一个指针到一个字符,这是4个字节的大小。 一个char *的大小为4个字节。 这不要紧,如果你有一个小单词或一个字母或任何东西。 这将是4个字节。 我们也学到了一点关于铸造, 所以,你可以看到,如果你有,例如,一个程序,说: INT X = 3,然后输出(“%d”,X / 2) 你们知道它会在屏幕上打印吗? 有人吗?>> [学生] 2。 1 >> 1,是的。 当你做3/2 1.5, 但由于我们使用的是一个整数,它会忽略的小数部分, 你将有1。 如果你不希望发生这种情况你可以做什么,例如, 声明持股量Y = X。 则X为3现在要在y是3.000。 然后你就可以打印的Y / 2。 其实,我应该有一个2。那边。 它会做3.00/2.00, 你会得到1.5。 我们有这个.2 f只要求2的小数部分的十进制单位。 如果你有0.3 f,它实际上有1.500。 如果它是2的为1.50。 我们也有这样的情况在这里。 如果你这样做持股量X = 3.14,那么你的printf x 你将得到3.14。 如果你做x = x的整数, 这意味着把X作为一个int,则在打印x现在 你将不得不3.00。 这是否有意义吗? 因为你第一次治疗x为整数,所以你忽略的小数部分, 然后你印刷。 最后,你也可以做到这一点, X = 65,那么你声明一个char C = X, 然后,如果你打印的C,你实际上是在将要得到的 A,所以基本上你在这里做什么 的整数转换成字符, 就像ASCII表。 我们还谈到数学运算符。 他们中的大多数都非常简单,+, - ,*,/, 我们谈到的mod,这是一个部门,剩下的2个号码。 如果你有10%3,例如, 这意味着10除以3,其余的是什么? 这将是1,所以它实际上是非常有用的了很多的节目。 对于维琼内尔和凯撒,我敢肯定,如果大家都使用MOD。 关于数学运算符时,必须非常小心,将*和/。 例如,如果你这样做(3/2)* 2,你会得到吗? [学生] 2。 呀,2,因为二分之三是要为1.5, 但因为你是做2个整数之间的操作 其实你只是要考虑, 1 * 2,然后将是2,所以要非常非常小心 做数学运算时,整数,因为 你可能会得到2 = 3,在这种情况下。 也非常小心的优先级。 通常,你应该使用括号,要确保你知道你在做什么。 一些有用的快捷键,当然,一个是我+ +或i + = 1 或使用+ =。 这是同样的事情,做I = I + 1。 你也可以做我 - 我 - = 1, 这是同样的事情I = -1, 东西你们使用了很多在for循环中,至少。 此外,*,* =,如果你这样做了,例如,如果你使用, *的话说,I = I * 2 = 2是同样的事情, 同样的事情为师。 如果你做的I / = 2,I = I / 2,这是同样的事情。 现在功能。 你们的经验教训,其功能是一个很好的策略,以节省代码 而你编程,所以如果你要执行相同的任务 在代码中一遍又一遍,可能您要使用的功能 只是让你不必一遍又一遍的复制和粘贴代码。 其实,主要是一个函数,当我告诉你一个函数的格式 你会看到,这是很明显的。 我们还可以使用一些库的功能, 例如,printf,进球,这是从CS50库, 和其他功能,如TOUPPER。 所有这些功能实际上是实现其他图书馆, 当你把这些系绳文件的开始你的程序 你说可以请你给我的代码为这些功能 所以我没有实现他们自己吗? 你也可以编写自己的函数,因此,当你开始编程 你会意识到,图书馆不具有的所有功能,你需要的。 例如,我们在过去的pset,写,画,加密和查找, 这是非常,非常重要的是要能写功能 因为他们是有用的,我们使用他们所有的时间编程, 这样可以节省大量的代码。 函数的格式是这一个。 我们开始返回类型。返回类型是什么? 这只是当你的函数会返回。 如果你有一个函数,例如,阶乘, 是要计算的阶乘的整数, 可能它会返回一个整数。 然后返回类型为int。 的printf实际上有一个返回类型为void 因为你不返回任何东西。 你只是在屏幕上打印的东西 和退出的功能之后。 然后,你的功能,你可以选择的名称。 您应该有点合理,如不选择一个名称,如某某 或像X2F。 尝试弥补的名称是有道理的。 例如,如果它的因子,说阶乘的。 如果要画的东西,它是一个函数,将它命名画。 然后我们有参数,也被称为参数, 这是像你的函数所需要的资源, 从你的代码来执行其任务。 如果你要计算一个数的阶乘 可能你需要有一个数字来计算阶乘。 的论点,你将有一个是这个数字本身。 那么它会做一些事情,结束时,返回的值 除非它是一个void函数。 让我们看一个例子。 如果我想编写一个函数,总结一个整数数组中的所有数字, 首先,返回类型为int 因为我有一个整数数组。 然后,我要的功能名称,如sumArray, 然后是怎么回事本身采取的阵列,为int NUMS, 然后长度的数组,所以我知道我有多少个数字来概括。 然后,我要初始化的变量称为总和,例如,为0, 每次我看到一个数组中的元素,我把它添加到总和,所以我做了一个for循环。 就像乐喜说,你INT I = 0,I <长度和i + +。 数组中的每一个元素,我所做的次数总和+ = [I], 然后我又回到了一笔,所以这是很简单的,这样可以节省大量的代码 如果您使用此功能了很多次。 然后,我们看看条件。 我们有,否则,否则,如果。 让我们来看看者的区别是什么。 看一看在这些代码。它们之间的区别是什么? 第一个基本的代码要你告诉 一个数是否为+, - ,或0。 第一个说,如果> 0,则它的正面。 如果它是= 0,那么它是0,并且,如果是<0,那么它的负。 和其他人正在做的,如果,否则,如果,否则。 两者之间的区别是,这实际上是将 如果> 0,<0或= 0三次, 所以,如果你有2号,例如,它会到这里来,并说 如果(X> 0),它会说“是”,所以我打印阳性。 但是,即使我知道,这是> 0,它不会是0或<0 我仍然会做的是0,<0, 所以实际上,我要进去,如果我没有到 因为我已经知道,它不是要满足这些条件。 我可以使用,否则的话,else语句。 它基本上是说,如果X = 0我打印的积极。 如果不是的话,我也对此进行测试。 如果这是我要做到这一点。 基本上,如果我有X = 2,你会说 如果(X> 0),是的,打印本。 现在,我知道,这是> 0,它满足了第一,如果 我什至不运行此代码。 的代码运行速度更快,实际上,3倍的速度,如果你用这个。 我们还了解到,和(或)。 我不会去,因为亚力克西斯已经谈到他们。 这只是&&和| |运算符。 我唯一​​会说的时候要小心,有3个条件。 使用括号,因为当你有一个条件,这是非常令人困惑的 和其他的一个或另一个。 使用括号只是为了确保你的条件是有意义的 因为在这种情况下,例如,你可以想像, 它可以是第一个条件和一个或另一个 或2中所组合的条件和 或第三人,所以,只是小心些而已。 最后,我们谈到了开关。 交换机是非常有用的,当你有一个变量。 比方说,你有一个变量如:n 这些情况下,可以是0,1,或2,并且对于每个 你要去执行一项任务。 你可以说开关的变量,它表明, 像值1的值,然后是我要做到这一点, 然后我打破,这意味着在任何其他情况下,我不会看 因为我们已经满足这种情况下, 然后value2和等等,我也可以有一个默认的开关。 这意味着,如果它不能满足的情况下,我 我做别的事情,但是这是可选的。 这一切对我来说。现在,让我们汤米。 好吧,这将是第3周上下。 这些都是一些我们的主题将涵盖,密码,范围,数组,等等。 一个快字上的密码。我们不会敲定这个家。 我们这样做的pset 2,但测验,请确保您知道其中的差别 之间的凯撒密码和维琼内尔的密码, 如何这些密码的工作是什么样子的加密 和解密使用这些密码的文本。 请记住,简单的恺撒密码的每个字符的旋转相同的量, 确保您MOD由数个英文字母。 和维琼内尔密码,在另一方面,每个字符旋转 不同的金额,而不是说 每一个字符3维琼内尔旋转,旋转每个字符 由不同的金额,根据一些关键字 中的关键字,其中每个字母代表了一些不同的量 旋转清晰的文本。 让我们先说说变量的作用域。 有2种不同类型的变量。 我们有局部变量,而这些将要定义的 外的主要或以外的任何函数或块, 和这些将在你的程序中的任何地方访问。 如果你有一个函数,在该函数中是一个while循环 大的全局变量的访问无处不在。 ,另一方面,是一个局部变量的作用域的地方,它被定义。 如果你有一个函数,例如,我们有这个函数g, 和内部的g是一个变量,在这里称为y, 这意味着,这是一个局部变量。 即使这个变量被称为y 这个变量是所谓的Y这两个函数 不知道对方的局部变量。 另一方面,在这里,我们说X = 5, 这范围以外的任何函数。 它的主要范围以外的,所以这是一个全局变量。 这意味着,这些功能里面,当我说X - 或x + + 我访问相同的x,,这y和这家Y是不同的变量。 这是一个全局变量和局部变量之间的差异。 就设计而言,有时,它可能是一个更好的主意 保持局部变量时,你可能可以 因为有一堆全局变量可以得到真正的混乱。 如果你有一堆的功能,所有的修改同样的事情, 你可能会忘记此功能,如果不慎修改, 这功能不知道这一点, 它变得相当混乱,因为你得到更多的代码。 保持局部变量时,你可能可以 是刚刚好的设计。 阵列,请记住,仅仅是同一类型的元素的列表。 里面的CI不能有一个列表,如1,2.0,你好。 我们不能做到这一点。 当在C我们声明一个数组的所有元素必须是相同的类型。 在这里,我有3个整数的数组。 在这里,我有数组的长度,但如果我只是在此语法声明 我指定所有的元素都是我并没有从技术上需要这个3。 编译器足够聪明,找出数组应为多大。 现在,当我想获取或设置一个数组的值 这是的语法来做到这一点。 这实际上修改的第二个元素的数组,因为记得, 编号从0开始,而不是1。 如果我想读取该值,我可以这样说X =数组[1]。 或者,如果我想设置该值,如我在这里做什么, 我可以说,数组[1] = 4。 这通过其索引访问元素 或他们的位置,他们是在数组中, 从0开始,上市。 我们也可以有数组的数组 这就是所谓的多维数组。 当我们有一个多维数组 这意味着我们可以有类似的行和列, 而这仅仅是一个可视化的思考方式。 当我有一个多维数组,这意味着我要开始需要 超过1指数,因为如果我有一个网格 只是说你在哪个行并没有给我们一个数字。 这真的只是给我们一个数字列表。 比方说,我有这样的阵列。 我有一个数组称为网格,和我说这是2行3列, 所以这是一个可视化的方式。 当我说我想要得到的元素[1] [2] 这意味着,因为这些是第一,然后列行 我要跳转到第1行,因为我说了一句。 然后我到这里来,到第2列,我要得到的值是6。 有意义吗? 多维数组,请记住,在技术上是一个数组的数组。 我们可以有数组的数组的数组。 我们可以继续下去,但一个真正的方式来思考 这是怎么被解雇,这是怎么回事,是形象化 在这样的一个网格。 当我们通过阵列的功能,他们会表现 当我们通过定期的变量功能有点不同 像传递一个int或float。 当我们传递了一个int或char或任何其他数据类型 我们刚接过来一看,如果修改 该变量的值的变化不会传播 到调用函数。 有了一个数组,在另一方面,这将发生。 如果我通过在阵列中的一些功能,该功能改变的一些内容, 我回来的时候调用它的函数 我的数组是不同的,为的词汇 是数组是通过引用传递,我们将在后面看到。 这是关系到如何指针的工作,而这些基本数据类型, 另一方面,通过值传递。 我们可以认为那是一些变量的副本,然后通过在副本中。 不要紧,我们做什么与该变量。 调用函数将不知道它被改变了。 数组是在这方面,不同的只是一点点。 例如,正如我们刚才看到的,主要是简单的功能 可以在两个参数。 第一个参数的主要功能是ARGC,或参数的个数, 第二个参数被称为ARGV, 这些参数的实际值。 比方说,我有一个程序叫this.c, 和我说这一点,我要在命令行中运行此。 现在在某些参数传递到我的计划,称这 我可以这样说,/这是CS 50。 这是我们想象的大卫每天都在做终端。 但是,现在该程序的主函数内的 这些值,因此argc是4。 这可能是有点混乱,因为我们真的只有通过在CS 50。 这是只有3个。 但请记住,第一个元素的argv的第一个参数 是函数本身的名称。 因此,这意味着我们有4个东西在这里, 及第一要素将是/这。 这将被表示为一个字符串。 那么剩下的元素是我们的程序名后键入。 因此,正如顺便说一句,因为我们在pset中2中所看到的, 记住该字符串50≠50的整数。 因此,我们不能说些什么,“X = ARGV 3。 这只是不会是有道理的,因为这是一个字符串,这是一个整数。 所以,如果你要转换之间的2,请记住,我们要 有这种神奇的功能,称为ATOI。 这需要一个字符串并返回该字符串表示的整数内。 所以这是一个容易犯的错误的测验, 只是在想,这会自动将正确的类型。 但我们知道,这将永远是字符串 即使只包含字符串的整数或一个字符或一个浮子。 所以,现在让我们来谈谈运行时间。 当我们做这些疯狂的事情,所有这些算法, 它变得非常有用的,要问的问题,“他们需要多长时间?” 我们表示,所谓的渐近符号。 因此,这意味着 - 好,让我们说,我们给我们的算法 一些真的,真的,真的很大的投入。 我们要问的问题,“如何是将要采取的吗? 多少个步骤将需要运行我们的算法 作为输入的函数的大小?“ 因此,第一种方式我们可以形容运行时间是大澳 这是我们的最坏情况下的运行时间。 因此,如果我们要对数组进行排序,我们给我们的算法的一个数组 时,应在升序降序排列, 那将是最坏的情况。 这是我们的最大长度的时间,我们的算法将采取的上限。 在另一方面,本Ω是要描述的最佳情况下的运行时间。 因此,如果我们给一个已排序的数组排序算法, 排序需要多长时间? 而这一点,然后,描述了一个运行时间的上下限。 因此,这里有一些话,介绍一些常见的运行时间。 这些是按升序排列。 我们有最快的运行时间不变。 这意味着,无论有多少个元素,我们给我们的算法, 不管有多大,我们的阵列,整理 或做我们正在做的阵列将始终以相同的时间。 因此,我们可以表示,只需用1,这是一个常数。 我们也期待在数运行时间。 因此,类似二进制搜索是对数, 我们削减了问题的一半,每次 然后事情就从那里获得更高的。 如果你正在编写一个O任何阶乘的算法, 你可能不应该认为这是你的日常工作​​。 当我们比较的运行时间,重要的是要记住这些东西。 所以,如果我有一个算法是O(N),而其他人 有一个度为O(2n)的算法,这其实是渐近等价。 因此,如果我们想象Ň像一百十亿元是一个很大的数字: 所以,当我们比较一百十亿元的东西像一百十亿元+ 3, 突然,3并没有真正使一个很大的区别了。 这就是为什么我们要开始考虑这些事情是相等的。 因此,这些常量在这里的事情,比如,有这2个,或加入3, 这些仅仅是常数,而这些会下降。 所以这就是为什么这些运行时间是相同的,说他们是O(n)的所有3。 同样,如果我们有2个运行时间,比方说为O(n 3 + 2N²),我们可以添加 + N,+ 7,然后我们有另一种运行时,只是Ø(N³)。 再次,这些是相同的东西,因为这些 - 这些是不一样的。 这是同样的事情,对不起。因此,这些都是一样的,因为 这n³将主宰这个2N²。 什么是不一样的事情是,如果我们的运行时间,如O(N³)和O(N 2) 因为这n³是远远大于这n 2。 因此,如果我们的指数,突然开始啦, 但是,当我们只是处理因素,因为我们是在这里, 那么它不会的问题,因为他们只是要辍学。 让我们来看看到目前为止,我们已经看到的一些算法 并谈谈它们的运行时间。 第一种方法寻找一些在列表中,我们所看到的,是线性搜索。 而实施的是超级简单的线性搜索。 我们只是有一个列表,我们要看看在列表中的每一个元素 直到我们找到的数量,我们要寻找的。 因此,这意味着,在最坏的情况下,这为O(n)。 和这里,最坏的情况下可能是,如果该元素是 最后一个元素,然后使用线性搜索,我们来看看在每一个元素 直到我们得到的最后一个知道,它实际上是在列表中。 我们不能就这样放弃半说,“这是可能不存在。” 我们来看看整个事情的线性搜索。 最好情况下的运行时间,在另一方面,是恒定的 因为在最好的情况下,我们要寻找的元素是列表中的第一个。 所以,我们要花费1步,不管有多大的列表 如果我们要找的第一个元素,每一次。 因此,当您搜索时,请记住,它不要求我们的列表进行排序。 因为我们只是去看看,每一个元素,它其实并不重要 什么样的顺序,这些元素所在 一个更聪明的搜索算法是类似二进制搜索。 记住,是当你要执行的二进制搜索 在中间的列表中继续寻找。 因为我们正在寻找的中间,我们需要对列表进行排序 否则,我们不知道在哪里,中间是,我们来看看以上 整个列表中找到它,然后在这一点上,我们只是在浪费时间。 因此,如果我们有一个排序的列表中,我们发现中间,我们要比较的中间 的元素,我们要寻找的。 如果是太高,那么我们就可以忘记的右半边 因为我们知道,如果我们的元素已经过高 和一切该元素的权利甚至更高, 那么我们就需要看看那里了。 凡在另一方面,如果我们的元素是太低, 我们知道一切该元素的左侧还太低, 所以它不是真正意义,看看有,。 通过这种方式,每一步,每一次我们看的列表中点, 我们要削减一半,因为我们的问题,我们突然知道 一大堆的数字,不能是一个我们要找的。 在伪代码,这将是这个样子, 而且,因为我们切割的列表中每一次的一半, 我们的最坏情况下的运行时间从线性到对数的跳跃。 因此,我们突然有记录的步骤,以便找到列表中的元素。 最好的情况下的运行时间,不过,仍然是不变的 因为现在,让我们只想说,我们正在寻找的元素是 原始列表总是精确中间。 因此,我们可以成长为大,因为我们希望我们的名单,但如果我们要寻找的是在中间元素, 那么我们要花费1步。 所以这就是为什么我们是O(log n)的Ω(1)或恒定。 让我们实际运行在此列表中的二进制搜索。 因此,让我们说,我们正在寻找的元素164。 我们要做的第一件事是找到这个列表的中点。 它只是发生的中点是要倒在这两个数字之间, 所以我们就武断地说,每次2号的中点之间, 让我们圆了。 我们只需要确保我们这样做的每一个步骤的方式。 因此,我们要圆了,和我们说,161是我们的名单中。 因此,161 <164,和161的每一个元素的左侧 <164,所以我们不知道它会帮助我们在所有 开始寻找的元素,因为在这里我们不能有。 所以,我们可以做的是,我们只是忘了,整个左半边的列表, 现在只考虑从右边的161起。 所以,再一次,这是中点;让我们圆了。 现在175是太大了。 因此,我们知道它不会帮助我们期待在这里或在这里, 因此,我们就可以扔一边去,和我们最终会达到了164个。 二进制搜索的任何问题? 让我们从搜索通过一个已经排序的列表 实际以任何顺序的号码列表 该列表以升序排列。 我们看到在第一种算法被称为冒泡排序。 这将是比较简单的,我们看到的算法。 冒泡排序法说,当任意2个元素列表内的地方, 这意味着有一个更高的编号,以左侧的一个较小的数字, 然后,我们将交换他们,因为这意味着,该清单将是 “更多排序”比以前。 我们只是打算再继续这个过程中,一次又一次地 直到最后元素的的种泡到正确的位置,我们有一个排序的列表。 这是怎么回事运行时间为O(N²)。为什么呢? 好了,因为在最坏的情况下,我们采取的每一个元素,并 我们要结束了列表中的所有其他元素进行比较。 但是,在最好的情况下,我们有一个已排序的列表中,冒泡排序 只通过一次去,说:“不,我没有做任何掉期,所以我所做的一切。” 因此,我们有最好的情况Ω(n)的运行时间。 让我们来运行冒泡排序的列表。 首先,让我们看一些伪真的很快。 我们想说,我们要跟踪的,在每一次迭代的循环, 跟踪与否,我们改变的任何元素。 如此的原因是,我们要停下来的时候,我们没有交换任何元素。 因此,在我们的循环的开始,我们还没有交换任何东西,所以我们会说这是假的。 现在,我们要去列表,并比较i个元素的元素i + 1 如果是的情况下,有一个更大的编号,以左侧的一个较小的数字, 然后,我们只是来交换他们。 然后,我们要记住,我们交换元素。 这意味着,我们需要在列表中至少1个或更多的时间去 因为我们停止的条件,其中已排序的整个列表时, 这意味着我们还没有作出任何掉期。 所以这就是为什么我们的条件,在这里是“,而一些元素已被调换。 所以,现在就让我们看一看在此名单上的运行。 我的名单5,0,1,6,4。 要开始冒泡排序的方式在左侧,和它的比较 的第i个元素,所以0到i + 1,它是元件1。 它说,5> 0,但现在是向左, 所以我需要换5和0。 当我换,我突然得到这个不同的列表。 5> 1,所以我们要来交换他们。 5是6,所以我们不需要在这里做任何事情。 但6> 4,所以我们需要交换。 同样,我们需要贯穿整个名单,最终发现 这些是为了,我们交换, 在这一点上,我们需要更多的时间运行在列表中 以确保一切都在它的顺序,并在这一点上冒泡排序已经完成。 采取一些元素,不同的算法和排序,选择排序。 选择排序背后的想法是,我们要建立一个排序的列表部分 在一个时间的1个元素。 而要做到这一点的方式,我们是通过建立列表中的左部。 基本上,每 - 每一步,我们将要采取的最小元素,我们已经离开 尚未排序,我们将它移动到该排序段。 这意味着我们需要不断发现的最小的未分类的元素 然后,最小的元素交换的任何一种 最左边的元素进行排序。 运行时间为O(N 2),因为在最坏的情况下 我们需要比较每一个元素的所有其他元素。 因为我们说,如果我们在左半边的列表,我们需要 整个右段去寻找最小的元素。 然后,再次,我们需要去整个右段和 继续一遍又一遍,再次。 这为n²。我们将需要一个for循环内的另一个循环 这表明N²。 在最好的情况下思想,让我们说,我们给它一个已排序的列表; 我们其实并不比n²做的更好。 由于选择排序有没有办法知道 最小的元素只是我正好要寻找的。 它仍然需要确保,这实际上是最低的。 只有这样,才能确保它的最低限度,使用这种算法, 再看看每一个元素。 所以,真的,如果你给它 - 如果你给一个已排序的列表中选择排序, 它不会做任何比给它一个列表,该列表还没有排序。 顺便说一下,如果真的发生的情况下,有什么是O(的东西) 及的欧米茄的东西,我们只是说,它是θ的东西更简洁。 所以,如果你看到在任何地方,这是什么,仅仅表示。 如果事情是THETA的n²,它既是大O(N 2)和Ω(N ​​2)。 因此,最好的情况和最坏的情况下,它不会有所作为, 该算法是每次都做同样的事情。 因此,这是选择排序的伪代码可能看起来像。 基本上,我们会说,我想遍历列表 由左到右,并在每个迭代循环,我要移动 到这部分的列表中最小的元素。 有一次我移动的东西,我从来没有需要再看看该元素。 因为只要我换的左部的列表中的一个元素,它的排序 因为我们正在做的一切都在按升序排列使用最小值。 所以我们说,好了,我们在i位置上,我们需要的所有元素 i的右边的,以便找到最低。 因此,这意味着,我们想看看从i + 1到列​​表末尾。 而现在,如果我们目前正在观察的元素是低于我们的最低到目前为止, 请记得,我们开始只是最小关断 任何元素,我们目前在我假设这是最低的。 如果我找到比这更小的元素,然后我会说,好吧, 好了,我已经找到了新的最低。 我要记住,最低为。 所以,现在,我已经经历了一次该权利未分类的分类, 我可以说,我要交换的最小元素的位置i的元素是。 这是要建立我的清单,我的排序部分,由左到右的名单, 我们永远不需要看一个元素,再一次在该部分。 一旦我们交换了。 因此,让我们在此列表中选择排序的运行。 这里的蓝色元件将是在i,和红色的元件将是最小元素。 所以,我开始一路在左侧的列表,所以在5。 现在,我们需要找到最小的未分类的元素。 所以我们说0,所以0是我的新的最低。 但我不能停止,因为即使我们能够识别出0是最小的, 我们需要运行通过列表以确保所有其它元素。 因此,1是更大的,6是更大的,是更大的。 这意味着,在所有这些元素后,我已经决定0是最小的。 所以,我要交换5和0。 一旦我换,我会得到一个新的列表,我知道,我从来没有需要再次看,0 因为一旦我交换吧,我已经整理它,我们就大功告成了。 现在,碰巧的是​​,蓝色的元素又是5, 我们需要在1%,6和4,确定1 是最小最小的元素,所以我们交换1和5。 同样,我们需要看看 - 比较5〜6和4, 我们交换4和5,最后,比较 这2个数字,并交换它们,直到我们得到我们的排序列表。 选择排序的任何问题吗? 好吧。让我们继续上次的话题,那就是递归。 递归,请记住,这是真的荟萃东西的功能 反复调用自身。 因此,在某些时候,而我们的机能的反复调用本身, 需要有一些点,我们不再称自己。 因为如果我们不这样做,那么我们只是要继续这样下去,永远, 我们的计划是不会终止。 我们将这种情况的基本情况。 说,而不是再次调用一个函数的基本情况, 我只是返回一些值。 因此,一旦我们返回一个值,我们称自己已经停止了, 至今,我们已经取得了其余的电话也可以返回。 的碱情况下,与此相反的是递归的情况下。 这是当我们想要拨打另一个电话的功能,我们目前所处的 虽然并非始终如此,我们可能要使用不同的参数。 所以,如果我们有一个称为F的函数,且f称为1个参数, ,我们只是调用f(1),F(1),F(1),和它碰巧的是, 参数1分为递归的情况下,我们仍然永远不会停止。 即使我们有一个基础的情况下,我们需要确保,最终我们要达到这一基本情况。 我们不只是保持在这个递归的情况下。 通常情况下,当我们调用自己的时候,我们可能有不同的观点。 这是一个非常简单的递归函数。 因此,这将计算出一个数的阶乘。 在这里往上顶,我们有我们的基本情况。 的情况下,N≤1,我们不会再次打电话因子。 我们要停止,我们只是将返回一定的价值。 如果这是不正确的,那么我们要达到我们的递归的情况。 请注意,在这里,我们不只是调用的阶乘(N),因为那将是非常有用的。 我们会打电话给别的东西的阶乘。 所以你可以看到,最终,如果我们通过一个阶乘(5)的东西, 我们要调用阶乘(4)等,最终我们要达到这一基本情况。 因此,这看起来不错。让我们看看会发生什么,当我们真正运行这个。 这是堆栈,让我们说,主要是要调用这个函数的参数(4)。 因此,一旦的阶乘看到和= 4,阶乘将调用本身。 现在,突然之间,我们的阶乘(3)。 因此,这些功能都将继续保持增长,直到最终,我们打我们的基本情况。 在这一点上,这是,则返回值返回(nx的的返回值), 的返回值,这是NX的返回值。 最后,我们需要打一些数字。 我们在上方在这里,说返回1。 这意味着,一旦我们回到这个数字,我们可以弹出堆栈。 因此,这阶乘(1)就完成了。 1时返回时,这个阶乘(1)返回,返回1。 返回值,请记住,这是NX的返回值。 突然,这家伙知道,我想回到2。 所以请记住,返回值,这是NX的返回值。 所以,现在我们可以说,3×2,最后,在这里,我们可以说, 这将是4×3×2。 而一旦这种回报,我们得到一个整数内的主要。 递归有任何疑问? 好的。因此,有更多的时间回答大家的提问结束时, 但现在约瑟夫将支付余下的主题。 王阳乐]。所以,现在,我们已经谈到递归, 让我们来谈谈合并排序是一点点。 合并排序是基本的数字排序的列表的另一种方式。 它的工作原理是,归并排序,你有一个列表,我们要做的是 我们说,让我们将其划分为两半。 我们将首先运行再次合并排序的左半边, 然后我们会遇到合并排序的右半​​, 这给了我们两半进行排序,和现在我们要结合这些半在一起。 这是一个有点难以看到没有一个例子,所以我们走走过场,看看会发生什么。 所以,你开始这个列表中,我们把它分为两半。 我们经营的第一个合并排序的左半边。 所以这是左前卫,和现在我们再通过这个列表 被传递到合并排序,然后我们看, 在此列表的左侧,我们运行它的合并排序。 现在,我们得到了2号的列表, 现在的左半边长仅1元,我们不能 分裂一个列表,其中只有1成半的元素,所以我们只是说,一旦我们有50个, 这仅仅是1元,它已经排序。 一旦我们完成了,我们可以看到,我们可以 移动至右半此列表, 3排序,所以现在这个列表进行排序的两半 我们可以将这些数字重新走到一起。 因此,我们期待在50和3,三是小于50,所以它在第一,然后50用武之地。 现在,这样做了,我们回去这是正确的一半,列表和排序。 42是它自己的号码,所以它已经排序。 所以,现在我们比较这些2和图3是小于42,因此,被放在第一, 现年42岁的被提出,和50被放进去。 现在,排序,我们去所有的方式回到顶端,1337年和15。 好吧,我们现在看这个名单的左半边; 1337本身,所以它的排序,并同15。 所以,现在我们结合这2个数字进行排序,原单,15 <1337, 所以在第一位,然后是1337去英寸 现在我们整理的两半原始列表的顶部。 所有我们需要做的是结合这些。 我们期待在该列表中的第2号,3 <15,所以先进行排序的数组。 15 <42,所以它去了。现在,42 <1337,去英寸 50 <1337,所以去英寸注意到了我们仅仅花了2个号码,这个列表。 所以,我们不能简单地交替之间的列表。 我们只是在寻找一开始,我们正在采取元素 这是更小的,然后把它到我们的数组。 现在,我们已经合并了所有的一半就完成了。 有任何疑问,归并排序?是吗? [学生]:如果它分裂成不同的群体,为什么他们不只是分裂一次 你有3个和2组中的吗? [其他问题不知所云] 原因 - 所以,问题是,为什么我们不能只是把它们合并的第一步后,我们有吗? 我们之所以能做到这一点,双方在最左边的元素, 然后采取较小的一个,并把它放在,是因为我们知道,这些 个人的名单中排序的订单。 所以,如果我在最左边的元素的两个部分, 我知道他们将这些列表中的最小元素。 所以,我可以把它们放到这个大名单的最小元素点。 另一方面,如果在第二个层次那边,我看这2列出了 50,3,42,1337和15,这些都是没有排序。 所以,如果我在50和1337,我要放50到我的清单。 但是,这并不真正有意义,因为是最小的元素的所有这些。 所以我们可以做到这一点结合步骤的唯一原因是因为我们的名单已经排序。 这就是为什么我们得到了所有的方式向底部 因为当我们只是一个单一的号码,你知道,一个单一的数字 在其本身已经是一个排序的列表。 有什么问题吗?不是吗? 复杂性?好了,你可以看到,在每一步的最终数字, 我们可以将一个列表中的一半log N次, 这是我们得到这个N X日志n的复杂性。 你会看到最好的情况下,归并排序是n日志n,它只是恰巧 最坏的情况下,或Ω那边的,也是n日志n。 要记住的东西。 移动,让我们去到一些超级基本的文件I / O。 如果你看,你会发现我们在争夺某种系统 在那里你可以写入到日志文件中,如果你读通过的代码。 让我们来看看你怎么可能做到这一点。 好了,我们有fprintf,你能想到的只是输出, 只是打印到一个文件中,而不是,因此在开始时的f。 这种代码在这里,它是什么,你可能已经看到在争夺, 它通过你的二维数组打印出来行由行的数字是什么。 在这种情况下,输出打印到您的终端或我们所说的标准输出部分。 而现在,在这种情况下,我们要做的是与fprintf的printf取代, 告诉您要打印的文件,在这种情况下,它只是它打印输出到该文件 而不是把它打印出来到你的终端。 好了,那么这引出了一个问题:我们从哪里得到这类型的文件,对不对? 我们通过在这fprintf机能的,但我们不知道它是从哪里来的。 好了,早在代码中,我们有什么是在这里,这个代码块 基本上说,打开该文件要求log.txt中。 我们做什么后,我们必须确保该文件实际上是打开成功。 因此,它可能会失败的原因有多种,你没有足够的空间在您的计算机上,例如。 因此,它始终是重要的,之前你做任何操作的文件 我们检查该文件是否被成功打开。 那么,什么是一个,这是一个争论的FOPEN,好了,我们可以打开一个文件,在许多方面。 我们可以做的是,我们可以通过它瓦特,这意味着覆盖的文件,如果它退出了, 我们可以通过一个一个,这是他们附加的文件,而不是覆盖它, 或者我们可以指定R,这意味着,让我们打开的文件为只读。 因此,如果程序试图进行任何更改的文件, 骂他们,不要让他们这样做。 最后,一​​旦我们完成的文件,完成执行操作就可以了, 我们需要确保我们关闭文件。 因此,在你的程序,你要通过他们再次 您打开这个文件,只是将其关闭。 因此,这是重要的事情,你必须确保你做。 所以请记住,你可以打开一个文件,然后就可以写入到文件中, 操作中的文件,但你必须在年底关闭该文件。 任何问题,基本的文件I / O?是吗? [学生提问,不知所云] 就在这里。现在的问题是,这log.txt文件出现在哪里? 好吧,如果你只要给它log.txt中,它会创建在同一目录下的可执行文件。 所以,如果让隐私无处藏 - >> [学生提问,不知所云] 是。在同一个文件夹中,或在同一个目录下,如你所说的话。 现在,内存,堆栈和堆。 因此,如何在计算机的内存吗? 那么,你可以想像内存块的排序。 而在内存中,我们有什么所谓的堆卡在那里,栈,在那里。 和堆向下增长的堆栈是向上增长的。 因此,作为托米提到的 - 哦,好了,我们这些其他4段,我会在第二 - 汤米刚才说,你知道他的功能是如何称呼自己和称呼对方? 他们建立这种堆栈帧。 那么,如果主调用f​​oo,foo的被放在堆栈中。 foo调用了酒吧,酒吧将在堆栈上,并因此获得在堆栈中。 当他们返回,他们各拿采取的堆栈。 什么这些地方,保持记忆的? 那么,顶部,这是文本段包含程序本身。 的存在,所以本机代码,一旦你编译的程序。 其次,任何初始化的全局变量。 所以,你必须在你的程序中的全局变量,和你说的一样,A = 5, 得到段,下, 您有任何未初始化的全局数据,它只是诠释一个, 但你不说,它等于任何东西。 实现这些都是全局变量,所以他们在外面的主。 因此,这意味着任何的已宣告但不会被初始化的全局变量。 那么,是什么在堆吗?分配的内存使用malloc,我们将在一点点。 最后,与堆栈您有任何局部变量 任何功能,你可以调用他们的任何参数。 的最后一件事,你不真正了解的环境变量做什么, 但只要你运行的程序,有一些相关的,如 这是用户名的人谁跑的程序。 这就是将要排序的底部。 的内存地址,这是十六进制值, 值的顶部从0开始,和他们走一路下滑到了谷底。 在这种情况下,如果你在32位系统上, 在底部的地址将是0x开头,然后自动对焦,因为这是32位, 它是8个字节,并且在这种情况下,8个字节对应于8个十六进制数字。 所以,在这里你将有一样,0xFFFFFF具有,和在那里,你将有0。 那么,什么是指针?你们有些人可能不涉及这部分之前。 但我们没有去,所以在课堂上的指针只是一个数据类型 店,而不是某种类型的值,如50,它存储在内存中的某个位置的地址。 这样的记忆[不知所云]。 因此,在这种情况下,我们所拥有的,我们有一个指针,指向一个整数或一个int *, 它包含这个十六进制的0xDEADBEEF地址。 那么,我们有什么,现在,该指针指向在内存中的某个位置, 这只是一个,值50是在此内存位置。 在32位系统中,所有32位系统上,指针占用32位或4个字节。 但是,例如,在64位系统中,指针是64位。 所以,你要记住的东西。 因此,在结束位系统,指针是结束位长。 指针是一种难以消化的,没有多余的东西, 所以,让我们通过一个例子来动态内存分配。 动态内存分配为你做,或者我们调用malloc, 它可以让你分配某种形式的设置之外的数据。 因此,此数据是一种程序的持续时间更永久的。 因为你知道,如果你里面的一个函数,该函数返回x声明, 你不再需要访问的数据存储在x。 让我们做什么指针,是他们让我们存储记忆体或存储值 在不同的内存段,即堆。 现在,一旦我们返回的功能,只要我们有一个指针 内存中该位置,那么我们能做些什么,我们就可以看的值。 让我们看一个例子:这是我们的内存布局。 我们有这个功能,主要。 它所做的是 - 好了,就这么简单,对不对? - 诠释第X = 5,这只是一个变量在堆栈中的主。 另一方面,现在我们宣派指针调用函数giveMeThreeInts。 所以现在我们进入这个功能,我们创建了一个新的堆栈帧。 然而,在这个堆栈帧中,我们声明int *温度, 这对我们的mallocs 3个整数。 因此,大小的int会给我们带来多少字节,int是, 和malloc为我们提供了多少字节的空间在堆中。 因此,在这种情况下,我们已经建立了足够的空间,3个整数, 和堆在那里,这就是为什么我画更高。 一旦我们完成了,我们回来在这里,你只需要3个int返回, ,它返回的地址,在这种情况下,内存是的。 我们设置指针开关,在那里,我们只是一个指针。 但该函数返回被堆积在这里消失。 因此,临时消失,但我们仍然保持的地址在哪里 这3个整数内的电源。 因此,在这组中,指针的范围,为本地的堆叠框架, 但他们指的是在堆中的内存。 这是否有意义吗? [学生]:你能重复一次吗? >> [约瑟夫]是的。 所以,如果我回去只是一点点,你会看到该临时分配 一些内存的堆在那里。 所以,此功能时,giveMeThreeInts收益,该协议栈在这里会消失。 而与它的任何变量,在这种情况下,该指针被分配在层叠帧。 这是怎么回事消失,但由于我们返回临时 我们设定指针= temp中,指针现在指向相同的内存位置温度。 所以,现在,即使我们失去了温度,即本地指针, 我们仍保留什么该变量的指针指向内部的内存地址。 有问题吗?这可以是种一个令人困惑的话题,如果你还没有看过它在部分。 ,你的TF我们可以肯定会去,当然,我们可以回答问题 在年底的审查会议。 但,这是一个复杂的话题,我有更多的例子,是要显示 这将有助于澄清指针实际上是什么。 在这种情况下,指针是相当于阵列, 同样的事情作为一个int数组,这样我就可以使用这个指针。 所以我索引为0,和不断变化的第一个整数为1, 改变所述第二至2的整数,和第三至3的整数。 因此,更多的指针。好了,记得Binky。 在这种情况下,我们分配了一个指针,或我们宣布一个指针, 但最初,当我刚刚宣布的指针,它不指向在内存中的任何地方。 这只是它里面的垃圾值。 所以,我不知道该指针所指向的。 它有一个地址,只是充满了0和1的地方,它最初宣布。 我不能做任何事情,直到我调用malloc 然后它给了我一个很小的空间就堆在那里我可以把内部的值。 再说,我不知道,此内存里面有什么。 所以我必须做的第一件事是检查系统是否有足够的内存 给我1个整数摆在首位,这就是为什么我在做此检查。 如果指针是空的,这意味着,它没有足够的空间,或某些其他错误发生, 所以我要退出,我的计划。  但是,如果它没有成功,现在我可以使用这个指针 *指针的地址在哪里 该值的地方是,它设置它等于1。 因此,在这里,我们检查,如果该内存存在。 一旦你知道它的存在,你可以把它 你要投入什么样的价值,在这种情况下,1。 一旦我们完成它,你需要释放该指针 因为我们需要的系统内存,你提出的要求摆在首位。 因为电脑不知道什么时候,我们已经完成了它。 在这种情况下,我们明确地告诉它,好吧,我们就大功告成了与记忆。 如果其他应用程序需要它,其他一些程序需要它,感觉自由地前进,并把它。 我们还可以做的是,我们就可以得到局部变量上设置的地址。 所以int x是里面堆放的主要框架。 而当我们使用这个符号,这和运营商,它是什么 它需要的x,和x是只是一些数据在存储器中,但它有一个地址。 它位于什么地方。因此,通过调用&X,这是什么做的是它给了我们x的地址。 通过这样做,我们正在做的指针指向其中x是在内存中。 现在,我们只是做一些事情,如* X,我们会得到5回。 这颗恒星被称为提领。 按照地址,你会得到它的价值,存放在那里。 有什么问题吗?是吗? [学生]:如果你不这样做的三尖的东西,它仍可以编译吗? 是。如果你不这样做的3指针的东西,它仍然要进行编译, 但我会告诉你发生了什么在第二,并没有这样做, 这就是我们所说的内存泄漏。你不给系统 支持它的记忆,所以一段时间后程序会积累 内存,它的使用,并没有其他人可以使用它。 如果你见过有150万字节在您的计算机上的Firefox, 在任务管理器,这是怎么回事。 你有内存泄漏的程序,他们没有处理。 那么,如何指针的算术运算的工作吗? 好了,指针运算是有点像索引到一个数组中。 在这种情况下,我有一个指针,我做的是我的第一个元素的指针指向 此数组中的3个整数,我已经分配好了。 所以现在我做什么,明星的指针只是改变列表中的第一个元素。 星级指针+1点在这里。 因此,指针是在这里,指针+1是在这里,指针+2是在这里。 因此,只要加1是沿着这个数组同样的事情。 我们做的是,当我们这样做的指针+1,你的地址在这里, 中获得的价值在这里,你把一个明星从整个表达式 取消对它的引用。 所以,在这种情况下,我这个数组中的第一位置设置为1,则 第二位置为2〜3中的第三位置。 我在做什么在这里是我打印的指针+1, 这只是给了我2。 现在,我递增指针,所以指针等于指针+1, 向前移动。 所以现在如果我打印出来的指针+1 +1指针现在是3, 在这种情况下,打印出3。 而以免费的东西,我给它的指针 必须指出的数组,我回来了从malloc的开始。 因此,在这种情况下,如果我在这里呼吁3,这不会是正确的, 因为它是在中间的数组。 我必须减去到原来的位置 最初的第一点,我才可以释放它。 因此,这里是一个更复杂的例子。 在这种情况下,我们7个字符,一个字符数组分配。 在这种情况下,我们正在做的是,我们在第6循环, 我们将它们设置为Z。 因此,对于int i = 0,I> 6,我+ +, 所以,指针+我只是给我们,在这种情况下, 指针,指针1,指针2,指针3,依此类推等等回路中。 它要做的是得到该地址,解引用获得的价值, 和变化值到Z。 那么,在年底记住这是一个字符串,对不对? 所有的字符串结束的空终止字符。 所以,我要做的就是在指针6,我把空终止符中。 现在我基本上是做什么在这里实现输出一个字符串,对不对? 所以,当输出现在,当它达到一个字符串的结束吗? 当它击中的空终止字符。 因此,在这种情况下,我原来的指针指向该数组的开始。 我的第一个字符打印出来。我将它移到1。 我打印的字符。我移动过来。 我一直这样做,直到我到达终点。 而现在的结束*指针解引用,并得到空终止字符。 所以我的while循环运行,只有当该值不是空终止字符。 所以,现在我退出这个循环。 所以,如果我从这个指针减去6, 我回去的方式开始。 请记住,我这样做是因为我去的开始,以释放它。 所以,我知道这是一个很多。有什么问题吗? 请,是吗? [学生提问不知所云] 你能说大声吗?抱歉。 [学生]:最后一张幻灯片之前释放的指针, 你改变指针的值吗? [约瑟夫]所以,就在这里。 >> [学生]:哦,好吧。 [约瑟夫]所以,我有一个指针减减,右, 移动的东西回来,然后我释放它, 因为这个指针指向的数组的开始。 [学生]:但是,这不会需要你停止了之后的所有行。 [约瑟夫]所以,如果我停止后,这将被视为内存泄漏, 因为我没有运行免费的。 [学生]:我后的第一个三线指针+1 [不知所云] [不知所云]。 [约瑟夫]嗯。那么,什么是那里的问题? 抱歉。不,不。走,走,请。 [学生]:所以,你就不会改变指针的值。 你会不会有,做指针减减。 [约瑟夫]是的,没错。 所以,当我做指针+1和+2指针, 我不这样做指针等于指针+1。 所以,只是停留指针指向的数组的开始。 这是只有当我做加再加,它的值设置里面的指针, 它实际上移动一起。 好的。 还有问题吗? 同样的,如果这是铺天盖地的,这将覆盖在会议上。 问问你的教学研究员,它结束时,我们可以回答的问题。 通常我们不喜欢做这减去的事情。 这要求我跟踪我多少数组中的偏移。 所以,总的来说,这是只是为了解释如何指针运算。 但是,我们通常喜欢做的是什么,我们要创建的副本的指针, 然后,我们将使用该副本,当我们走动的字符串。 因此,在这些情况下,您使用的整个字符串复制到打印, 但我们没有这样做指针减6跟踪我们搬到了多少, 因为我们知道,我们仍然指向原来的点开始的列表 我们改变的是这个副本。 因此,在一般情况下,改变你原来的指针的副本。 不要试图有点像 - 不要改变原来的副本。 试图改变你原来的只读副本。 所以,你会注意到当我们通过串入的printf 你不必在它前面放一个明星,像我们一样与所有其他指针引用,对不对? 所以,如果你打印出整个字符串的%s需要一个地址, 在这种情况下一个指针,或在这种情况下,像一个字符数组。 字符,字符*和数组同样的事情。 指针是字符,字符数组同样的事情。 所以,我们要做的是通过指针。 我们没有通过,如*指针或类似的东西。 因此,数组和指针是同样的事情。 当你正在做的事情,比如x [Y]在这里的数组, 它在做什么引擎盖下的是它说,没关系,这是一个字符数组, 所以这是一个指针。 因此,x是同样的事情, 所以它做什么是Y到X, 这是同样的事情向前发展在内存中。 而现在X + Y为我们提供了某种形式的地址, 我们解引用地址或箭头 内存中该位置在哪里,我们得到的价值,在内存中的位置。 所以,所以这两个是完全一样的东西。 这只是一个语法糖。 他们做同样的事情。他们只是对彼此的不同句法。 那么,什么可能出错的指针吗?一样,有很多。好吧。所以,不好的事情。 你可以做一些不好的事情不检查,如果您的malloc调用返回null,正确的吗? 在这种情况下,我要求给我的系统 - 这个数字是什么? 2亿次4一样,因为一个整数的大小为4个字节。 我要求它像8十亿字节。 当然,我的电脑是不是能够给我这么大的内存回。 而我们没有检查,如果这是空的,所以,当我们试图取消对它的引用在那里 - 按照箭头的地方的去 - 我们没有这方面的记忆。 这就是我们所说的释放空指针。 这基本上使你出现段错误。 这是你可以segfault错误的方式之一。 其他不好的事情可以做 - 哦,好的。 这是一个空指针解引用。好吧。 其他不好的事情 - 嗯,要解决,你只要把在那里的检查 检查指针是否为空 和退出的程序,如果它发生了,malloc返回一个空指针。 这是XKCD漫画。人们理解它。排序的。 因此,记忆体。我去了这一点。 我们在一个循环中调用malloc,但我们每次调用malloc 我们正在失去这个指针指向的轨道, 因为我们弄错了。 因此,初始调用malloc给我的记忆在这里。 我的指针的指针。 现在,我不释放它,所以现在我调用malloc。 现在,在这里。现在,我的记忆中指出在这里。 指着在这里。指着在这里。 但我已经失去了所有的记忆,在这里,我分配的地址跟踪。 所以现在我没有任何参考了。 所以,我不能让他们自由这个循环之外。 因此,为了解决这样的事情, 如果你忘了空闲内存,你会得到此内存泄漏, 你必须释放内部的存储器中,这个循环一旦你用它做。 那么,这是什么情况。我知道很多你不喜欢这一点。 但现在 - 耶!你得到这样的44,000千字节。 所以,你释放它的循环结束时, 而这只是每次释放内存。 从本质上讲,你的程序没有内存泄漏了。 现在,别的东西你可以做的是释放一些内存,你问过两次。 在这种情况下,你的malloc东西,你改变它的值。 您可以免费一次,因为你说你用它做。 但后来我们再释放它。 这是非常糟糕的东西。 它不会到最初出现段错误, 但经过一段时间这样做是双重释放这会破坏你的堆结构, 你将学习多一点点,如果你选择一个类CS61。 但本质上一段时间后你的电脑会感到困惑 什么样的内存位置和它的存储 - 在数据被存储在内存中。 因此,两次释放一个指针是一个坏的事情,你不想做。 其他的东西,可以去错了不使用sizeof。 因此,在这种情况下,你的malloc 8个字节, 这是为两个整数同样的事情,对不对? 所以,这是绝对安全的,但它呢? 那么,作为卢卡斯谈到不同的体系, 整数是不同长度。 因此,在您正在使用的电器,整数是4个字节, 但其他一些系统上,他们可能是8个字节,也可能是16个字节。 所以,如果我只是在这里使用这个号码, 这个程序可能会在设备上工作, 但它不会在其他一些系统分配足够的内存。 在这种情况下,这是sizeof运算符用于。 当我们调用的sizeof(int),这样做是什么  它为我们提供了一个程序正在运行的系统上的整数的大小。 所以,在这种情况下,表示sizeof(int)将返回4类似器具上, 现在这个意愿,4 * 2,8, 这仅仅是为两个整数的必要的空间的量。 在不同的系统中,如果一个int是16个字节或8字节一样, 它只是将返回足够的字节来存储量。 最后,结构。 所以,如果你想存储在内存中的数独板,怎么可能我们做到这一点呢? 你可能会想到的第一件事像一个变量, 一个变量的第二件事情,第三件事情是一个变量, 第四件事 - 坏,右一个变量? 所以,你可以在此之上的一种改进是一个9×9阵列。 这很好,但是如果你想其他的东西相关联的数独板 喜欢的难度董事会, 或者,例如,你的分数是什么,或你解决这个板多少时间呢? 好了,你可以做的是,你可以创建一个结构。 我基本上可以说是在这里,我定义这个结构, 我定义的板9×9的数独板组成。 它所拥有的,它具有的水平的名称的指针。 它还具有X和Y,这是我现在的坐标。 还花费的时间[不知所云],它有到目前为止,我已经输入的总数的移动。 因此,在这种情况下,我可以分组一大堆的数据整合到一个结构 而不是它像飞舞着像不同的变量 ,我真的不能跟踪的。 这让我们有很好的语法形式的引用不同的结构,这里面的东西。 我可以做board.board,我得到的数独板。 Board.level,我是多么艰难。 ,Board.x和board.y给我,我可能会在董事会的坐标。 因此,我访问就是我们所说的结构体中的字段。 这定义的sudokuBoard,这是一个类型,我有。 现在,我们在这里。我有一个变量称为“板”的类型sudokuBoard。 因此,现在就可以访问这个结构在​​这里的所有领域。 任何关于结构的问题吗?是吗? [学生]:对于整数X,Y,你宣布一个行吗? >> [约瑟夫]嗯。 [学生]:所以,你可以做,所有的人? 喜欢在x,y的逗号倍,总? [约瑟夫]是的,你可以做到这一点,但我之所以把x和y在同一行 - ,问题是我们为什么能做到这一点在同一行吗? 我们为什么不把所有这些在同一行上 x和y是彼此相关的, 而这仅仅是风格上更正确的,在一定意义上, 因为它的分组在同一行上的两件事情 ,像那种同样的事情。 而我只是分裂分开。这只是一种风格的东西。 它在功能上并没有任何差别。 对结构的任何其他问题? 您可以定义一个图鉴与结构。 宠物小精灵有一个数字,它有一个字母,一个老板,一个类型。 然后,如果你有一个数组的神奇宝贝,你可以弥补图鉴,对不对? 好了,爽。因此,结构的问题。这些都是相关的结构。 最后,GDB。 GDB让你做什么呢?它可以让你调试你的程序。 如果你还没有使用GDB,我建议看短 只是在GDB是什么,你如何使用它,你会如何使用它, 和测试程序。 因此,让你做什么GDB是它让暂停[不知所云]你的程序 和一个实际线。 例如,我想在我的计划,如3号线暂停执行, 而我在第3行,我可以打印出所有的值有。 因此,我们所说的像行暂停 我们称这种放一个断点,在该行 然后我们就可以打印出当时的变量在程序状态的。 然后,我们可以从那里通过的程序行由行一步。 然后我们就可以看的堆栈状态的时间。 因此,为了使用GDB,我们所做的就是我们称之为铛上的C文件, 但我们有通过-ggdb标志的。 一旦我们做,我们只是运行gdb生成的输出文件。 所以你得到一些像这样质量的文本, 但真的所有您需要做的是输入命令的开头。 在主要突破主要把一个断点。 列表400列出了400行左右的代码行。 因此,在这种情况下,你可以环顾四周,说,哦, 我想设置一个断点,在397行,这条线, 那么你的程序运行到这一步,这将打破。 这将暂停在那里,您可以打印出来,例如,价值高或低。 因此,有一堆你需要知道的命令, 该幻灯片将在网站上, 所以,如果你只是想引用这些还是喜欢把它们放在你的备忘单,感觉很自由。 酷。这是测试复习0,如果您有任何问题,我们将坚持围绕。 好的。  [掌声] [CS50.TV]