1 00:00:00,000 --> 00:00:02,520 [Powered by Google Translate] [第4节 - 更舒适] 2 00:00:02,520 --> 00:00:04,850 [罗布·鲍登 - 哈佛大学] 3 00:00:04,850 --> 00:00:07,370 [这是CS50。 - CS50.TV] 4 00:00:08,920 --> 00:00:13,350 我们明天有一个测验,如果你们不知道。 5 00:00:14,810 --> 00:00:20,970 它基本上是你能看到在课堂上或在课堂上所看到的一切。 6 00:00:20,970 --> 00:00:26,360 这包括指针,即使他们是一个非常新的主题。 7 00:00:26,360 --> 00:00:29,860 你至少应该深入了解他们的高水平。 8 00:00:29,860 --> 00:00:34,760 凡是过去在课堂上,你应该了解的测验。 9 00:00:34,760 --> 00:00:37,320 所以,如果你有问题了,你可以问他们。 10 00:00:37,320 --> 00:00:43,280 但是,这将是一个非常学生主导的会议,你们问的问题, 11 00:00:43,280 --> 00:00:45,060 所以我希望人们有很多疑问。 12 00:00:45,060 --> 00:00:48,020 有没有人有问题吗? 13 00:00:49,770 --> 00:00:52,090 是。 >> [学生]你能对指针呢? 14 00:00:52,090 --> 00:00:54,350 我就去了指针。 15 00:00:54,350 --> 00:00:59,180 所有的变量一定住在内存中, 16 00:00:59,180 --> 00:01:04,450 但通常你不担心你刚才说X + 2和y + 3 17 00:01:04,450 --> 00:01:07,080 编译器将找出的东西都在那里生活。 18 00:01:07,080 --> 00:01:12,990 当你正在处理的指针,现在你显式地使用这些内存地址。 19 00:01:12,990 --> 00:01:19,800 因此,一个单一的变量将永远只住在任何给定的时间在一个单一的地址。 20 00:01:19,800 --> 00:01:24,040 如果我们想声明一个指针,类型是什么的样子? 21 00:01:24,040 --> 00:01:26,210 >> 我要声明一个指针p。的类型是什么样子呢? 22 00:01:26,210 --> 00:01:33,530 [学生] * P。 >>呀。所以int * p。 23 00:01:33,530 --> 00:01:38,030 我怎样才能使它指向为x? >> [学生]连字号。 24 00:01:40,540 --> 00:01:45,300 鲍登]符号是真正的被称为运营商的地址。 25 00:01:45,300 --> 00:01:50,460 所以当我说&X变量x的内存地址。 26 00:01:50,460 --> 00:01:56,790 所以现在我有指针p,在我的代码中的任何地方,我可以使用* P 27 00:01:56,790 --> 00:02:02,960 我也可以用x,这将是完全一样的东西。 28 00:02:02,960 --> 00:02:09,520 (* P)。这是什么做的吗?这是什么明星是什么意思? 29 00:02:09,520 --> 00:02:13,120 [学生]是指在该点的值。 >>呀。 30 00:02:13,120 --> 00:02:17,590 因此,如果我们看它,它可以是非常有用的,画出来的图 31 00:02:17,590 --> 00:02:22,230 这是一个小方块的记忆,而这恰好为x值4, 32 00:02:22,230 --> 00:02:25,980 然后我们有一个小盒子,内存为P, 33 00:02:25,980 --> 00:02:31,590 ,因此P点为x,所以我们画一个箭头从p到x。 34 00:02:31,590 --> 00:02:40,270 所以,当我们说我们说* P框,即p。 35 00:02:40,270 --> 00:02:46,480 星是按照箭头,然后做你想做的那个盒子在那里。 36 00:02:46,480 --> 00:03:01,090 因此,我可以说,* P = 7;将去框,x和变化,7。 37 00:03:01,090 --> 00:03:13,540 或者,我可以说Z = * P * 2,这是令人困惑,因为它的明星,明星。 38 00:03:13,540 --> 00:03:19,230 一星级提领P,其他明星乘以2。 39 00:03:19,230 --> 00:03:26,780 请注意,我可以有一样好更换用x * P。 40 00:03:26,780 --> 00:03:29,430 您可以使用它们以同样的方式。 41 00:03:29,430 --> 00:03:38,000 再后来我可以有p指向一个完全新的东西。 42 00:03:38,000 --> 00:03:42,190 我只能说,P = &z; 43 00:03:42,190 --> 00:03:44,940 所以,现在P没有再久点为x,它指向到z。 44 00:03:44,940 --> 00:03:50,510 任何时候,我做* P一样做Ž。 45 00:03:50,510 --> 00:03:56,170 因此,有用的事情是,一旦我们开始进入功能。 46 00:03:56,170 --> 00:03:59,790 >> 声明一个指针,它指向的东西,这是一种无用的 47 00:03:59,790 --> 00:04:03,140 然后你只提领 48 00:04:03,140 --> 00:04:06,060 时,你也可以使用原来的变量开始。 49 00:04:06,060 --> 00:04:18,190 但是,当你进入功能 - 让我们说,我们有一些功能,诠释富, 50 00:04:18,190 --> 00:04:32,810 需要一个指针,* p = 6; 51 00:04:32,810 --> 00:04:39,990 就像我们之前看到的掉期,你不能做一个有效的交换和一个单独的函数 52 00:04:39,990 --> 00:04:45,180 只是路过,因为一切都在C总是按值传递的整数。 53 00:04:45,180 --> 00:04:48,360 即使当你通过指针是按值传递的。 54 00:04:48,360 --> 00:04:51,940 碰巧的是,这些值是内存地址。 55 00:04:51,940 --> 00:05:00,770 所以当我说富(P);我的指针传递到函数foo 56 00:05:00,770 --> 00:05:03,910 然后foo是做* P = 6; 57 00:05:03,910 --> 00:05:08,600 所以里面的函数,* p是等价于x, 58 00:05:08,600 --> 00:05:12,720 但我不能使用x里面的这个函数该功能,因为它不是范围之内。 59 00:05:12,720 --> 00:05:19,510 因此,* p = 6是唯一的出路,我可以访问另一个函数的局部变量。 60 00:05:19,510 --> 00:05:23,600 或者,指针是唯一的出路,我可以访问另一个函数的局部变量。 61 00:05:23,600 --> 00:05:31,600 [学生]:比方说,你想返回一个指针。你究竟是如何做到这一点? 62 00:05:31,600 --> 00:05:44,270 [鲍登]返回一个指针的东西,如int y = 3的回报&Y? >> [学生]是啊。 63 00:05:44,270 --> 00:05:48,480 鲍登]好吧。你不应该这样做。这是不好的。 64 00:05:48,480 --> 00:05:59,480 我想我看到了在这些演讲的幻灯片,你开始看到这个图的内存 65 00:05:59,480 --> 00:06:02,880 在这里,你有内存地址0 66 00:06:02,880 --> 00:06:09,550 在这里你有内存地址4演出或2到32。 67 00:06:09,550 --> 00:06:15,120 那么,你已经得到了一些东西,有些东西,然后你有你的堆栈 68 00:06:15,120 --> 00:06:21,780 你有你的堆,你刚开始学习,长大了。 69 00:06:21,780 --> 00:06:24,390 [学生]是不是在堆上面的堆栈? 70 00:06:24,390 --> 00:06:27,760 >> 是啊。堆在上面,不是吗? >> [学生]好了,他把0之上。 71 00:06:27,760 --> 00:06:30,320 [学生]:哦,他把0之上。 >> [学生]:哦,好吧。 72 00:06:30,320 --> 00:06:36,060 免责声明:Anywhere与CS50你要这样看。 >> [学生]好吧。 73 00:06:36,060 --> 00:06:40,290 只是,当你第一次看到栈, 74 00:06:40,290 --> 00:06:45,000 就像当你想到一个堆栈,你认为彼此顶部堆放的东西。 75 00:06:45,000 --> 00:06:50,810 因此,我们倾向于翻转左右,所以堆栈的增长就像一个堆栈通常会 76 00:06:50,810 --> 00:06:55,940 代替堆栈垂下。 >> [学生]不要堆技术上长大过,虽然? 77 00:06:55,940 --> 00:07:01,100 这取决于你的意思是长大了。 78 00:07:01,100 --> 00:07:04,010 堆和栈的增长方向相反。 79 00:07:04,010 --> 00:07:09,420 栈是在不断增长,在这个意义上,它的成长过程 80 00:07:09,420 --> 00:07:12,940 朝着更高的内存地址和堆增长 81 00:07:12,940 --> 00:07:17,260 在它的增长向低内存地址。 82 00:07:17,260 --> 00:07:20,250 因此,顶端是0和底部是高的存储器地址。 83 00:07:20,250 --> 00:07:26,390 他们两人都不断增长,只是在相反的方向。 84 00:07:26,390 --> 00:07:29,230 [学生]:我刚才只是说,因为你说你把堆栈的底部 85 00:07:29,230 --> 00:07:33,640 因为它的堆栈看起来更直观,因为在堆的顶部开始, 86 00:07:33,640 --> 00:07:37,520 堆的本身上,所以that's - >>呀。 87 00:07:37,520 --> 00:07:44,960 你也觉得长大了,大的堆空间,但堆栈如此。 88 00:07:44,960 --> 00:07:50,280 因此,堆栈是一个样的,我们要展现长大的。 89 00:07:50,280 --> 00:07:55,390 但是无论你看,否则会显示地址0顶部 90 00:07:55,390 --> 00:07:59,590 和最高的内存地址的底部,所以这是你的内存通常的看法。 91 00:07:59,590 --> 00:08:02,100 >> 你有问题吗? 92 00:08:02,100 --> 00:08:04,270 [学生]:你能告诉我们更多的堆? 93 00:08:04,270 --> 00:08:06,180 是啊。我会在第二。 94 00:08:06,180 --> 00:08:12,220 首先,要追溯到为什么返回&Y是一件坏事, 95 00:08:12,220 --> 00:08:18,470 在栈上,你有一堆的栈帧代表所有的功能 96 00:08:18,470 --> 00:08:20,460 已被调用。 97 00:08:20,460 --> 00:08:27,990 因此,忽略以前的事情,你的堆栈的顶部总是要的主要功能 98 00:08:27,990 --> 00:08:33,090 因为这是第一个函数的调用。 99 00:08:33,090 --> 00:08:37,130 然后当你调用另一个函数的堆栈是怎么回事增长下降。 100 00:08:37,130 --> 00:08:41,640 所以,如果我调用一些函数,foo和它得到它自己的堆栈帧, 101 00:08:41,640 --> 00:08:47,280 它可以调用一些函数,酒吧,它得到它自己的堆栈帧。 102 00:08:47,280 --> 00:08:49,840 和酒吧可以是递归的,它可以调用本身, 103 00:08:49,840 --> 00:08:54,150 等第二次调用酒吧是会得到它自己的堆栈帧。 104 00:08:54,150 --> 00:08:58,880 因此,这些堆栈帧的局部变量 105 00:08:58,880 --> 00:09:03,450 和所有的功能参数 - 106 00:09:03,450 --> 00:09:08,730 此功能是在本地范围内的任何事情,这些堆栈帧。 107 00:09:08,730 --> 00:09:21,520 因此,这意味着当我说像酒吧是一个函数, 108 00:09:21,520 --> 00:09:29,270 我只是要声明一个整数,然后返回一个指针,该整数。 109 00:09:29,270 --> 00:09:33,790 Ÿ生活? 110 00:09:33,790 --> 00:09:36,900 [学生] Y住在酒吧。 >> [鲍登]是啊。 111 00:09:36,900 --> 00:09:45,010 在这个小广场的内存的某个地方,是一个较小的广场,有Y。 112 00:09:45,010 --> 00:09:53,370 当我返回&Y,我返回一个指针,这个小的内存块。 113 00:09:53,370 --> 00:09:58,400 但后来当函数返回时,它的堆栈帧被弹出堆栈。 114 00:10:01,050 --> 00:10:03,530 这就是为什么它被称为堆栈。 115 00:10:03,530 --> 00:10:06,570 这是堆栈的数据结构,如果你知道那是什么。 116 00:10:06,570 --> 00:10:11,580 甚至像一个托盘堆叠的例子, 117 00:10:11,580 --> 00:10:16,060 主要是要去的底部,然后你调用的第一个函数是要去最重要的是, 118 00:10:16,060 --> 00:10:20,400 ,你可以回到主,直到返回的所有功能,被称为 119 00:10:20,400 --> 00:10:22,340 已放置在它上面。 120 00:10:22,340 --> 00:10:28,650 >> [学生]所以,如果你做返回该值是Y,如有更改,恕不另行通知。 121 00:10:28,650 --> 00:10:31,290 是的,it's - >> [学生]会被覆盖。 >>呀。 122 00:10:31,290 --> 00:10:34,660 这是完全 - 如果你尝试 - 123 00:10:34,660 --> 00:10:38,040 这也将是一个int *吧,因为它返回一个指针, 124 00:10:38,040 --> 00:10:41,310 因此它的返回类型是int *。 125 00:10:41,310 --> 00:10:46,500 如果您尝试使用这个函数的返回值,这是不确定的行为 126 00:10:46,500 --> 00:10:51,770 因为指针所指向坏的内存。 >> [学生]好吧。 127 00:10:51,770 --> 00:11:01,250 那么,如果,例如,你宣布INT * Y = malloc的(如sizeof(int))? 128 00:11:01,250 --> 00:11:03,740 这是更好的。是。 129 00:11:03,740 --> 00:11:07,730 [学生]当我们谈到如何将事情拖到我们的回收站 130 00:11:07,730 --> 00:11:11,750 他们实际上没有被删除,我们只是失去它们的指针。 131 00:11:11,750 --> 00:11:15,550 因此,在这种情况下,我们实际上是删除值是它在内存中仍然存在? 132 00:11:15,550 --> 00:11:19,130 对于在大多数情况下,它会仍然存在。 133 00:11:19,130 --> 00:11:24,220 但是,让我们说,我们碰巧调用一些其他的功能,巴兹。 134 00:11:24,220 --> 00:11:28,990 巴兹在这里会得到它自己的堆栈帧。 135 00:11:28,990 --> 00:11:31,470 这将覆盖所有的这些东西, 136 00:11:31,470 --> 00:11:34,180 然后如果你稍后尝试和使用的指针,你有, 137 00:11:34,180 --> 00:11:35,570 它不会是相同的值。 138 00:11:35,570 --> 00:11:38,150 它已经改变了,只是因为你调用该函数巴兹。 139 00:11:38,150 --> 00:11:43,080 [学生],但我们没有,我们仍然可以得到3? 140 00:11:43,080 --> 00:11:44,990 鲍登在所有的可能性,你会的。 141 00:11:44,990 --> 00:11:49,670 但你不能依靠这一点。 Ç只是说未定义的行为。 142 00:11:49,670 --> 00:11:51,920 >> [学生]:哦,确实如此。好吧。 143 00:11:51,920 --> 00:11:58,190 所以,当你想返回一个指针,这是在使用中的malloc来。 144 00:12:00,930 --> 00:12:15,960 我其实只是返回的malloc(3 *表示sizeof(int))。 145 00:12:17,360 --> 00:12:24,050 我们将在malloc的在第二,但对malloc的想法是所有的局部变量 146 00:12:24,050 --> 00:12:26,760 始终走在堆栈中。 147 00:12:26,760 --> 00:12:31,570 任何malloced的堆,它会永远,始终是在堆上 148 00:12:31,570 --> 00:12:34,490 直到你明确地释放它。 149 00:12:34,490 --> 00:12:42,130 因此,这意味着,当你的malloc东西,在函数返回后,它要生存下去。 150 00:12:42,130 --> 00:12:46,800 [学生]程序停止运行后,它会生存吗? >>序号 151 00:12:46,800 --> 00:12:53,180 好了,所以它会在那里,直到该程序是所有的方式运行。 “是的。 152 00:12:53,180 --> 00:12:57,510 我们可以去当程序停止运行时,会发生什么。 153 00:12:57,510 --> 00:13:02,150 您可能需要提醒我,但是这​​是一个完全独立的东西。 154 00:13:02,150 --> 00:13:04,190 [学生]的malloc创建一个指针? >>呀。 155 00:13:04,190 --> 00:13:13,030 的malloc的 - >> [学生]我认为的malloc指定的内存块的指针可以使用。 156 00:13:15,400 --> 00:13:19,610 [鲍登我想,图。 >> [学生]因此,虽然此功能吗? 157 00:13:19,610 --> 00:13:26,430 [学生]是啊,malloc的指定,您可以使用一个内存块, 158 00:13:26,430 --> 00:13:30,470 然后返回该内存块的地址。 159 00:13:30,470 --> 00:13:36,750 >> 鲍登]是啊。所以,当你的malloc,你抓住一些的内存块 160 00:13:36,750 --> 00:13:38,260 这是目前在堆中。 161 00:13:38,260 --> 00:13:43,040 如果堆太小,则堆只是要发展壮大,它生长在这个方向。 162 00:13:43,040 --> 00:13:44,650 因此,让我们说,堆太小。 163 00:13:44,650 --> 00:13:49,960 然后,它的增长一点点,返回一个指向块,只是增长。 164 00:13:49,960 --> 00:13:55,130 当你免费的东西,你有更多的空间在堆中, 165 00:13:55,130 --> 00:14:00,030 于是在以后调用malloc的,可以重复使用,您以前释放的内存。 166 00:14:00,030 --> 00:14:09,950 malloc和free重要的事情是,它可以完全控制 167 00:14:09,950 --> 00:14:12,700 这些内存块的整个生命周期。 168 00:14:12,700 --> 00:14:15,420 全局变量是永远活着。 169 00:14:15,420 --> 00:14:18,500 在其范围内的局部变量还活着。 170 00:14:18,500 --> 00:14:22,140 只要你走过去花括号,局部变量都死了。 171 00:14:22,140 --> 00:14:28,890 Malloced记忆是活着的时候,你希望它是活着的 172 00:14:28,890 --> 00:14:33,480 然后被释放,当你告诉它被释放。 173 00:14:33,480 --> 00:14:38,420 其实这些都是只有3种类型的内存,真的。 174 00:14:38,420 --> 00:14:41,840 自动内存管理,这是堆栈。 175 00:14:41,840 --> 00:14:43,840 事情发生自动为您。 176 00:14:43,840 --> 00:14:46,910 当你说诠释的x,分配的内存诠释x。 177 00:14:46,910 --> 00:14:51,630 当x超出范围时,内存回收的x。 178 00:14:51,630 --> 00:14:54,790 有动态内存管理,这是malloc的, 179 00:14:54,790 --> 00:14:56,740 这是当你有控制。 180 00:14:56,740 --> 00:15:01,290 您可以动态决定内存时应该和不应该被分配。 181 00:15:01,290 --> 00:15:05,050 然后是静态的,它只是意味着它永远常存, 182 00:15:05,050 --> 00:15:06,610 这是全局变量。 183 00:15:06,610 --> 00:15:10,240 他们只是一直在内存中。 184 00:15:10,960 --> 00:15:12,760 >> 有问题吗? 185 00:15:14,490 --> 00:15:17,230 [学生]:你可以定义一个块用花括号 186 00:15:17,230 --> 00:15:21,220 但不必有一个if语句或while语句或类似的东西吗? 187 00:15:21,220 --> 00:15:29,130 您可以在一个函数中定义一个块,但有花括号。 188 00:15:29,130 --> 00:15:32,100 [学生]:所以,你不能只是像一个随机的一对花括号中的代码 189 00:15:32,100 --> 00:15:35,680 有局部变量吗? >>是的,你可以。 190 00:15:35,680 --> 00:15:45,900 里面的int酒吧,我们可以有{Y = 3;}。 191 00:15:45,900 --> 00:15:48,440 这应该是在这里。 192 00:15:48,440 --> 00:15:52,450 但是,这完全定义的诠释y的范围。 193 00:15:52,450 --> 00:15:57,320 之后,第二个大括号,Y不能使用了。 194 00:15:57,910 --> 00:16:00,630 你几乎从来没有这样做,虽然。 195 00:16:02,940 --> 00:16:07,370 程序结束时会发生什么, 196 00:16:07,370 --> 00:16:18,760 有一个误解/的一半谎言,我们只是使事情变得更容易给人以种。 197 00:16:18,760 --> 00:16:24,410 我们告诉你,当你分配内存 198 00:16:24,410 --> 00:16:29,860 你分配一些内存块,该变量。 199 00:16:29,860 --> 00:16:34,190 但是你没有真正直接接触RAM永远在你的程序中。 200 00:16:34,190 --> 00:16:37,490 如果你想起来了,我怎么德鲁 - 201 00:16:37,490 --> 00:16:44,330 实际上,如果你通过在GDB中,你会看到同样的事情。 202 00:16:51,120 --> 00:16:57,590 不管有多少次你运行你的程序或你正在运行什么程序, 203 00:16:57,590 --> 00:16:59,950 栈总是要开始 - 204 00:16:59,950 --> 00:17:06,510 你总是会看到的变数时,地址oxbffff的东西。 205 00:17:06,510 --> 00:17:09,470 它通常是在该地区的某个地方。 206 00:17:09,470 --> 00:17:18,760 但如何才能在2个节目可能有相同的内存的指针呢? 207 00:17:20,640 --> 00:17:27,650 [学生]有一些任意指定的地方应该是在RAM oxbfff 208 00:17:27,650 --> 00:17:31,320 其实是可以在不同的地方,当该功能被称为。 209 00:17:31,320 --> 00:17:35,920 是啊。这个词是虚拟内存。 210 00:17:35,920 --> 00:17:42,250 我们的想法是,每一个过程,每一个正在运行的程序在您的计算机上 211 00:17:42,250 --> 00:17:49,450 有自己的 - 让我们假设32位 - 完全独立的地址空间。 212 00:17:49,450 --> 00:17:51,590 这是一个地址空间。 213 00:17:51,590 --> 00:17:56,220 它有自己完全独立的4 GB的使用。 214 00:17:56,220 --> 00:18:02,220 >> 所以如果你同时运行2个节目,这个节目本身,看到4千兆字节 215 00:18:02,220 --> 00:18:04,870 这个程序看到4 GB的本身, 216 00:18:04,870 --> 00:18:07,720 这是不可能对这一计划取消引用指针 217 00:18:07,720 --> 00:18:10,920 从这项计划中的内存。 218 00:18:10,920 --> 00:18:18,200 虚拟内存是什么是一个进程的地址空间的映射 219 00:18:18,200 --> 00:18:20,470 实际的事情上RAM。 220 00:18:20,470 --> 00:18:22,940 所以,就看您的操作系统知道, 221 00:18:22,940 --> 00:18:28,080 嘿,这家伙解引用指针oxbfff,这实际上意味着 222 00:18:28,080 --> 00:18:31,040 他希望1000字节RAM, 223 00:18:31,040 --> 00:18:38,150 而如果这个程序指针引用oxbfff,他真正想要的RAM字节10000。 224 00:18:38,150 --> 00:18:41,590 他们可以随意相距甚远。 225 00:18:41,590 --> 00:18:48,730 在一个单一的进程的地址空间,即使是真实的东西。 226 00:18:48,730 --> 00:18:54,770 所以像看到所有4个千兆本身,但让我们说 - 227 00:18:54,770 --> 00:18:57,290 [学生]:是否每一个过程 - 228 00:18:57,290 --> 00:19:01,350 比方说,你有一台电脑,只有4 GB的RAM。 229 00:19:01,350 --> 00:19:06,430 每一个过程看到整个4 GB的吗? “是的。 230 00:19:06,430 --> 00:19:13,060 但是,4千兆字节,它认为是骗人的。 231 00:19:13,060 --> 00:19:20,460 这只是它认为这一切的记忆,因为它不知道任何其他过程中存在。 232 00:19:20,460 --> 00:19:28,140 它只会使用尽可能多的内存,因为它实际上需要。 233 00:19:28,140 --> 00:19:32,340 操作系统不会给RAM这一过程 234 00:19:32,340 --> 00:19:35,750 如果它不使用任何内存在这整个地区。 235 00:19:35,750 --> 00:19:39,300 它不会给该地区的记忆体。 236 00:19:39,300 --> 00:19:54,780 但这样的想法是 - 我想 - 我不能想到一个比喻。 237 00:19:54,780 --> 00:19:56,780 类比是很难的。 238 00:19:57,740 --> 00:20:02,700 的虚拟内存的问题之一,它解决的事情之一 239 00:20:02,700 --> 00:20:06,810 是的进程应该是彼此完全不知道。 240 00:20:06,810 --> 00:20:12,140 所以你可以写任何程序,任何指针解除引用, 241 00:20:12,140 --> 00:20:19,340 喜欢只写一个程序,说*(ox1234), 242 00:20:19,340 --> 00:20:22,890 而这解引用的内存地址为1234。 243 00:20:22,890 --> 00:20:28,870 >> 但它的操作系统,然后翻译一下1234手段。 244 00:20:28,870 --> 00:20:33,960 所以,如果1234年恰好是一个有效的内存地址,这个过程中, 245 00:20:33,960 --> 00:20:38,800 就像是在堆栈上或什么的,然后将返回值的内存地址 246 00:20:38,800 --> 00:20:41,960 据的过程都知道。 247 00:20:41,960 --> 00:20:47,520 但是,如果1234是不是一个有效的地址,如发生土地 248 00:20:47,520 --> 00:20:52,910 在一些小的内存,这里说的是超越的堆栈和堆超越 249 00:20:52,910 --> 00:20:57,200 和你还没有真正使用,那么这时候你得到的东西一样的segfaults 250 00:20:57,200 --> 00:21:00,260 因为你接触的记忆,你不应该接触。 251 00:21:07,180 --> 00:21:09,340 这也是如此 - 252 00:21:09,340 --> 00:21:15,440 一个32位系统,32位意味着你有32位以确定一个内存地址。 253 00:21:15,440 --> 00:21:22,970 这就是为什么指针为8个字节,因为32位是8个字节 - 或4个字节。 254 00:21:22,970 --> 00:21:25,250 指针是4个字节。 255 00:21:25,250 --> 00:21:33,680 所以,当你看到一个指针一样oxbfffff上,即是 - 256 00:21:33,680 --> 00:21:40,080 在任何给定的程序中,你可以构建任意的指针, 257 00:21:40,080 --> 00:21:46,330 任何地方,从OX0的牛8 f's的 - FFFFFFFF。 258 00:21:46,330 --> 00:21:49,180 [学生]:你不是说他们是4个字节? >>呀。 259 00:21:49,180 --> 00:21:52,730 [学生]然后每个字节 - >> [鲍登]十六进制。 260 00:21:52,730 --> 00:21:59,360 十六进制 - 5,6,7,8。所以指针,你要总是在十六进制。 261 00:21:59,360 --> 00:22:01,710 这只是我们如何分类的指针。 262 00:22:01,710 --> 00:22:05,240 每2个数字的十六进制数是1个字节。 263 00:22:05,240 --> 00:22:09,600 因此,有4个字节是8个十六进制数字。 264 00:22:09,600 --> 00:22:14,190 因此,每一个指针在32位系统将是4个字节, 265 00:22:14,190 --> 00:22:18,550 这意味着,在你的过程,你可以构造任意4个字节 266 00:22:18,550 --> 00:22:20,550 和一个指针,它 267 00:22:20,550 --> 00:22:32,730 这意味着,只要它是知道的,它可以处理一个完整的2到32个字节的存储器。 268 00:22:32,730 --> 00:22:34,760 虽然它并没有真正访问, 269 00:22:34,760 --> 00:22:40,190 即使你的电脑只有512兆,它认为它有那么多的记忆。 270 00:22:40,190 --> 00:22:44,930 和操作系统是足够聪明的,它只会分配你真正需要的。 271 00:22:44,930 --> 00:22:49,630 它不只是去了,哦,一个新的进程:4个音乐会。 272 00:22:49,630 --> 00:22:51,930 >> 是啊。 >> [学生]什么牛是什么意思?你为什么不写呢? 273 00:22:51,930 --> 00:22:54,980 这仅仅是十六进制的符号。 274 00:22:54,980 --> 00:22:59,590 当你看到一个数字开始牛的,连续的东西是十六进制的。 275 00:23:01,930 --> 00:23:05,760 [学生]您解释有关程序结束时会发生什么。 “是的。 276 00:23:05,760 --> 00:23:09,480 程序结束时会发生什么事是操作系统 277 00:23:09,480 --> 00:23:13,600 只是,它已经为这些地址,这就是它的映射删除。 278 00:23:13,600 --> 00:23:17,770 操作系统可以现在只是给另一个程序使用该内存。 279 00:23:17,770 --> 00:23:19,490 [学生]好的。 280 00:23:19,490 --> 00:23:24,800 所以,当你分配在堆或堆栈或全局变量或任何东西, 281 00:23:24,800 --> 00:23:27,010 他们都只是消失尽快程序结束 282 00:23:27,010 --> 00:23:32,120 因为操作系统现在是免费的,任何其他进程的内存。 283 00:23:32,120 --> 00:23:35,150 [学生]:即使有可能还是写的值吗? >>呀。 284 00:23:35,150 --> 00:23:37,740 值很可能仍然存在。 285 00:23:37,740 --> 00:23:41,570 这只是这将是很难得到他们。 286 00:23:41,570 --> 00:23:45,230 它更难以得到他们比是在一个已删除的文件 287 00:23:45,230 --> 00:23:51,450 因为被删除的文件类型的坐在那里,很长一段时间,和硬盘驱动器是大了很多。 288 00:23:51,450 --> 00:23:54,120 因此,它要覆盖的不同部分的内存 289 00:23:54,120 --> 00:23:58,640 在它发生之前,该文件用于在覆盖的内存块。 290 00:23:58,640 --> 00:24:04,520 但主记忆体,RAM,循环快了很多, 291 00:24:04,520 --> 00:24:08,040 所以要非常迅速地被覆盖。 292 00:24:10,300 --> 00:24:13,340 或其他任何问题吗? 293 00:24:13,340 --> 00:24:16,130 [学生]我有一个不同的题目的问题。 “好了。 294 00:24:16,130 --> 00:24:19,060 有没有人有问题吗? 295 00:24:20,170 --> 00:24:23,120 >> 好吧。不同的主题。 >> [学生]好吧。 296 00:24:23,120 --> 00:24:26,550 我所经历的实践考验, 297 00:24:26,550 --> 00:24:30,480 在其中的一个,它是谈论的sizeof 298 00:24:30,480 --> 00:24:35,630 和它返回的值或不同类型的变量。 “是的。 299 00:24:35,630 --> 00:24:45,060 它说,int和long都返回4,所以他们都是4个字节长。 300 00:24:45,060 --> 00:24:48,070 是一个int和long之间有什么区别呢,还是同样的事情吗? 301 00:24:48,070 --> 00:24:50,380 是的,是有差别的。 302 00:24:50,380 --> 00:24:52,960 C标准 - 303 00:24:52,960 --> 00:24:54,950 我可能会陷入困境。 304 00:24:54,950 --> 00:24:58,800 就像C标准C是什么,官方文件C. 305 00:24:58,800 --> 00:25:00,340 这是它说什么。 306 00:25:00,340 --> 00:25:08,650 因此,在C标准只是说,一个字符将永远,永远是1字节。 307 00:25:10,470 --> 00:25:19,040 之后的一切 - 简短的永远只是定义为大于或等于一个字符。 308 00:25:19,040 --> 00:25:23,010 这可能是严格大于,但不积极。 309 00:25:23,010 --> 00:25:31,940 一个int刚刚定义为大于或等于一个短。 310 00:25:31,940 --> 00:25:36,210 长定义为大于或等于int。 311 00:25:36,210 --> 00:25:41,600 和长的长是大于或等于长。 312 00:25:41,600 --> 00:25:46,610 因此,C标准定义是唯一的相对顺序的一切。 313 00:25:46,610 --> 00:25:54,880 实际的内存占用量普遍实施, 314 00:25:54,880 --> 00:25:57,640 但它在这一点上相当不错的。 >> [学生]好吧。 315 00:25:57,640 --> 00:26:02,490 因此,短裤几乎总是为2个字节。 316 00:26:04,920 --> 00:26:09,950 诠释几乎总是要为4个字节。 317 00:26:12,070 --> 00:26:15,340 长期多头几乎总是为8个字节。 318 00:26:17,990 --> 00:26:23,160 和渴望,这取决于您使用的是32位或64位系统。 319 00:26:23,160 --> 00:26:27,450 如此长的是将对应的系统的类型。 320 00:26:27,450 --> 00:26:31,920 如果您使用的是32位系统,如家电,这将是4个字节。 321 00:26:34,530 --> 00:26:42,570 如果您使用的是64位的,想了很多最新的计算机,这将是8个字节。 322 00:26:42,570 --> 00:26:45,230 >> 整数是几乎总是在这一点上的4个字节。 323 00:26:45,230 --> 00:26:47,140 长期多头几乎都是8个字节。 324 00:26:47,140 --> 00:26:50,300 在过去,整数用于仅是2个字节。 325 00:26:50,300 --> 00:26:56,840 但是请注意,这完全满足所有这些关系大于等于。 326 00:26:56,840 --> 00:27:01,280 ,只要是完全可以是相同的大小为一个整数, 327 00:27:01,280 --> 00:27:04,030 它也可以是相同的大小作为一个长长。 328 00:27:04,030 --> 00:27:11,070 它只是恰巧是在99.999%的系统,它是将等于 329 00:27:11,070 --> 00:27:15,800 int或一个很长很长。它仅仅依赖于32位或64位。 >> [学生]好吧。 330 00:27:15,800 --> 00:27:24,600 在彩车上,是如何指定的小数点位? 331 00:27:24,600 --> 00:27:27,160 作为二进制一样吗? >>呀。 332 00:27:27,160 --> 00:27:30,570 你不需要知道,CS50。 333 00:27:30,570 --> 00:27:32,960 你甚至不知道,在61。 334 00:27:32,960 --> 00:27:37,350 你不好好学习,真的在任何课程。 335 00:27:37,350 --> 00:27:42,740 这只是一个表象。 336 00:27:42,740 --> 00:27:45,440 我忘了确切的位分配。 337 00:27:45,440 --> 00:27:53,380 浮点的想法是,你分配一个特定的比特数来表示 - 338 00:27:53,380 --> 00:27:56,550 基本上,一切都在科学记数法。 339 00:27:56,550 --> 00:28:05,600 所以,你分配一个特定的数位代表的数量,如1.2345。 340 00:28:05,600 --> 00:28:10,200 我不能代表一个数字位数多于5。 341 00:28:12,200 --> 00:28:26,300 然后,你也分配一个特定的数位,它往往能像 342 00:28:26,300 --> 00:28:32,810 你只能去到了一定的数量,那样你可以有最大的指数, 343 00:28:32,810 --> 00:28:36,190 和你只能去到一定指数, 344 00:28:36,190 --> 00:28:38,770 喜欢,你可以有最小的指数。 345 00:28:38,770 --> 00:28:44,410 >> 我不记得确切的方式位被分配到所有这些值, 346 00:28:44,410 --> 00:28:47,940 但在一定的比特数的致力于为1.2345, 347 00:28:47,940 --> 00:28:50,930 另一个一定的比特数,致力于指数, 348 00:28:50,930 --> 00:28:55,670 它的唯一可能代表一个具有一定规模的指数。 349 00:28:55,670 --> 00:29:01,100 [学生]和双?那是像一个额外的长期持股量呢? >>呀。 350 00:29:01,100 --> 00:29:07,940 这是同样的事情,但现在你用8个字节,而不是4个字节为float。 351 00:29:07,940 --> 00:29:11,960 现在,您就可以使用9位或10位, 352 00:29:11,960 --> 00:29:16,630 而这将是能上去到第300,而不是100。 >> [学生]好吧。 353 00:29:16,630 --> 00:29:21,550 花车也是4个字节。 “是的。 354 00:29:21,550 --> 00:29:27,520 好了,再次,它可能取决于整体上普遍实施, 355 00:29:27,520 --> 00:29:30,610 但花车为4个字节,双打有8个。 356 00:29:30,610 --> 00:29:33,440 双打被称为双,因为它们的两倍大小的花车。 357 00:29:33,440 --> 00:29:38,380 [学生]好的。以及是否有两双吗? >>有没有。 358 00:29:38,380 --> 00:29:43,660 我想 - >> [学生]喜欢长期多头? >>呀。我不认为如此。是。 359 00:29:43,660 --> 00:29:45,950 [学生]在去年的测试的主要功能有问题 360 00:29:45,950 --> 00:29:49,490 是程序的一部分。 361 00:29:49,490 --> 00:29:52,310 答案是,它并没有你的程序的一部分。 362 00:29:52,310 --> 00:29:55,100 在什么情况下?这是我所看到的。 363 00:29:55,100 --> 00:29:59,090 [波顿似乎 - >> [学生]什么情况? 364 00:29:59,090 --> 00:30:02,880 你有问题吗? >> [学生]是的,我可以肯定拉起来。 365 00:30:02,880 --> 00:30:07,910 它没有,从技术上说,但基本上将是。 366 00:30:07,910 --> 00:30:10,030 [学生]我看到了一个不同的年份的。 367 00:30:10,030 --> 00:30:16,220 这是True或False:一个有效的 - “”哦,。c文件吗? 368 00:30:16,220 --> 00:30:18,790 [学生]所有的文件必须有 - [都讲一次 - 不知所云] 369 00:30:18,790 --> 00:30:21,120 好吧。所以这是分开的。 370 00:30:21,120 --> 00:30:26,800 >> 。c文件只需要包含的功能。 371 00:30:26,800 --> 00:30:32,400 你可以编译成机器代码,二进制文件,不管, 372 00:30:32,400 --> 00:30:36,620 没有它的可执行文件。 373 00:30:36,620 --> 00:30:39,420 一个有效的可执行文件必须有一个main函数。 374 00:30:39,420 --> 00:30:45,460 你可以写100个函数在1个文件,但没有主 375 00:30:45,460 --> 00:30:48,800 ,然后编译为二进制, 376 00:30:48,800 --> 00:30:54,460 那么你就写另一个文件,只有主,但这些函数调用了一堆 377 00:30:54,460 --> 00:30:56,720 在这个二进制文件在这里。 378 00:30:56,720 --> 00:31:01,240 因此,如果你正在做的可执行文件,这就是链接器 379 00:31:01,240 --> 00:31:05,960 是它结合了这2成一个可执行的二进制文件。 380 00:31:05,960 --> 00:31:11,400 因此,一个c文件并不需要在所有的主要功能。 381 00:31:11,400 --> 00:31:19,220 而在大的代码库,你会看到成千上万的文件和1个主文件。 382 00:31:23,960 --> 00:31:26,110 还有问题吗? 383 00:31:29,310 --> 00:31:31,940 [学生]是另一个问题。 384 00:31:31,940 --> 00:31:36,710 它说是一个编译器。 True或False? 385 00:31:36,710 --> 00:31:42,030 得到的答案是假的,我明白了为什么它不是像铿锵。 386 00:31:42,030 --> 00:31:44,770 但我们所说的是什么,如果不是吗? 387 00:31:44,770 --> 00:31:49,990 Make是基本上只是 - 我可以清楚地看到它调用它。 388 00:31:49,990 --> 00:31:52,410 但它只是运行命令。 389 00:31:53,650 --> 00:31:55,650 制作。 390 00:31:58,240 --> 00:32:00,870 我可以拉起来。是啊。 391 00:32:10,110 --> 00:32:13,180 哦,是的。同时也要做到这一点。 392 00:32:13,180 --> 00:32:17,170 这说的make工具的目的是自动确定 393 00:32:17,170 --> 00:32:19,610 一个大程序都需要重新编译件 394 00:32:19,610 --> 00:32:22,350 发出的命令重新编译它们。 395 00:32:22,350 --> 00:32:27,690 你可以让文件,绝对是巨大的。 396 00:32:27,690 --> 00:32:33,210 让看文件的时间戳记,就像我们之前说的, 397 00:32:33,210 --> 00:32:36,930 你可以编译单个文件,并且它不是,直到你得到的链接器 398 00:32:36,930 --> 00:32:39,270 他们拼凑成一个可执行文件。 399 00:32:39,270 --> 00:32:43,810 所以,如果你有10个不同的文件,你犯了一个变化1, 400 00:32:43,810 --> 00:32:47,870 那么是什么牌子要做的只是重新编译,1个文件 401 00:32:47,870 --> 00:32:50,640 然后重新链接起来。 402 00:32:50,640 --> 00:32:53,020 但比它更笨。 403 00:32:53,020 --> 00:32:55,690 它是由你来完全确定,这就是它应该做的。 404 00:32:55,690 --> 00:32:59,560 默认情况下,有能力认识到这一点时间戳的东西, 405 00:32:59,560 --> 00:33:03,220 但你可以写一个make文件做任何事情。 406 00:33:03,220 --> 00:33:09,150 你可以写一个文件,这样,当你键入它只是CD到另一个目录。 407 00:33:09,150 --> 00:33:15,560 我感到沮丧,因为我粘性里面的一切,我的产品 408 00:33:15,560 --> 00:33:21,740 然后我看到的PDF从Mac。 409 00:33:21,740 --> 00:33:30,720 >> 所以我去搜索,我可以干什么去了,连接到服务器, 410 00:33:30,720 --> 00:33:36,950 连接到服务器,我为我的设备,然后我打开PDF 411 00:33:36,950 --> 00:33:40,190 被编译使用LaTeX。 412 00:33:40,190 --> 00:33:49,320 但我感到沮丧,因为每一次我需要更新的PDF, 413 00:33:49,320 --> 00:33:53,900 我不得不将它复制到一个特定的目录,它可以访问 414 00:33:53,900 --> 00:33:57,710 它越来越烦了。 415 00:33:57,710 --> 00:34:02,650 所以我写了一个make文件,你必须定义它使事情。 416 00:34:02,650 --> 00:34:06,130 这是你如何使PDF LaTeX的。 417 00:34:06,130 --> 00:34:10,090 就像任何其他的make文件 - 我猜你没见过的牌子文件, 418 00:34:10,090 --> 00:34:13,510 但我们有一个全球性的品牌在家电文件,该文件只是说, 419 00:34:13,510 --> 00:34:16,679 如果你要编译一个C文件,使用铿锵。 420 00:34:16,679 --> 00:34:20,960 所以在这里,我让我说,在我的make文件 421 00:34:20,960 --> 00:34:25,020 这个文件,你会想与PDF LaTeX的编译。 422 00:34:25,020 --> 00:34:27,889 所以它的PDF胶乳做编译。 423 00:34:27,889 --> 00:34:31,880 提出的,是不能编译。它只是运行这些命令中指定的顺序我。 424 00:34:31,880 --> 00:34:36,110 所以它运行的PDF LaTeX的,它会将我希望它被复制到的目录, 425 00:34:36,110 --> 00:34:38,270 它的CD的目录和做其他的事情, 426 00:34:38,270 --> 00:34:42,380 但它是所有认识的文件改变时, 427 00:34:42,380 --> 00:34:45,489 ,如果它的变化,然后它会运行它应该运行的命令 428 00:34:45,489 --> 00:34:48,760 当文件更改。 >> [学生]好吧。 429 00:34:50,510 --> 00:34:54,420 我不知道在哪里的全球MAKE文件是为我检查出来的。 430 00:34:57,210 --> 00:35:04,290 其他问题吗?任何东西,从过去的测验?任何指针的东西吗? 431 00:35:06,200 --> 00:35:08,730 有一些微妙的指针一样的东西 - 432 00:35:08,730 --> 00:35:10,220 我不会能够找到一个竞猜的问题上 - 433 00:35:10,220 --> 00:35:16,250 但就像这样的事情。 434 00:35:19,680 --> 00:35:24,060 请确保你明白,当我说* X * Y - 435 00:35:24,890 --> 00:35:28,130 这不正是任何东西,我猜。 436 00:35:28,130 --> 00:35:32,140 但是,像* X * Y,那是2个变量是在栈上。 437 00:35:32,140 --> 00:35:37,220 当我说X = malloc的(如sizeof(int)),X仍然是一个变量在栈上, 438 00:35:37,220 --> 00:35:41,180 malloc的一些块以上的堆,我们在x点的堆。 439 00:35:41,180 --> 00:35:43,900 >> 因此,在栈上的堆的东西。 440 00:35:43,900 --> 00:35:48,100 无论何时,只要你的malloc什么,你不可避免地存储它里面一个指针。 441 00:35:48,100 --> 00:35:55,940 因此,该指针是在栈上,malloced块在堆中。 442 00:35:55,940 --> 00:36:01,240 很多人感到困惑,并说* X = malloc的,x是在堆上。 443 00:36:01,240 --> 00:36:04,100 号X点是在堆上。 444 00:36:04,100 --> 00:36:08,540 x本身是在栈上,无论出于何种原因,除非你有X是一个全局变量, 445 00:36:08,540 --> 00:36:11,960 在这种情况下,它正好是在另一个区域中的内存。 446 00:36:13,450 --> 00:36:20,820 因此,跟踪,这些盒子和箭头图是很常见的测验。 447 00:36:20,820 --> 00:36:25,740 或者,如果它不是0测验,测验1。 448 00:36:27,570 --> 00:36:31,940 你应该知道所有的这些,在编译的步骤 449 00:36:31,940 --> 00:36:35,740 因为你必须回答这些问题。是。 450 00:36:35,740 --> 00:36:38,940 [学生]:我们能不能​​去这些步骤 - >>当然。 451 00:36:48,340 --> 00:36:58,640 我们之前的步骤和编译预处理, 452 00:36:58,640 --> 00:37:16,750 编译,汇编和链接。 453 00:37:16,750 --> 00:37:21,480 预处理。那是干什么的? 454 00:37:29,720 --> 00:37:32,290 这是最简单的一步 - 嗯,不喜欢 - 455 00:37:32,290 --> 00:37:35,770 这并不意味着它应该是显而易见的,但是它是最容易的一步。 456 00:37:35,770 --> 00:37:38,410 你们能实现它自己。是啊。 457 00:37:38,410 --> 00:37:43,410 [学生]你有你的,包括像这样的副本,然后还定义。 458 00:37:43,410 --> 00:37:49,250 它看起来的东西如#include和#define, 459 00:37:49,250 --> 00:37:53,800 它只是复制和粘贴那些真正的意思。 460 00:37:53,800 --> 00:37:59,240 因此,当你说#cs50.h的,预处理的复制和粘贴cs50.h 461 00:37:59,240 --> 00:38:01,030 到该线。 462 00:38:01,030 --> 00:38:06,640 当你说#定义x为4,预处理整个程序 463 00:38:06,640 --> 00:38:10,400 4 x的所有实例替换。 464 00:38:10,400 --> 00:38:17,530 因此,预处理器需要一个有效的C文件,并输出一个有效的C文件 465 00:38:17,530 --> 00:38:20,300 事情已经复制和粘贴。 466 00:38:20,300 --> 00:38:24,230 所以,现在编译。那是干什么的? 467 00:38:25,940 --> 00:38:28,210 [学生]从C到二进制。 468 00:38:28,210 --> 00:38:30,970 >> 鲍登]不走,一路为二进制。 469 00:38:30,970 --> 00:38:34,220 [学生]为机器代码呢? >>这是不是机器代码。 470 00:38:34,220 --> 00:38:35,700 [学生]大会? >>大会。 471 00:38:35,700 --> 00:38:38,890 在大会之前,去所有的方式为C代码, 472 00:38:38,890 --> 00:38:45,010 和大多数语言做这样的事情。 473 00:38:47,740 --> 00:38:50,590 选择任何高级语言,如果你要编译它, 474 00:38:50,590 --> 00:38:52,390 它的编译步骤。 475 00:38:52,390 --> 00:38:58,140 首先,它要编译Python到C,那么它会编译C大会, 476 00:38:58,140 --> 00:39:01,600 然后,大会会得到转换为二进制。 477 00:39:01,600 --> 00:39:07,800 所以编译是把它从C到大会。 478 00:39:07,800 --> 00:39:12,130 这个词编译通常是指把它从一个更高的水平 479 00:39:12,130 --> 00:39:14,340 到一个较低的电平的编程语言。 480 00:39:14,340 --> 00:39:19,190 因此,这是唯一的编译步骤,你开始与一个高层次的语言 481 00:39:19,190 --> 00:39:23,270 并最终在一个低层次的语言,这就是为什么被称为编译步骤。 482 00:39:25,280 --> 00:39:33,370 [学生]在编写过程中,让我们说你已经完成了#cs50.h. 483 00:39:33,370 --> 00:39:42,190 编译器重新编译cs50.h,类似的功能,在那里, 484 00:39:42,190 --> 00:39:45,280 翻译成汇编代码为好, 485 00:39:45,280 --> 00:39:50,830 或将它复制并粘贴大会前的东西吗? 486 00:39:50,830 --> 00:39:56,910 cs50.h将几乎永远不会结束在大会。 487 00:39:59,740 --> 00:40:03,680 函数原型和事情的东西一样,只是你要小心。 488 00:40:03,680 --> 00:40:09,270 它保证了编译器可以检查的事情,比如你调用函数 489 00:40:09,270 --> 00:40:12,910 用正确的返回类型和正确的参数之类的东西。 490 00:40:12,910 --> 00:40:18,350 >> 所以cs50.h将预处理到文件中,然后当它的编译 491 00:40:18,350 --> 00:40:22,310 它基本上是扔掉后,确保一切都被正确调用。 492 00:40:22,310 --> 00:40:29,410 但定义的函数在CS50库,这是分开cs50.h, 493 00:40:29,410 --> 00:40:33,610 那些不会被单独编译。 494 00:40:33,610 --> 00:40:37,270 这实际上会在连接步骤,所以我们会在第二。 495 00:40:37,270 --> 00:40:40,100 但首先,什么是装配? 496 00:40:41,850 --> 00:40:44,500 [学生]:大会为二进制? >>呀。 497 00:40:46,300 --> 00:40:48,190 组装。 498 00:40:48,190 --> 00:40:54,710 我们不把它编译,因为大会是一个很值得纯二进制翻译。 499 00:40:54,710 --> 00:41:00,230 大会二进制的逻辑是非常小的。 500 00:41:00,230 --> 00:41:03,180 这就像在一个表中查找,哦,我们有这个指令; 501 00:41:03,180 --> 00:41:06,290 对应于二进制01110。 502 00:41:10,200 --> 00:41:15,230 因此,组装一般输出。o文件的文件。 503 00:41:15,230 --> 00:41:19,020 o文件是我们之前说的, 504 00:41:19,020 --> 00:41:21,570 怎样文件不需要有一个主要的功能。 505 00:41:21,570 --> 00:41:27,640 可将任何文件编译成。o文件,只要它是一个有效的C文件。 506 00:41:27,640 --> 00:41:30,300 它可以被编译成。Ø。 507 00:41:30,300 --> 00:41:43,030 现在,连接实际上是带来了一堆的。o文件,并给他们带来一个可执行的。 508 00:41:43,030 --> 00:41:51,110 因此,联做的是你能想到的的CS50库的。o文件。 509 00:41:51,110 --> 00:41:56,980 这是一个已编译的二进制文件。 510 00:41:56,980 --> 00:42:03,530 因此,当你编译你的文件,你的hello.c的,这调用getString, 511 00:42:03,530 --> 00:42:06,360 ,hello.c中被编译成hello.o 512 00:42:06,360 --> 00:42:08,910 hello.o是二进制。 513 00:42:08,910 --> 00:42:12,830 它使用GetString的,所以它需要去到cs50.o的, 514 00:42:12,830 --> 00:42:16,390 连接器smooshes在一起,并将其复制到这个文件中的GetString 515 00:42:16,390 --> 00:42:20,640 和出来一个可执行文件,它需要的所有功能。 516 00:42:20,640 --> 00:42:32,620 因此,cs50.o是不实际的O文件,但它是足够接近,有没有根本的区别。 517 00:42:32,620 --> 00:42:36,880 因此,联刚带来了一大堆的文件 518 00:42:36,880 --> 00:42:41,390 分别包含了所有的功能,我需要使用 519 00:42:41,390 --> 00:42:46,120 并创建将实际运行的可执行文件。 520 00:42:48,420 --> 00:42:50,780 >> 因此,这也是我们之前说什么 521 00:42:50,780 --> 00:42:55,970 在那里你可以有1000个。c文件,在编译所有。o文件, 522 00:42:55,970 --> 00:43:00,040 这可能会需要一段时间,那么你改变1。c文件。 523 00:43:00,040 --> 00:43:05,480 你只需要重新编译。c文件,然后重新链接一切, 524 00:43:05,480 --> 00:43:07,690 连接一切重新走到一起。 525 00:43:09,580 --> 00:43:11,430 [学生]当我们连接我们写lcs50吗? 526 00:43:11,430 --> 00:43:20,510 是啊,所以lcs50。这标志信号连接器,你应该链接该库中。 527 00:43:26,680 --> 00:43:28,910 有问题吗? 528 00:43:41,310 --> 00:43:46,860 我们走了过来二进制以外的第一堂课,5秒吗? 529 00:43:50,130 --> 00:43:53,010 我不认为如此。 530 00:43:55,530 --> 00:43:58,820 你应该知道,我们已经讨论了所有的大的OS, 531 00:43:58,820 --> 00:44:02,670 ,你应该是可以的,如果我给你一个函数, 532 00:44:02,670 --> 00:44:09,410 你可以说这是大O,大约。好,大O是粗糙的。 533 00:44:09,410 --> 00:44:15,300 所以如果你看到嵌套的for循环遍历相同数量的东西, 534 00:44:15,300 --> 00:44:22,260 如INT I,I > [学生] n的平方。 >>它往往为n的平方。 535 00:44:22,260 --> 00:44:25,280 如果您有三重嵌套的,它往往是n的三次方。 536 00:44:25,280 --> 00:44:29,330 这样的事情,你应该能够立即指出。 537 00:44:29,330 --> 00:44:33,890 你需要知道插入排序,冒泡排序和归并排序和所有的人。 538 00:44:33,890 --> 00:44:41,420 这是容易理解为什么他们是那些N的平方和n日志n和所有的 539 00:44:41,420 --> 00:44:47,810 因为我觉得有一个测验的一年,我们基本上给你 540 00:44:47,810 --> 00:44:55,050 冒泡排序算法的实现和说,“什么是这个函数的运行时间?” 541 00:44:55,050 --> 00:45:01,020 所以,如果你认出来冒泡排序,那么你可以立即回答n的平方。 542 00:45:01,020 --> 00:45:05,470 但是,如果你只是来看看,你不甚至需要认识到它的冒泡排序; 543 00:45:05,470 --> 00:45:08,990 你可以说这是这样做的,这。这是n的平方。 544 00:45:12,350 --> 00:45:14,710 [学生]有任何强硬的例子,你可以拿出, 545 00:45:14,710 --> 00:45:20,370 像类似的想法搞清楚? 546 00:45:20,370 --> 00:45:24,450 >> 我不认为我们会给你任何强硬的例子。 547 00:45:24,450 --> 00:45:30,180 冒泡排序的是艰难的,因为我们会去, 548 00:45:30,180 --> 00:45:36,280 甚至认为,只要你明白,你遍历数组 549 00:45:36,280 --> 00:45:41,670 为每个数组中的元素,这将是东西的N平方。 550 00:45:45,370 --> 00:45:49,940 有一般的问题,比如在这里,我们有 - 哦。 551 00:45:55,290 --> 00:45:58,530 就在几天前,道格声称,“我已经发明了一种算法,可以排序的数组 552 00:45:58,530 --> 00:46:01,780 “n个数O(log n)的时间!” 553 00:46:01,780 --> 00:46:04,900 那么,我们如何知道这是不可能的吗? 554 00:46:04,900 --> 00:46:08,850 [听不见的学生反应] >>呀。 555 00:46:08,850 --> 00:46:13,710 最起码,你要摸每个数组中的元素, 556 00:46:13,710 --> 00:46:16,210 所以这是不可能的,对数组进行排序 - 557 00:46:16,210 --> 00:46:20,850 如果一切是在未排序的顺序,那么你将要接触的数组中的一切, 558 00:46:20,850 --> 00:46:25,320 所以这是不可能做到这一点比O的n。 559 00:46:27,430 --> 00:46:30,340 [学生]您向我们展示了这个例子能够做到这一点,在O n个 560 00:46:30,340 --> 00:46:33,920 如果你使用了大量的内存。 >>呀。 561 00:46:33,920 --> 00:46:37,970 而that's - 我忘了什么that's - 计数排序? 562 00:46:47,360 --> 00:46:51,330 嗯。这是一个整数的排序算法。 563 00:46:59,850 --> 00:47:05,100 我一直在寻找这个特殊的名字,我不记得上周。 564 00:47:05,100 --> 00:47:13,000 是啊。这些类型的排序,可以完成的事情,大O的n。 565 00:47:13,000 --> 00:47:18,430 但也有局限性,你只能使用整数到了一定的数量。 566 00:47:20,870 --> 00:47:24,560 另外,如果你想的东西that's排序 - 567 00:47:24,560 --> 00:47:30,750 如果您的阵列为012,-12,151,400万, 568 00:47:30,750 --> 00:47:35,120 然后,单元素要完全毁掉整个分拣。 569 00:47:42,060 --> 00:47:44,030 >> 有问题吗? 570 00:47:49,480 --> 00:47:58,870 [学生]如果你有一个递归函数,它只是使递归调用 571 00:47:58,870 --> 00:48:02,230 在一个return语句,这是尾递归, 572 00:48:02,230 --> 00:48:07,360 这不使用更多的内存在运行时 573 00:48:07,360 --> 00:48:12,550 或至少​​使用可比较的记忆体作为一个迭代的解决方案吗? 574 00:48:12,550 --> 00:48:14,530 波顿:是的。 575 00:48:14,530 --> 00:48:19,840 它可能会有点慢,但不是真的。 576 00:48:19,840 --> 00:48:23,290 尾递归是相当不错的。 577 00:48:23,290 --> 00:48:32,640 再看看堆栈帧,让我们说,我们有主 578 00:48:32,640 --> 00:48:42,920 我们有int酒吧(X)的东西。 579 00:48:42,920 --> 00:48:52,310 这不是一个完美的递归函数,但回报酒吧(X - 1)。 580 00:48:52,310 --> 00:48:57,620 所以,很显然,这是有缺陷的。您需要基础案例之类的东西。 581 00:48:57,620 --> 00:49:00,360 但这里的想法是,这是尾递归, 582 00:49:00,360 --> 00:49:06,020 这意味着,当主调用酒吧,它会得到它的堆栈帧。 583 00:49:09,550 --> 00:49:12,440 在这个堆栈帧,将是一个小的内存块 584 00:49:12,440 --> 00:49:17,490 对应其参数x。 585 00:49:17,490 --> 00:49:25,840 因此,让我们说主要发生在调用酒吧(100); 586 00:49:25,840 --> 00:49:30,050 因此,x是要开始为100。 587 00:49:30,050 --> 00:49:35,660 如果编译器认识到这是一个尾递归函数, 588 00:49:35,660 --> 00:49:38,540 然后在酒吧禁止递归调用, 589 00:49:38,540 --> 00:49:45,490 而不是制造新的栈帧,这是堆栈中开始增长在很大程度上, 590 00:49:45,490 --> 00:49:48,220 最终它会运行到堆,然后你得到的segfaults 591 00:49:48,220 --> 00:49:51,590 因为内存开始碰撞。 592 00:49:51,590 --> 00:49:54,830 >> 因此,而不是它自己的堆栈帧,它可以实现, 593 00:49:54,830 --> 00:49:59,080 哎,我从来没有真正回来到这个堆栈帧, 594 00:49:59,080 --> 00:50:08,040 所以不是我就替换此参数与99,然后开始酒吧。 595 00:50:08,040 --> 00:50:11,810 ,然后它会再次将达到回转杆(X - 1), 596 00:50:11,810 --> 00:50:17,320 ,而不是一个新的堆栈帧,将刚刚替换其当前参数98 597 00:50:17,320 --> 00:50:20,740 然后跳回到最开始的吧。 598 00:50:23,860 --> 00:50:30,430 这些操作,更换,堆栈上的值,跳回到开始, 599 00:50:30,430 --> 00:50:32,430 是非常有效的。 600 00:50:32,430 --> 00:50:41,500 所以这不仅是一个单独的函数相同的内存使用情况,这是迭代 601 00:50:41,500 --> 00:50:45,390 因为你只能使用1堆栈帧,但你没有苦难的缺点 602 00:50:45,390 --> 00:50:47,240 调用函数。 603 00:50:47,240 --> 00:50:50,240 调用函数可能会有点昂贵,因为它需要做的这一切设置 604 00:50:50,240 --> 00:50:52,470 和拆卸,这一切的东西。 605 00:50:52,470 --> 00:50:58,160 因此,这尾递归还是不错的。 606 00:50:58,160 --> 00:51:01,170 [学生]:为什么不创建新的步骤? 607 00:51:01,170 --> 00:51:02,980 因为它意识到它并不需要。 608 00:51:02,980 --> 00:51:07,800 调用酒吧刚刚返回的递归调用。 609 00:51:07,800 --> 00:51:12,220 因此,它并不需要做任何的返回值。 610 00:51:12,220 --> 00:51:15,120 它只是将立即返回。 611 00:51:15,120 --> 00:51:20,530 因此,它只是要更换自己的参数,并重新开始。 612 00:51:20,530 --> 00:51:25,780 还有,如果你没有尾递归版本, 613 00:51:25,780 --> 00:51:31,460 然后你会得到所有这些酒吧,这个酒吧返回 614 00:51:31,460 --> 00:51:36,010 它有这一个,然后返回它的值,酒吧立即返回 615 00:51:36,010 --> 00:51:39,620 ,并返回它的值这一个,那么它只是将立即返回 616 00:51:39,620 --> 00:51:41,350 这一个,并返回它的值。 617 00:51:41,350 --> 00:51:45,350 因此,要保存弹出所有这些东西的堆栈 618 00:51:45,350 --> 00:51:48,730 因为返回值仅仅是将要通过所有的方式回到反正。 619 00:51:48,730 --> 00:51:55,400 那么为什么不直接取代我们的观点与更新的参数,并重新开始吗? 620 00:51:57,460 --> 00:52:01,150 如果该函数不是尾递归,如果你做这样的事情 - 621 00:52:01,150 --> 00:52:07,530 [学生]如果酒吧(X + 1)。 >>呀。 622 00:52:07,530 --> 00:52:11,770 >> 所以,如果你把它的条件,那么你正在做的事情的返回值。 623 00:52:11,770 --> 00:52:16,260 或者即使你只是做回2条(X - 1)。 624 00:52:16,260 --> 00:52:23,560 所以,现在巴( - 1)需要返回,以便它来计算该值的2倍, 625 00:52:23,560 --> 00:52:26,140 所以现在它需要有自己的独立的堆栈帧, 626 00:52:26,140 --> 00:52:31,180 现在,无论你怎么努力,你将需要 - 627 00:52:31,180 --> 00:52:34,410 这是不是尾递归的。 628 00:52:34,410 --> 00:52:37,590 [学生]我会尽量把一个递归的目标是一个尾递归 - 629 00:52:37,590 --> 00:52:41,450 鲍登在一个理想的世界,但在CS50你没有。 630 00:52:43,780 --> 00:52:49,280 一般情况下,为了得到尾递归,成立一个额外的参数 631 00:52:49,280 --> 00:52:53,550 栏将采取整数X到Y 632 00:52:53,550 --> 00:52:56,990 y对应到极致的事情,你要返回。 633 00:52:56,990 --> 00:53:03,650 那么你将要返回酒吧(X - 1),2 * Y。 634 00:53:03,650 --> 00:53:09,810 所以,这只是一个高层次的,你怎么变换的事情是尾递归。 635 00:53:09,810 --> 00:53:13,790 但是,额外的参数 - 636 00:53:13,790 --> 00:53:17,410 然后在结束时你达到你的基本情况,你刚刚返回y 637 00:53:17,410 --> 00:53:22,740 因为你已经积累的全部时间,你想要的返回值。 638 00:53:22,740 --> 00:53:27,280 你这种已经这样做迭代,但使用递归调用。 639 00:53:32,510 --> 00:53:34,900 有问题吗? 640 00:53:34,900 --> 00:53:39,890 [学生]也许指针的算术运算,如在使用字符串。 “当然可以。 641 00:53:39,890 --> 00:53:43,610 指针的算术运算。 642 00:53:43,610 --> 00:53:48,440 使用字符串时,很容易因为字符串是char明星, 643 00:53:48,440 --> 00:53:51,860 字符是永远的,始终是一个单字节, 644 00:53:51,860 --> 00:53:57,540 指针的算术运算相当于定期算术当你在处理字符串。 645 00:53:57,540 --> 00:54:08,790 远的不说的char * s =“你好”。 646 00:54:08,790 --> 00:54:11,430 因此,我们有一个内存块中。 647 00:54:19,490 --> 00:54:22,380 它需要6个字节,因为你总是需要空终止符。 648 00:54:22,380 --> 00:54:28,620 和char * s将要指向这个数组的开始。 649 00:54:28,620 --> 00:54:32,830 因此,S点。 650 00:54:32,830 --> 00:54:36,710 现在,这基本上是任何数组是如何工作的, 651 00:54:36,710 --> 00:54:40,780 无论它是否是一个由malloc或无论是在堆栈上的回报。 652 00:54:40,780 --> 00:54:47,110 任何数组基本上是一个指针阵列的开始, 653 00:54:47,110 --> 00:54:53,640 那么任何数组操作,任何索引,只是进入该数组一定的偏移。 654 00:54:53,640 --> 00:55:05,360 >> 所以当我说类似[3]这是怎么回事s和计数3个字符中。 655 00:55:05,360 --> 00:55:12,490 因此s [3],我们有0,1,2,3,因此s [3]将参考本升。 656 00:55:12,490 --> 00:55:20,460 [学生]我们可以达到相同的值,执行s + 3,然后括号明星的吗? 657 00:55:20,460 --> 00:55:22,570 是。 658 00:55:22,570 --> 00:55:26,010 这是相当于*(+ 3); 659 00:55:26,010 --> 00:55:31,240 ,这是永远总是相等的,无论你做什么。 660 00:55:31,240 --> 00:55:34,070 你永远需要使用括号的语法。 661 00:55:34,070 --> 00:55:37,770 您可以随时使用*(+ 3)语法。 662 00:55:37,770 --> 00:55:40,180 人们往往喜欢括号语法。 663 00:55:40,180 --> 00:55:43,860 [学生]因此,所有的数组实际上是指针。 664 00:55:43,860 --> 00:55:53,630 有轻微的区别,当我说×[4] >> [学生]这是否创建内存吗? 665 00:55:53,630 --> 00:56:03,320 鲍登,是要创建4个的整数堆栈上的,所以16个字节的整体。 666 00:56:03,320 --> 00:56:05,700 它会在栈上创建16个字节。 667 00:56:05,700 --> 00:56:09,190 x未存储在任何位置。 668 00:56:09,190 --> 00:56:13,420 这仅仅是一个符号参照开始的事情。 669 00:56:13,420 --> 00:56:17,680 因为你声明的数组里面的这个函数, 670 00:56:17,680 --> 00:56:22,340 编译器会做的仅仅是变量x的所有实例都替换 671 00:56:22,340 --> 00:56:26,400 与它发生在选择把16个字节。 672 00:56:26,400 --> 00:56:30,040 它不能这样做,因为用char * s是一个实际的指针。 673 00:56:30,040 --> 00:56:32,380 它是免费的,再点其他的事情。 674 00:56:32,380 --> 00:56:36,140 x是一个常数。你不能拥有它指向不同的数组。 >> [学生]好吧。 675 00:56:36,140 --> 00:56:43,420 但这样的想法,这个索引是一样的,不管它是否是一个传统的阵列 676 00:56:43,420 --> 00:56:48,230 或者如果它是一个指针的东西,或者如果它是一个到malloced数组的指针。 677 00:56:48,230 --> 00:56:59,770 而事实上,它是相等的,这是同样的事情。 678 00:56:59,770 --> 00:57:05,440 它实际上只是翻译的括号里面是什么,什么是左括号内, 679 00:57:05,440 --> 00:57:07,970 把它们加在一起,并取消引用。 680 00:57:07,970 --> 00:57:14,710 因此,这是一样有效(+)或S [3]。 681 00:57:16,210 --> 00:57:22,090 [学生]你能有2维数组的指针指向? 682 00:57:22,090 --> 00:57:27,380 >> 它的难度。传统上,没有。 683 00:57:27,380 --> 00:57:34,720 一个二维数组是一维数组使用一些方便的语法 684 00:57:34,720 --> 00:57:54,110 因为当我说诠释x [3] [3],这真的只是1阵列的9个值。 685 00:57:55,500 --> 00:58:03,000 因此,当I指数,编译器知道我的意思。 686 00:58:03,000 --> 00:58:13,090 如果我说,X [1] [2],它知道我要到第二行,所以​​将跳过第3, 687 00:58:13,090 --> 00:58:17,460 那么想的第二件事,所以它会得到这一个。 688 00:58:17,460 --> 00:58:20,480 但它仍然只是一个一维数组。 689 00:58:20,480 --> 00:58:23,660 所以,如果我想指定一个指向该数组的指针, 690 00:58:23,660 --> 00:58:29,770 我想说的* p = X; 691 00:58:29,770 --> 00:58:33,220 x的类型是 - 692 00:58:33,220 --> 00:58:38,280 粗略的说这是x的类型,因为它只是一个符号,它不是一个实际的变量, 693 00:58:38,280 --> 00:58:40,140 但它仅仅是一个int *。 694 00:58:40,140 --> 00:58:44,840 x是刚刚开始的指针。 >> [学生]好吧。 695 00:58:44,840 --> 00:58:52,560 因此,我将无法访问[1] [2]。 696 00:58:52,560 --> 00:58:58,370 我认为是有特殊的语法来声明一个指针, 697 00:58:58,370 --> 00:59:12,480 什么可笑的,如int(* p [ - 这是绝对荒谬的。我什至不知道。 698 00:59:12,480 --> 00:59:17,090 但有语法声明函数指针像括号和活动。 699 00:59:17,090 --> 00:59:22,960 它甚至有可能不会让你这样做。 700 00:59:22,960 --> 00:59:26,640 我回头看的东西,将真相告诉我。 701 00:59:26,640 --> 00:59:34,160 我会寻找它以后,如果有一个语法点。但你永远不会看到它。 702 00:59:34,160 --> 00:59:39,670 即使语法是过时的,如果你使用它,人们会感到困惑。 703 00:59:39,670 --> 00:59:43,540 多维数组是非常罕见的,因为它是。 704 00:59:43,540 --> 00:59:44,630 你几乎 - 705 00:59:44,630 --> 00:59:48,490 好吧,如果你在做矩阵的事情,它不会是罕见的, 706 00:59:48,490 --> 00:59:56,730 但在C中,你很少会被使用多维数组。 707 00:59:57,630 --> 01:00:00,470 是啊。 >> [学生]:比方说,你有一个很长的阵列。 708 01:00:00,470 --> 01:00:03,900 >> 因此,在虚拟内存中,它似乎是所有连续, 709 01:00:03,900 --> 01:00:05,640 彼此旁边的类似的元素, 710 01:00:05,640 --> 01:00:08,770 但在物理内存中,它有可能为被分裂了吗? “是的。 711 01:00:08,770 --> 01:00:16,860 虚拟内存的工作原理是,它只是分离 - 712 01:00:19,220 --> 01:00:24,860 的分配单元是页面,这往往是4千字节, 713 01:00:24,860 --> 01:00:29,680 所以说,当一个进程,哎,我想使用此内存, 714 01:00:29,680 --> 01:00:35,970 操作系统会分配它的那个小4千字节的内存块。 715 01:00:35,970 --> 01:00:39,100 即使你只使用一个单字节的内存块在整个不大, 716 01:00:39,100 --> 01:00:42,850 操作系统是要给它完整的4千字节。 717 01:00:42,850 --> 01:00:49,410 因此,这是什么意思是,我可以 - 让我们说这是我的筹码。 718 01:00:49,410 --> 01:00:53,180 该协议栈可以被分开。我的堆栈可能是兆字节,兆字节。 719 01:00:53,180 --> 01:00:55,020 我的筹码将是巨大的。 720 01:00:55,020 --> 01:01:00,220 但栈本身被分裂成单独的页面, 721 01:01:00,220 --> 01:01:09,010 如果我们看一下在这里,让我们说,这是我们的RAM, 722 01:01:09,010 --> 01:01:16,600 如果我有2 GB的RAM,这是我的第零个字节的RAM地址为0,如实际, 723 01:01:16,600 --> 01:01:22,210 这是2 GB一路下跌。 724 01:01:22,210 --> 01:01:27,230 因此,该页面可能对应到这个块在这里。 725 01:01:27,230 --> 01:01:29,400 在这里,该页面可能与此块。 726 01:01:29,400 --> 01:01:31,560 这可能对应于这一个在这里。 727 01:01:31,560 --> 01:01:35,540 因此,操作系统是免费分配物理内存 728 01:01:35,540 --> 01:01:39,320 任何单独的页面随意。 729 01:01:39,320 --> 01:01:46,180 这意味着,如果这条边界发生跨越阵列, 730 01:01:46,180 --> 01:01:50,070 发生一个数组左和右以此顺序一个页面 731 01:01:50,070 --> 01:01:54,460 那么该数组将被分割在物理内存中。 732 01:01:54,460 --> 01:01:59,280 然后当你退出程序,当进程结束时, 733 01:01:59,280 --> 01:02:05,690 这些映射将被删除,然后它是免费的,使用这些小的块,其他的事情。 734 01:02:14,730 --> 01:02:17,410 还有问题吗? 735 01:02:17,410 --> 01:02:19,960 [学生]指针的算术运算。 >>哦,是的。 736 01:02:19,960 --> 01:02:28,410 字符串比较容易,但看着像int类型, 737 01:02:28,410 --> 01:02:35,000 使之恢复为int x [4]; 738 01:02:35,000 --> 01:02:41,810 这是否是一个数组无论是到malloced 4个整数的数组指针, 739 01:02:41,810 --> 01:02:47,060 它会以同样的方式来对待。 740 01:02:50,590 --> 01:02:53,340 [学生]因此,数组的堆? 741 01:03:01,400 --> 01:03:05,270 [鲍登]数组是不是在堆上。 >> [学生]哦。 742 01:03:05,270 --> 01:03:08,320 >> [鲍登]这类型的磁盘阵列的倾向是在堆栈上 743 01:03:08,320 --> 01:03:12,220 除非你宣布它在, - 忽略全局变量。不要使用全局变量。 744 01:03:12,220 --> 01:03:16,280 里面的功能,我说×[4]; 745 01:03:16,280 --> 01:03:22,520 这将在栈上创建一个整数4块,这个数组。 746 01:03:22,520 --> 01:03:26,960 但是这个malloc(4 *表示sizeof(int));要去的堆。 747 01:03:26,960 --> 01:03:31,870 但在这一点上我可以在几乎相同的方式,使用x和p 748 01:03:31,870 --> 01:03:36,140 其他比我说的例外,前行约,您可以重新分配p。 749 01:03:36,140 --> 01:03:40,960 从技术上讲,它们的尺寸有所不同,但是这是完全不相干的。 750 01:03:40,960 --> 01:03:43,310 你从来没有真正使用它们的大小。 751 01:03:48,020 --> 01:03:56,810 我可以说的p P [3] = 2;或×[3] = 2; 752 01:03:56,810 --> 01:03:59,680 您可以使用完全相同的方式。 753 01:03:59,680 --> 01:04:01,570 因此,指针的算术运算 - 是的。 754 01:04:01,570 --> 01:04:07,390 [学生]你没有做P *如果你有括号? 755 01:04:07,390 --> 01:04:11,720 括号内是一个隐式的间接引用。 “好了。 756 01:04:11,720 --> 01:04:20,200 其实,你在说什么的你可以得到多维数组 757 01:04:20,200 --> 01:05:02,650 的指针,你可以做的是一样的东西,比方说,** PP = malloc的(如sizeof(*)* 5); 758 01:05:02,650 --> 01:05:06,900 我就写了这一切第一。 759 01:05:37,880 --> 01:05:41,020 我不想要的那一个。 760 01:05:41,020 --> 01:05:42,550 好吧。 761 01:05:42,550 --> 01:05:48,910 我在这里做的是 - 这应该是PP [I]。 762 01:05:48,910 --> 01:05:53,680 因此,PP是一种指向指针的指针。 763 01:05:53,680 --> 01:06:02,420 ,你mallocing PP指向到一个数组中的5 INT星。 764 01:06:02,420 --> 01:06:10,950 因此,在内存中,你必须在栈上页 765 01:06:10,950 --> 01:06:20,150 要5块,这些都是自己的指针指向一个数组。 766 01:06:20,150 --> 01:06:28,210 然后,当我的malloc在这里,我的malloc的那些个人,每个指针 767 01:06:28,210 --> 01:06:32,080 应该指向一个单独的4个字节的堆块。 768 01:06:32,080 --> 01:06:35,870 因此,这点到4个字节。 769 01:06:37,940 --> 01:06:40,660 而这一次不同的4个字节。 770 01:06:40,660 --> 01:06:43,200 >> 所有的人都指向自己的4个字节。 771 01:06:43,200 --> 01:06:49,080 这给了我做多维的事情的一种方式。 772 01:06:49,080 --> 01:06:58,030 我能说的PP [3] [4],但现在这是不一样的东西,多维数组 773 01:06:58,030 --> 01:07:05,390 因为多维数组它翻译[3] [4]到一个单一的偏移量x数组。 774 01:07:05,390 --> 01:07:14,790 这解引用,P,访问的第三个指标,然后解引用 775 01:07:14,790 --> 01:07:20,790 访问 - 将是无效的 - 第二个索引。 776 01:07:24,770 --> 01:07:31,430 而当我们的int x [3] [4]前一个多维数组 777 01:07:31,430 --> 01:07:35,740 和双支架时,它真的只有一个解引用, 778 01:07:35,740 --> 01:07:40,490 你一个单一的指针和一个偏移量, 779 01:07:40,490 --> 01:07:42,850 这是真正的2D引用。 780 01:07:42,850 --> 01:07:45,840 你跟着2个独立的指针。 781 01:07:45,840 --> 01:07:50,420 因此,这在技术上也是可以让你有多维数组 782 01:07:50,420 --> 01:07:53,550 其中每个单独的阵列是不同的尺寸。 783 01:07:53,550 --> 01:07:58,000 因此,我认为锯齿状的多维数组是它叫什么 784 01:07:58,000 --> 01:08:01,870 因为真正的第一件事可能指向的东西,有10个元素, 785 01:08:01,870 --> 01:08:05,540 第二件事情可能指向的东西,有100个元素。 786 01:08:05,540 --> 01:08:10,790 [学生]:是否有任何限制数的指针,你可以有 787 01:08:10,790 --> 01:08:14,290 指向其他指针? >>序号 788 01:08:14,290 --> 01:08:17,010 您可以有int ***** p。 789 01:08:18,050 --> 01:08:23,760 返回指针的算术运算 - >> [学生]哦。 >>呀。 790 01:08:23,760 --> 01:08:35,649 [学生]如果我有int ** p,然后我做了一个间接引用和我说,P *是等于这个值, 791 01:08:35,649 --> 01:08:39,560 它只会做1级提领吗? “是的。 792 01:08:39,560 --> 01:08:43,340 所以,如果我要访问的最后一个指针指向的东西 - 793 01:08:43,340 --> 01:08:46,210 然后,你*** P。 “好了。 794 01:08:46,210 --> 01:08:54,080 因此,这是p点到1块到另一个块,点,点到另一个块。 795 01:08:54,080 --> 01:09:02,010 然后,如果你做* P =别的东西,那么你正在改变这 796 01:09:02,010 --> 01:09:13,640 到现在指向一个不同的块。 “好了。 797 01:09:13,640 --> 01:09:17,649 >> [鲍登],如果这些malloced,那么你现在已经泄漏的内存 798 01:09:17,649 --> 01:09:20,430 除非你碰巧有这些不同的参考 799 01:09:20,430 --> 01:09:25,270 因为你不能得到那些的,你只是扔掉了。 800 01:09:25,270 --> 01:09:29,550 指针的算术运算。 801 01:09:29,550 --> 01:09:36,310 ×[4];要分配4个整数的数组 802 01:09:36,310 --> 01:09:40,670 其中x是要指向的数组的开始。 803 01:09:40,670 --> 01:09:50,420 所以,当我说的东西,比如x [1],我想它的意思去的第二个整数的数组, 804 01:09:50,420 --> 01:09:53,319 这将是这一个。 805 01:09:53,319 --> 01:10:04,190 但实际上,这是4个字节到数组中,因为这个整数占用4个字节。 806 01:10:04,190 --> 01:10:08,470 因此,偏移量为1的真正含义偏移量为1 807 01:10:08,470 --> 01:10:12,030 倍的大小的任何类型的数组。 808 01:10:12,030 --> 01:10:17,170 这是一个整数数组,所以它知道做1次为int的大小,当它想抵消。 809 01:10:17,170 --> 01:10:25,260 其他的语法。请记住,这是相当于*(X + 1); 810 01:10:25,260 --> 01:10:35,250 当我说指针+ 1,返回的地址指针存储 811 01:10:35,250 --> 01:10:40,360 加1倍的大小的指针的类型。 812 01:10:40,360 --> 01:10:59,510 因此,如果x = ox100,那么x + 1 = ox104。 813 01:10:59,510 --> 01:11:19,750 你可以滥用这一点,说什么像char * C =(CHAR *)X; 814 01:11:19,750 --> 01:11:23,050 现在c将是相同的地址作为x。 815 01:11:23,050 --> 01:11:26,040 c是将是等于ox100, 816 01:11:26,040 --> 01:11:31,490 但C + 1将是等于ox101的 817 01:11:31,490 --> 01:11:38,030 因为指针的算术运算取决于您要添加的类型的指针。 818 01:11:38,030 --> 01:11:45,390 所以C + 1,它着眼于C,它是一个字符指针,所以它要增加1倍大小的char, 819 01:11:45,390 --> 01:11:48,110 总是为1,所以你得到101, 820 01:11:48,110 --> 01:11:54,890 而如果我这样做,X + 1 x,它也仍然是100将是104。 821 01:11:56,660 --> 01:12:06,340 [学生]:你可以使用C + +,以推进指针加1呢? 822 01:12:06,340 --> 01:12:09,810 是的,你可以。 823 01:12:09,810 --> 01:12:16,180 你不能这样做,因为x与x只是一个符号,它是一个常量,你不能改变x。 824 01:12:16,180 --> 01:12:22,610 >> 但C恰好是一个指针,所以C + +是完全有效的,这将增加1。 825 01:12:22,610 --> 01:12:32,440 如果c是一个int *,然后是C + + 104。 826 01:12:32,440 --> 01:12:41,250 + +指针的算术运算正如C +会做指针算术。 827 01:12:43,000 --> 01:12:48,870 其实,这是怎么了很多事情,比如合并排序 - 828 01:12:49,670 --> 01:12:55,710 创建副本的事情,而不是相反,你可以通过 - 829 01:12:55,710 --> 01:13:02,400 就像如果我想通过这半年的数组 - 让我们删除一些这方面的。 830 01:13:04,770 --> 01:13:10,520 比方说,我想通过这一边的数组的功能。 831 01:13:10,520 --> 01:13:12,700 ,我会通过该功能是什么? 832 01:13:12,700 --> 01:13:17,050 如果我通过X,我通过这个地址。 833 01:13:17,050 --> 01:13:23,780 不过,我想通过这个特定的地址。所以,我应该通过什么吗? 834 01:13:23,780 --> 01:13:26,590 [学生]指针+ 2? 835 01:13:26,590 --> 01:13:29,350 [鲍登]所以x + 2。是。 836 01:13:29,350 --> 01:13:31,620 这将是这个地址。 837 01:13:31,620 --> 01:13:42,810 您还可以经常看到它为x [2],然后在该地址。 838 01:13:42,810 --> 01:13:47,850 所以,你需要采取的地址,因为它的支架是一个隐式的间接引用。 839 01:13:47,850 --> 01:13:53,250 X [2]指的是在此框中的值,然后你要的那个盒子的地址, 840 01:13:53,250 --> 01:13:56,850 所以你说&X [2]。 841 01:13:56,850 --> 01:14:02,880 所以,如何在合并排序的东西,你想通过一半的东西的清单 842 01:14:02,880 --> 01:14:08,790 你真的只是通过X [2],而现在的递归调用, 843 01:14:08,790 --> 01:14:12,510 从那里开始我的新数组。 844 01:14:12,510 --> 01:14:15,130 最后一分钟的问题。 845 01:14:15,130 --> 01:14:20,050 [学生]如果我们不把一个符号或一个 - 那是什么叫什么名字? >>明星吗? 846 01:14:20,050 --> 01:14:23,200 [学生]星。 “从技术上讲,反引用运算符,但 - >> [学生]:取消引用。 847 01:14:23,200 --> 01:14:29,310 >> 如果我们不把一个明星或一个符号,会发生什么,如果我只是说y = x和x是一个指针? 848 01:14:29,310 --> 01:14:34,620 y的类型是什么? >> [学生]我只想说,它的指针。 849 01:14:34,620 --> 01:14:38,270 所以,如果你只是说y = x的,现在的X和Y点了同样的事情。 >> [学生]点了同样的事情。 850 01:14:38,270 --> 01:14:45,180 如果X是一个int的指针吗? >>,它会抱怨,因为你不能分配的指针。 851 01:14:45,180 --> 01:14:46,540 [学生]好的。 852 01:14:46,540 --> 01:14:51,860 请记住,指针,即使我们把它们画为箭头, 853 01:14:51,860 --> 01:15:02,010 真的,他们店 - * X - 实际上所有的x存储类似ox100, 854 01:15:02,010 --> 01:15:06,490 我们正好代表指向存储在100块。 855 01:15:06,490 --> 01:15:19,660 所以当我说诠释* Y = X,我只是复制ox100到Y, 856 01:15:19,660 --> 01:15:24,630 我们只是要表示为y,也指向ox100。 857 01:15:24,630 --> 01:15:39,810 如果我说我=(int)的X,然后我将存储任何的价值ox100是 858 01:15:39,810 --> 01:15:45,100 在它里面,但现在它会被解释为一个整数,而不是一个指针。 859 01:15:45,100 --> 01:15:49,310 但是,你需要的演员,否则,它会抱怨。 860 01:15:49,310 --> 01:15:53,300 [学生]:所以你的意思是投 - 861 01:15:53,300 --> 01:16:00,290 它是将要铸造的y值的x或铸造诠释的? 862 01:16:00,290 --> 01:16:03,700 [鲍登什么? 863 01:16:03,700 --> 01:16:07,690 [学生]好的。在这些括号将是一个x或AY? 864 01:16:07,690 --> 01:16:11,500 >> 鲍登]。 x和y是等效的。 >> [学生]好吧。 865 01:16:11,500 --> 01:16:14,390 因为他们是两个指针。 >>呀。 866 01:16:14,390 --> 01:16:21,050 [学生]因此,它会存储100的十六进制整数形式呢? >> [鲍登]是啊。 867 01:16:21,050 --> 01:16:23,620 但不是无论它指向的值。 868 01:16:23,620 --> 01:16:29,940 鲍登]是啊。 >> [学生]因此,只要整数形式的地址。好吧。 869 01:16:29,940 --> 01:16:34,720 [鲍登]如果你想一些奇怪的原因, 870 01:16:34,720 --> 01:16:38,900 你可以只处理指针和不会处理整数 871 01:16:38,900 --> 01:16:49,240 只是,如int * x = 0处。 872 01:16:49,240 --> 01:16:53,000 然后,你会得到真正的困惑,指针的算术运算后开始发生。 873 01:16:53,000 --> 01:16:56,570 因此,它们存储的数字是毫无意义的。 874 01:16:56,570 --> 01:16:58,940 它只是你怎么收场解释它们。 875 01:16:58,940 --> 01:17:02,920 所以我复制ox100从int *为int, 876 01:17:02,920 --> 01:17:07,790 我是自由分配 - 你们可能会被骂不投 - 877 01:17:07,790 --> 01:17:18,160 我可以自由分配(*)ox1234到这个任意整数*类似。 878 01:17:18,160 --> 01:17:25,480 所以ox123是一样有效的内存地址&Y。 879 01:17:25,480 --> 01:17:32,060 &Y发生,这几乎是ox123返回的东西。 880 01:17:32,060 --> 01:17:35,430 [学生]:这是一个非常酷的方式,从十六进制到十进制的形式, 881 01:17:35,430 --> 01:17:39,230 一样,如果你有一个指针,将其转换成一个int类型的吗? 882 01:17:39,230 --> 01:17:44,860 鲍登]你真的可以使用类似printf打印。 883 01:17:44,860 --> 01:17:50,300 比方说,我有int y = 100。 884 01:17:50,300 --> 01:18:02,700 因此我们知道printf(“%d个\ n - 你应该已经知道 - 打印为一个整数,%X。 885 01:18:02,700 --> 01:18:05,190 我们只需要打印十六进制的。 886 01:18:05,190 --> 01:18:10,760 因此,一个指针存储为十六进制, 887 01:18:10,760 --> 01:18:12,960 和没有存储为十进制,整数。 888 01:18:12,960 --> 01:18:14,700 一切都以二进制形式存储。 889 01:18:14,700 --> 01:18:17,950 只是我们往往显示为十六进制的指针 890 01:18:17,950 --> 01:18:23,260 因为我们认为在这4个字节的块的东西, 891 01:18:23,260 --> 01:18:25,390 和内存地址往往是熟悉的。 892 01:18:25,390 --> 01:18:28,890 我们是一样的,如果它开始与BF,那么它正好是在栈上。 893 01:18:28,890 --> 01:18:35,560 因此,这只是我们的解释为十六进制的指针。 894 01:18:35,560 --> 01:18:39,200 好吧。最后还有什么问题吗? 895 01:18:39,200 --> 01:18:41,700 >> 我会在这里了一下后,如果你还有什么。 896 01:18:41,700 --> 01:18:46,070 这是该结束。 897 01:18:46,070 --> 01:18:48,360 >> [学生]耶! [掌声] 898 01:18:51,440 --> 01:18:53,000 >> [CS50.TV]