第 5 集 · B1 · 7 分钟
C 语言 · 第5集 · 数组怎么建起来
从 Hello World、循环、递归讲到汉诺塔和动态规划,句子短,适合跟读。 开头:存储元素的类型,

中俄字幕
тип хранимого элемента,
存储元素的类型,
выбрать какое- то имя для массива.
为数组选择一个名称。
Оно должно также, как и для переменной, состоять из латинских букв, может быть
它和变量名一样,必须由英文字母组成,也可以包含
цифры, подчёркивания.
数字和下划线。
дальше в квадратных скобках указать
接着在方括号中指定
число количество хранимых элементов, а индексы автоматически будут от нуля до не
元素数量,而索引将自动从零开始,到该数值(含)为止。
включительно этого числа.
(即该数值)
Можем ли мы использовать переменный размер у массива?
数组能否使用可变大小?
и да, и нет, переменной в том смысле, что при входе внутрь функции это число уже
答案既是肯定的,也是否定的:所谓‘变量’是指,在进入函数时,该数值
должно быть известно.
必须已知。
Или на момент объявления вот тут число, которое сюда подставлю, некоторое n, оно
或者在声明时,此处要代入的数字(即某个n)
должно быть уже заранее.
必须事先确定。
известно. И компилятор постарается выделить соответствующее количество памяти под n
编译器会尝试为n个元素分配相应大小的内存。
количество ячеек.
个内存单元。
Индексы элементов будут от нуля до n не включительно.
元素的索引范围是从0到n(不包括n)。
А вот что будет храниться в этих элементах?
那么这些元素中究竟存储着什么?
Ну вот если мы возьмем и просто распечатаем содержимое этого массива, выделив
如果我们直接打印出已分配内存的该数组内容,让我们来看一下。
память, давайте посмотрим.
我们来看一看。
Давайте взглянем.
我们来看一下。
Итак, вот перед вами программа, в которой я создаю массив из n элементов, перебираю
现在您看到的是一个程序:我创建了一个包含n个元素的数组,并遍历它。
все индексы, и для каждого индекса я достаюсь к ячейке а и той и распечатываю ее на
所有索引,对每个索引,我访问数组a的对应单元并将其打印出来。
экран. Пробелами обрамляю, чтобы это дело
用空格包围,使输出更清晰。
выглядело. Что мы смотрим?
我们在看什么?
Что мы видим? мы видим, что там какие- то странные числа, неадекватные, мы таких
我们看到了什么?我们看到一些奇怪、不合理的数字,而我们并未向其中存入过这些数字。
чисел туда не закладывали.
我们从未将这些数字存入其中。
Что это такое?
这是什么?
Это, ну, в каком- то смысле сырая память, то есть что- то, что жило на стэке до
这在某种意义上是原始内存,即数组创建之前栈上已存在的数据。
того, как там создали массив.
也就是说,在该数组被创建之前,栈上原本就存在的内容。
Если компилятор принял решение этот конкретный массив запихать в
如果编译器决定将这个特定数组放入data段(静态数据段),
data, статические данные, ну, вряд ли, конечно, он это сделал, глядя на вот это вот,
当然,它几乎不可能仅凭这段代码就做出这种决定。
потому что в статических данных я бы наблюдал скорее нули.
因为静态数据中我更可能看到的是零。
В общем, шанс на то, что там будут нули, есть, но на самом деле мы не можем
总之,那里出现零是有可能的,但实际上我们不能
положиться на начальное состояние массива, и мы должны его инициализировать.
依赖数组的初始状态,而必须对其进行初始化。
Как это сделать?
如何实现?
Мы можем явным образом задать значения, которые мы хотим.
我们可以显式地指定想要的值。
Да, вот беру шесть элементов у меня, да, n равно шести.
是的,我这里有六个元素,没错,n 等于六。
Я сейчас это n записал в константы этапа компиляции.
我现在把 n 定义为编译期常量。
То есть вот то, что сейчас написано, это макрос препроцессора, который отдельно
也就是说,当前所写的这段内容是一个独立的预处理器宏,
стоящую буковку.
一个单独的字母。
n текстовую, ее нельзя назвать переменной.
这个 n 是文本形式的,不能称其为变量。
Это текстовая подстановка, он будет заменять до компиляции на циферку 6.
这是一个文本替换,编译前会被替换成数字6。
ну в итоге у меня получается что везде где написано n будет на самом деле во время
最终结果是,所有写有n的地方,在实际运行时
компиляции стоять шестерка массив из шести элементов так вот в этом случае понятное
编译时被替换为数字6,即一个包含6个元素的数组。因此在这种情况下,很明确的是
дело я увижу состояние да вот они 1 2 3 4 5 6
我看到的状态就是:1、2、3、4、5、6,
ровно то что я туда заложил но если представить себе что это n большое то это не
这正是我预先设定的内容。但如果设想n是一个很大的数,这种做法
очень удачно. Чаще всего бывает нужно заполнить массив нулями.
就不太合适了。通常,我们更需要将数组全部初始化为零。
Если я этот список инициализации сделаю покороче, вот так, например, 1 2 3, то я
如果我把这个初始化列表写得短一些,比如只写1、2、3,那么我将发现
замечу, что все остальные элементы у меня заполняются автоматически нулями.
其余所有元素会自动被填充为零。
То есть инициализация все- таки произошла, и вот эти дикие числа, они стерты не
也就是说,初始化确实发生了,而那些原本不可预测的数值,不仅在我指定的那三个单元格中
только из тех трех ячеек, про которые я сказал.
被清除了,而是整个数组都被清除了。
Вот я могу свести так к самому простому случаю, да, вот 1 и все остальное, да, и
我可以将其简化为最简单的情况,对,就是1,其余的都这样,对,而且
сказать, а почему тогда не ноль?
然后问:那为什么不是零呢?
Давайте вот так вот сделаем.
我们就这样来做。
И вот это самый простой способ обнулить ваш массив, то есть инициализировать его.
这是清空(即初始化)数组的最简单方法。
нулями. Вот, двигаемся дальше.
用零来初始化。好了,继续往下。
Хотелось бы теперь научиться инициализировать массив не нулями, а чем- то другим.
现在,我们还想学会用非零值来初始化数组。
Для этого придется делать это вручную.
为此,必须手动操作。
Точно такой же перебор индексов понадобится, как это при распечатке.
需要像打印时那样,逐个遍历所有索引。
То есть вот я пробегаю все индексы от нуля до n не включительно,
也就是说,我遍历从0到n(不包括n)的所有索引,
и заполняю, соответственно, в a[ i] какое- то значение.
并相应地将某个值填入a[i]中。
Ну, давайте туда положим i.
好吧,我们在这里放上 i。
В чем преимущество?
优势是什么?
Что у нас теперь вот это вот действие будет работать не только для массива из шести
现在,这个操作不仅适用于我手动编写的包含六个元素的数组,
элементов, который я там записал вручную, а это будет работать даже если я это n
而且即使我修改了这里的 n,它也能正常工作。
изменю. Давайте изменим это n, просто напишем сюда, например, там 10 или 20, и
让我们修改这个 n,比如直接在这里写上 10 或 20,
пожалуйста, у меня сразу вся программа, она перевычислена, перекомпилирована с этим
然后,我的整个程序会立即根据这个新数值重新计算、重新编译。
числом. Это очень удобно, потому что вы, наверное, видели, что у меня n не в одном
这非常方便,因为您可能已经注意到,我的 n 并非只出现在一个地方,
месте, а в целых уже трех местах.
而是出现在整整三个地方。
Если бы я шестерку туда вписал жестко, захардкодил ее, так говорят, захардкодил, то
如果我把数字 6 直接硬编码进去,那么
мне было бы неудобно переписывать это при другом n, а так я легко меняю этот
当 n 变化时,我就得费力地到处修改;而现在,我只需轻松更改这个值即可。
параметр, и у меня такой- то другой массив, другой его параметр размера.
一个参数,而我有另一个数组,它的另一个尺寸参数。
Давайте вернемся к шестерке.
让我们回到第六个元素。
и сделаем другое заполнение этого массива.
并对该数组进行另一种填充。
Кроме заполнения в прямыми индексами, просто индексами, мне хотелось бы добиться
除了用直接索引(即普通索引)填充外,我还希望实现
заполнения в обратку: 5, 4, 3, 2, 1, 0.
反向填充:5、4、3、2、1、0。
В этом случае мне потребуется.
在这种情况下,我需要
следующая формула заполнения в нулевом индексе пятерка, в первом четверка, во втором
以下填充公式:零索引处填5,第一索引处填4,第二索引处填
тройка. И мне очевидно.
3。显然,
что нужно вычитать из пятёрки.
需要从5中减去某个值。
Но если я хочу сделать так, чтобы это работало не только для массива размера 6, а
但如果我希望它不仅适用于大小为6的数组,而是
для любого массива размера n,
对于任意大小为 n 的数组,
то мне нужно придумать формулу.
那么我需要想出一个公式。
И я пытаюсь это сделать.
我正在尝试这样做。
Я говорю: давайте n минус i будет формула заполнения.
我说:让 n 减 i 作为填充的公式。
а нет, не получается.
啊,不行,这样行不通。
6 минус 0- это получается у нас 6.
6 减 0 等于 6。
Значит, мне нужно еще сделать минус 1 оттуда.
所以,我还需要再减去 1。
Вот n минус i минус 1 даст мне то, что нужно.
那么 n 减 i 再减 1 就能得到我需要的结果。
Давайте посмотрим на это дело.
我们来看看这个。
n минус i. Минус один.
n 减 i,再减 1。
Поехали. 5 4 3 2 1 0.
开始。5、4、3、2、1、0。
Вот оно благополучно у нас посчиталось.
好了,我们已成功完成计算。
Так, теперь следующая задачка.
好,接下来是下一道题。
Я хотел бы это же заполнение реализовать иначе.
我想用另一种方式实现相同的填充操作。
смотрите, я могу пробегать этот массив и заполнять его в обратном порядке.
请看,我可以遍历这个数组,并以逆序方式填充它。
Для этого я сейчас вот этот вот n минус i минус 1
为此,我现在将这里的 n 减 i 减 1
поставлю внутрь квадратных скобок, а заполнять буду индексами.
放入方括号内,然后用索引进行填充。
Чем будет отличаться это заполнение?
这种填充方式有什么不同?
На первой итерации тогда, когда i равно нулю, я вычисляю ячейку, в которую я буду
在第一次迭代时,也就是当 i 等于 0 时,我先计算要写入的单元格位置,
класть, и туда кладу ноль.
然后把 0 放进去。
на второй итерации i равняется единице я вычисляю четверку это ячейка в которую я
在第二次迭代中,i 等于 1,我计算四元组,即我要写入的单元格。
буду класть и туда кладу единицу то есть мой массив в этом случае будет заполняться
我会放进去,那里放1,也就是说,我的数组在这种情况下会被填充。
справа налево.
从右到左。
а не слева направо.
而是从右到左。
来源:Тимофей Хирьянов · CC BY 3.0