1 00:00:00,000 --> 00:00:02,000 [Powered by Google Translate] [Valgrind] 2 00:00:02,000 --> 00:00:05,000 [Nate Хардисон, Гарвардский университет] 3 00:00:05,000 --> 00:00:07,000 Это CS50, CS50.TV] 4 00:00:07,000 --> 00:00:10,000 Некоторые из наиболее трудных ошибок в программах C 5 00:00:10,000 --> 00:00:13,000 приходят от неправильного управления памятью. 6 00:00:13,000 --> 00:00:15,000 Есть огромное количество способов, чтобы ввернуть вещи, 7 00:00:15,000 --> 00:00:17,000 в том числе выделяя неправильный объем памяти, 8 00:00:17,000 --> 00:00:20,000 забывая при инициализации переменных, 9 00:00:20,000 --> 00:00:23,000 письменной форме до или после окончания буфера, 10 00:00:23,000 --> 00:00:25,000 и освобождение сохранить память несколько раз. 11 00:00:25,000 --> 00:00:28,000 Симптомы варьируются от сбои 12 00:00:28,000 --> 00:00:30,000 таинственно перезаписаны значениями, 13 00:00:30,000 --> 00:00:34,000 Часто на местах и ​​временах далеких от первоначальной ошибки. 14 00:00:34,000 --> 00:00:37,000 Отслеживание наблюдаемого проблемы обратно в основной первопричины 15 00:00:37,000 --> 00:00:39,000 может быть сложной задачей, 16 00:00:39,000 --> 00:00:42,000 но, к счастью, есть полезный программу под названием Valgrind 17 00:00:42,000 --> 00:00:44,000 что может сделать многое, чтобы помочь. 18 00:00:44,000 --> 00:00:47,000 >> Вы запускаете программу под Valgrind чтобы 19 00:00:47,000 --> 00:00:50,000 обширные проверки динамической памяти ассигнований и доступов. 20 00:00:50,000 --> 00:00:53,000 Когда Valgrind обнаруживает проблему, он дает вам немедленный, 21 00:00:53,000 --> 00:00:56,000 прямой информации, которая позволяет 22 00:00:56,000 --> 00:00:58,000 легче найти и исправить ошибку. 23 00:00:58,000 --> 00:01:01,000 Valgrind также сообщения о менее опасны проблем с памятью, 24 00:01:01,000 --> 00:01:04,000 таких как утечки памяти, выделение динамической памяти, 25 00:01:04,000 --> 00:01:07,000 и забыть, чтобы освободить его. 26 00:01:07,000 --> 00:01:10,000 Нравится наш компилятор, Clang, на наш отладчик, GDB, 27 00:01:10,000 --> 00:01:14,000 Valgrind является свободным программным обеспечением, и она установлена ​​на прибор. 28 00:01:14,000 --> 00:01:16,000 Valgrind работает на вашем исполняемый файл, 29 00:01:16,000 --> 00:01:20,000 не ваш. или с. ч. файлов исходного кода, 30 00:01:20,000 --> 00:01:23,000 так что вы собрали последнюю дату копии вашей программы 31 00:01:23,000 --> 00:01:25,000 использованием Clang или сделать. 32 00:01:25,000 --> 00:01:28,000 Затем запустить программу под Valgrind может быть 33 00:01:28,000 --> 00:01:32,000 так прост, как только префикс стандартной команды программы со словом Valgrind, 34 00:01:32,000 --> 00:01:35,000 который запускает Valgrind и запускает программу внутри него. 35 00:01:35,000 --> 00:01:38,000 При запуске Valgrind делает некоторые сложные 36 00:01:38,000 --> 00:01:41,000 перетасовывают настроить исполняемый файл для проверки памяти, 37 00:01:41,000 --> 00:01:44,000 так что это может занять немного, чтобы встать и бежать. 38 00:01:44,000 --> 00:01:48,000 Программа будет выполнять нормально, будь то гораздо медленнее, 39 00:01:48,000 --> 00:01:52,000 и когда он закончится, Valgrind будет печатать отчет о своей памяти. 40 00:01:52,000 --> 00:01:58,000 Если все пойдет хорошо, то она будет выглядеть примерно так: 41 00:01:58,000 --> 00:02:01,000 В этом случае,. / Clean_program 42 00:02:01,000 --> 00:02:04,000 это путь к программе, я хочу бежать. 43 00:02:04,000 --> 00:02:06,000 И хотя это не принимать никаких аргументов, 44 00:02:06,000 --> 00:02:09,000 если это так я бы просто трека их на конце команды, как обычно. 45 00:02:09,000 --> 00:02:12,000 Чистый программа просто глупая маленькая программа, которую я создал 46 00:02:12,000 --> 00:02:15,000 что выделяет место для блока целыми в куче, 47 00:02:15,000 --> 00:02:19,000 поставить некоторые значения в них, и освобождает весь блок. 48 00:02:19,000 --> 00:02:23,000 Это то, что вы снимаете для, без ошибок и без утечек. 49 00:02:23,000 --> 00:02:27,000 >> Другим важным показателем является общее количество байт выделяется. 50 00:02:27,000 --> 00:02:32,000 В зависимости от программы, если ассигнования в мегабайтах или выше, 51 00:02:32,000 --> 00:02:34,000 Вы, вероятно, делаете что-то неправильно. 52 00:02:34,000 --> 00:02:37,000 Вы излишне хранения дубликатов? 53 00:02:37,000 --> 00:02:40,000 Используете ли вы кучу для хранения, когда это было бы лучше использовать стек? 54 00:02:40,000 --> 00:02:43,000 Таким образом, ошибки памяти может быть по-настоящему зло. 55 00:02:43,000 --> 00:02:46,000 Более откровенные из них вызывают захватывающие аварии, 56 00:02:46,000 --> 00:02:49,000 но даже тогда она может быть трудно определить 57 00:02:49,000 --> 00:02:51,000 что именно привело к аварии. 58 00:02:51,000 --> 00:02:54,000 Более коварно, программа с памятью ошибки 59 00:02:54,000 --> 00:02:56,000 все еще может скомпилировано 60 00:02:56,000 --> 00:02:58,000 и все еще может казаться правильной работы 61 00:02:58,000 --> 00:03:01,000 потому что вам удалось поймать удачу большую часть времени. 62 00:03:01,000 --> 00:03:04,000 После нескольких "успешных результатов" 63 00:03:04,000 --> 00:03:07,000 Вы могли бы просто думаю, что крах счастливой случайности на компьютере, 64 00:03:07,000 --> 00:03:10,000 но компьютер никогда не ошибается. 65 00:03:10,000 --> 00:03:13,000 >> Запуск Valgrind может помочь вам отследить причину видимых ошибок памяти 66 00:03:13,000 --> 00:03:18,000 а также найти скрывающихся ошибок, которые вы даже не знали еще о. 67 00:03:18,000 --> 00:03:22,000 Каждый раз, когда Valgrind обнаруживает проблему, он выводит информацию о том, что он наблюдал. 68 00:03:22,000 --> 00:03:24,000 Каждый элемент является довольно кратким - 69 00:03:24,000 --> 00:03:27,000 Источник линия нарушителя инструкций, в чем дело, 70 00:03:27,000 --> 00:03:30,000 и немного информации о памяти участвуют - 71 00:03:30,000 --> 00:03:34,000 но часто это достаточно информации, чтобы обратить ваше внимание на правильное место. 72 00:03:34,000 --> 00:03:37,000 Вот пример из Valgrind работает на багги программы 73 00:03:37,000 --> 00:03:40,000 , что делает недействительным чтения динамической памяти. 74 00:03:40,000 --> 00:03:49,000 Мы не видим никаких ошибок или предупреждений в компиляцию. 75 00:03:49,000 --> 00:03:53,000 Ой-ой, ошибка Общий говорит, что есть две ошибки - 76 00:03:53,000 --> 00:03:56,000 два недействительным прочтений размером 4 - байт, то есть. 77 00:03:56,000 --> 00:04:01,000 Оба плохо читает произошла в основной функцией invalid_read.c, 78 00:04:01,000 --> 00:04:04,000 первым на линии 16, а второй на линии 19. 79 00:04:04,000 --> 00:04:06,000 Давайте посмотрим на код. 80 00:04:06,000 --> 00:04:11,000 Похоже, что первый вызов Printf пытается прочитать одну Int прошлом концу нашего блока памяти. 81 00:04:11,000 --> 00:04:13,000 Если мы оглянемся на выходе Valgrind, в 82 00:04:13,000 --> 00:04:16,000 мы видим, что Valgrind сказал нам именно это. 83 00:04:16,000 --> 00:04:19,000 Адрес мы пытаемся читать начинает 0 байт 84 00:04:19,000 --> 00:04:22,000 в конце прошлого блок размером 16 байта - 85 00:04:22,000 --> 00:04:25,000 четыре 32-разрядных целых чисел, что мы выделили. 86 00:04:25,000 --> 00:04:29,000 То есть, адреса мы пытались прочитать начинается в самом конце нашего блока, 87 00:04:29,000 --> 00:04:32,000 так же, как мы видим в наших плохих Printf вызова. 88 00:04:32,000 --> 00:04:36,000 Теперь, недействительными прочтений не кажется, что крупные сделки, 89 00:04:36,000 --> 00:04:39,000 Но если вы используете эти данные для управления потоком ваша программа - 90 00:04:39,000 --> 00:04:42,000 Например, как часть, если заявление или петля - 91 00:04:42,000 --> 00:04:45,000 Затем вещи могут молча идут плохо. 92 00:04:45,000 --> 00:04:47,000 Смотрите, как я могу запустить программу invalid_read 93 00:04:47,000 --> 00:04:50,000 и ничего необычного не происходит. 94 00:04:50,000 --> 00:04:52,000 Страшно, да? 95 00:04:52,000 --> 00:04:56,000 >> Теперь, давайте посмотрим на несколько видов ошибок, которые могут возникнуть в коде, 96 00:04:56,000 --> 00:04:59,000 и мы увидим, как Valgrind обнаруживает их. 97 00:04:59,000 --> 00:05:01,000 Мы только что видели пример invalid_read, 98 00:05:01,000 --> 00:05:04,000 так что теперь давайте посмотрим invalid_write. 99 00:05:04,000 --> 00:05:09,000 Опять же, никаких ошибок или предупреждений в компиляцию. 100 00:05:09,000 --> 00:05:12,000 Хорошо, Valgrind говорит, что есть две ошибки в этой программе - 101 00:05:12,000 --> 00:05:15,000 и invalid_write и invalid_read. 102 00:05:15,000 --> 00:05:18,000 Давайте проверим этот код. 103 00:05:18,000 --> 00:05:21,000 Похоже, что у нас есть экземпляр классического StrLen плюс одна ошибка. 104 00:05:21,000 --> 00:05:24,000 Кодекс не таНос дополнительный байт пространства 105 00:05:24,000 --> 00:05:26,000 для / 0 характера, 106 00:05:26,000 --> 00:05:30,000 поэтому, когда копия ул пошла писать на ssubstrlen "CS50 камни!" 107 00:05:30,000 --> 00:05:33,000 он написал 1 байт в конце прошлого нашего блока. 108 00:05:33,000 --> 00:05:36,000 Invalid_read приходит тогда, когда мы обращаемся к Printf. 109 00:05:36,000 --> 00:05:40,000 Printf заканчивается чтением недействительным памяти при чтении / 0 символов 110 00:05:40,000 --> 00:05:43,000 как это выглядит в конце этой струны это печать. 111 00:05:43,000 --> 00:05:45,000 Но все это не избежал Valgrind. 112 00:05:45,000 --> 00:05:48,000 Мы видим, что он пойман invalid_write как часть ул копию 113 00:05:48,000 --> 00:05:51,000 в строке 11 основных и invalid_read является частью Printf. 114 00:05:51,000 --> 00:05:54,000 Rock On, Valgrind. 115 00:05:54,000 --> 00:05:57,000 Опять же, это может показаться не имеет большого значения. 116 00:05:57,000 --> 00:06:00,000 Мы можем запустить эту программу снова и снова вне Valgrind 117 00:06:00,000 --> 00:06:03,000 и не вижу никакой ошибки симптомов. 118 00:06:03,000 --> 00:06:06,000 >> Тем не менее, давайте посмотрим на небольшое изменение этого, чтобы увидеть 119 00:06:06,000 --> 00:06:09,000 как вещи могут стать очень плохо. 120 00:06:09,000 --> 00:06:14,000 Так что, правда, мы злоупотребление вещей больше, чем просто немного в этом коде. 121 00:06:14,000 --> 00:06:17,000 Мы только выделения места в динамической памяти для двух строк 122 00:06:17,000 --> 00:06:19,000 Длина CS50 пород, 123 00:06:19,000 --> 00:06:22,000 на этот раз, помня / 0 характера. 124 00:06:22,000 --> 00:06:25,000 Но тогда мы бросаем в супер-длинная строка в блоке памяти 125 00:06:25,000 --> 00:06:27,000 что S указывает. 126 00:06:27,000 --> 00:06:30,000 Какой эффект будет, что есть на блок памяти, что T указывает на? 127 00:06:30,000 --> 00:06:34,000 Ну, если T указывает на память, что просто рядом с S, 128 00:06:34,000 --> 00:06:37,000 предстоящие только после этого, 129 00:06:37,000 --> 00:06:39,000 Затем мы могли бы написано над частью T. 130 00:06:39,000 --> 00:06:41,000 Давайте запустим этот код. 131 00:06:41,000 --> 00:06:43,000 Посмотрите, что произошло. 132 00:06:43,000 --> 00:06:47,000 Мы строк хранится в нашей куче блоков как представляется, надлежащим образом не печатается. 133 00:06:47,000 --> 00:06:49,000 Ничто, кажется, в корне ошибочна. 134 00:06:49,000 --> 00:06:52,000 Тем не менее, давайте вернемся в наш код и 135 00:06:52,000 --> 00:06:55,000 закомментируйте строку, где мы копируем CS50 пород 136 00:06:55,000 --> 00:06:59,000 во второй блок памяти, на который указывает тонн. 137 00:06:59,000 --> 00:07:02,000 Теперь, когда мы запустим этот код, мы должны 138 00:07:02,000 --> 00:07:06,000 Только увидеть содержимое первого блока памяти распечатать. 139 00:07:06,000 --> 00:07:09,000 Ух ты, даже если мы этого не сделали, ул копию 140 00:07:09,000 --> 00:07:12,000 любые символы, во втором блоке куча, на которую указывает T, 141 00:07:12,000 --> 00:07:15,000 мы получаем печать. 142 00:07:15,000 --> 00:07:18,000 Действительно, строка, которую мы заправленные в нашем первом блоке 143 00:07:18,000 --> 00:07:21,000 захватили первый блок, а во втором блоке, 144 00:07:21,000 --> 00:07:23,000 делает все кажется нормальным. 145 00:07:23,000 --> 00:07:26,000 Valgrind, однако, говорит нам правду. 146 00:07:26,000 --> 00:07:28,000 Там мы идем. 147 00:07:28,000 --> 00:07:32,000 Все эти недействительным читает и пишет. 148 00:07:32,000 --> 00:07:36,000 >> Давайте посмотрим на пример другого рода ошибки. 149 00:07:36,000 --> 00:07:39,000 Здесь мы делаем что-то довольно неудачно. 150 00:07:39,000 --> 00:07:41,000 Мы захватить пространство для Int в куче, 151 00:07:41,000 --> 00:07:45,000 и мы инициализируем указатель Int - P - указать на это пространство. 152 00:07:45,000 --> 00:07:48,000 Тем не менее, в то время как наш указатель инициализируется, 153 00:07:48,000 --> 00:07:52,000 данные, которые он указывает только имеет все барахло находится в той части кучи. 154 00:07:52,000 --> 00:07:55,000 Поэтому, когда мы загрузить данные в Int я, 155 00:07:55,000 --> 00:07:57,000 Мы технически инициализации я, 156 00:07:57,000 --> 00:08:00,000 но мы делаем это с нежелательных данных. 157 00:08:00,000 --> 00:08:03,000 Вызов утверждают, что это удобный макрос отладки 158 00:08:03,000 --> 00:08:06,000 определены в метко назвал утверждают, библиотеки, 159 00:08:06,000 --> 00:08:09,000 прервет программу, если ее тест условие не выполняется. 160 00:08:09,000 --> 00:08:11,000 То есть, если я не равно 0. 161 00:08:11,000 --> 00:08:14,000 В зависимости от того, что было в куче пространство, на которое указывает р, 162 00:08:14,000 --> 00:08:18,000 эта программа может работать иногда и не в другое время. 163 00:08:18,000 --> 00:08:20,000 Если это работает, мы просто повезло. 164 00:08:20,000 --> 00:08:24,000 Компилятор не будет ловить эту ошибку, но уверен, Valgrind воли. 165 00:08:24,000 --> 00:08:28,000 Там мы видим ошибки, вытекающие из нашего использования, что нежелательные данные. 166 00:08:28,000 --> 00:08:32,000 >> При выделении динамической памяти, но не освобождает его или освободить его, 167 00:08:32,000 --> 00:08:34,000 что называется утечка. 168 00:08:34,000 --> 00:08:37,000 Для небольшой, недолгой программа, которая запускается и тут же выходит, 169 00:08:37,000 --> 00:08:39,000 Утечки довольно безвредны, 170 00:08:39,000 --> 00:08:42,000 но для проектов большего размера и / или долговечность, 171 00:08:42,000 --> 00:08:46,000 даже небольшие утечки могут составить в нечто майора. 172 00:08:46,000 --> 00:08:49,000 Для CS50, мы ожидаем, что вы 173 00:08:49,000 --> 00:08:51,000 заботиться о освобождая все кучи памяти, которые вы выделяете, 174 00:08:51,000 --> 00:08:54,000 так как мы хотим, чтобы вы развивать навыки правильно обращаться с ручной процесс 175 00:08:54,000 --> 00:08:56,000 требуется C. 176 00:08:56,000 --> 00:08:59,000 Чтобы сделать это, ваша программа должна иметь точную 177 00:08:59,000 --> 00:09:03,000 одно-однозначное соответствие между таНос и бесплатные звонки. 178 00:09:03,000 --> 00:09:06,000 К счастью, Valgrind может помочь вам с утечками памяти тоже. 179 00:09:06,000 --> 00:09:09,000 Вот вытекающей программу под названием leak.c, что выделяет 180 00:09:09,000 --> 00:09:13,000 пространства в куче, пишет он, но не освободить его. 181 00:09:13,000 --> 00:09:16,000 Мы скомпилировать его с Марка и запустить его под Valgrind, 182 00:09:16,000 --> 00:09:18,000 и мы видим, что, хотя у нас нет никаких ошибок памяти, 183 00:09:18,000 --> 00:09:20,000 у нас есть одна утечка. 184 00:09:20,000 --> 00:09:23,000 Есть 16 байт определенно потерял, 185 00:09:23,000 --> 00:09:27,000 Это означает, что указатель на эту память не была в рамки, когда программа завершится. 186 00:09:27,000 --> 00:09:30,000 Теперь, Valgrind не дает нам массу информации об утечке, 187 00:09:30,000 --> 00:09:35,000 Но если мы будем следовать этой небольшой записке, что он дает вниз, к нижней части его доклада 188 00:09:35,000 --> 00:09:38,000 перезапустить с - утечка проверить = полный 189 00:09:38,000 --> 00:09:41,000 , чтобы увидеть полную информацию о утечка памяти, 190 00:09:41,000 --> 00:09:44,000 мы получим больше информации. 191 00:09:44,000 --> 00:09:46,000 Теперь, в кучу резюме, 192 00:09:46,000 --> 00:09:50,000 Valgrind говорит нам, где память, что было потеряно было первоначально выделено. 193 00:09:50,000 --> 00:09:52,000 Так же, как мы знаем, от поиска в исходном коде, 194 00:09:52,000 --> 00:09:55,000 Valgrind сообщает нам, что мы утечка памяти 195 00:09:55,000 --> 00:09:58,000 выделяется с призывом таНос на линии 8 из leak.c 196 00:09:58,000 --> 00:10:00,000 В основные функции. 197 00:10:00,000 --> 00:10:02,000 Довольно отличный. 198 00:10:02,000 --> 00:10:04,000 >> Valgrind классифицирует утечек с помощью этих терминов: 199 00:10:04,000 --> 00:10:07,000 Определенно потеряли - это динамической памяти 200 00:10:07,000 --> 00:10:10,000 в котором программа не имеет указателя. 201 00:10:10,000 --> 00:10:14,000 Valgrind знает, что ты когда-то был указатель, но с тех пор потерял его. 202 00:10:14,000 --> 00:10:17,000 Эта память определенно утечка. 203 00:10:17,000 --> 00:10:20,000 Косвенно потеряли - это динамической памяти 204 00:10:20,000 --> 00:10:24,000 к которой только указатели к нему также будут потеряны. 205 00:10:24,000 --> 00:10:27,000 Например, если вы потеряли ваш указатель на первый узел связанного списка, 206 00:10:27,000 --> 00:10:30,000 Затем первый узел сам бы определенно потерял, 207 00:10:30,000 --> 00:10:34,000 в то время как во всех узлах будет косвенно потерял. 208 00:10:34,000 --> 00:10:37,000 Возможно, потерял - это динамической памяти 209 00:10:37,000 --> 00:10:41,000 к которой Valgrind не может быть уверен, есть ли указатель или нет. 210 00:10:41,000 --> 00:10:44,000 Тем не менее достижимым является динамической памяти 211 00:10:44,000 --> 00:10:47,000 к которому программа по-прежнему имеет указатель на выходе, 212 00:10:47,000 --> 00:10:50,000 Обычно это означает, что глобальная переменная указывает на это. 213 00:10:50,000 --> 00:10:53,000 Чтобы проверить эти утечки, вы также должны включить опцию 214 00:10:53,000 --> 00:10:55,000 - Все еще достижима = да 215 00:10:55,000 --> 00:10:58,000 В вашем вызове Valgrind. 216 00:10:58,000 --> 00:11:01,000 >> Эти различные случаи могут потребоваться различные стратегии для очистки их, 217 00:11:01,000 --> 00:11:05,000 но утечки должны быть устранены. 218 00:11:05,000 --> 00:11:08,000 К сожалению, фиксируя утечки может быть трудно сделать, 219 00:11:08,000 --> 00:11:11,000 С неправильные вызовы бесплатно можно взорвать вашу программу. 220 00:11:11,000 --> 00:11:14,000 Например, если мы посмотрим на invalid_free.c, 221 00:11:14,000 --> 00:11:18,000 мы видим пример плохого освобождения памяти. 222 00:11:18,000 --> 00:11:21,000 Каким должно быть ни одного звонка, чтобы освободить весь блок 223 00:11:21,000 --> 00:11:24,000 памяти, на которую указывает int_block, 224 00:11:24,000 --> 00:11:27,000 вместо этого стала попытка освободить каждый Int размера раздела 225 00:11:27,000 --> 00:11:29,000 в памяти отдельно. 226 00:11:29,000 --> 00:11:32,000 Это катастрофически не удастся. 227 00:11:32,000 --> 00:11:34,000 Boom! Какие ошибки. 228 00:11:34,000 --> 00:11:36,000 Это, безусловно, не есть хорошо. 229 00:11:36,000 --> 00:11:39,000 Если вы застряли с такой ошибкой, хотя, и вы не знаете, где искать, 230 00:11:39,000 --> 00:11:41,000 падать обратно на ваш новый лучший друг. 231 00:11:41,000 --> 00:11:44,000 Вы уже догадались - Valgrind. 232 00:11:44,000 --> 00:11:47,000 Valgrind, как всегда, точно знает, в чем дело. 233 00:11:47,000 --> 00:11:50,000 Идентификатор и бесплатно рассчитывает не совпадают. 234 00:11:50,000 --> 00:11:52,000 Мы получили 1 идентификатор и 4 освобождает. 235 00:11:52,000 --> 00:11:55,000 И Valgrind также говорит нам, где первый звонок бесплатный плохо - 236 00:11:55,000 --> 00:11:58,000 тот, который вызвал разрушения - идет от - 237 00:11:58,000 --> 00:12:00,000 строка 16. 238 00:12:00,000 --> 00:12:03,000 Как видите, плохо звонков, чтобы освободить действительно плохи, 239 00:12:03,000 --> 00:12:05,000 поэтому мы рекомендуем позволяя вашей программе утечки 240 00:12:05,000 --> 00:12:08,000 в то время как вы работаете на получение функциональных правильно. 241 00:12:08,000 --> 00:12:12,000 Начните искать утечку только после того, как ваша программа работает правильно, 242 00:12:12,000 --> 00:12:14,000 без каких-либо других ошибок. 243 00:12:14,000 --> 00:12:16,000 >> И это все, что у нас есть для этого видео. 244 00:12:16,000 --> 00:12:18,000 Теперь, что же вы ждете? 245 00:12:18,000 --> 00:12:21,000 Перейти запустить Valgrind ваших программ прямо сейчас. 246 00:12:21,000 --> 00:12:25,000 Меня зовут Нейт Хардисон. Это CS50. [CS50.TV]