Сравнение array C и list Python
В этой статье я продолжу сравнивать языки Python и C. Сравню, чем отличается array (массив) из С от list (список) из Python. Также сравню циклы while из этих двух языков и сделаю ими обход массива и списка.
Все статьи
- 03 Декабрь 2024 Сравнение улучшения кода в Python и C++ (часть 1) Комментарии к посту
- 24 Октябрь 2024 Сравнение hashmap C/C++ с dict Python (часть 3) Комментарии к посту
- 10 Сентябрь 2024 Сравнение hash map С и C++ с dict Python (часть 2) Комментарии к посту
- 22 Август 2024 Сравнение hash map С/C++ с dict Python (часть 1) Комментарии к посту
- 29 Июль 2024 Где применяются Python и C/C++ (часть 2) Комментарии к посту
- 11 Июль 2024 Где применяются Python и C/C++ (часть 1) Комментарии к посту
- 20 Июнь 2024 Сравнение array C и list Python Комментарии к посту
- 12 Март 2024 Сравнение интерпретатора Python и компилятора C Комментарии к посту
- 24 Декабрь 2023 Сравнение типизации Python и C Комментарии к посту
Дополнительные материалы
Для скачивания материалов необходимо войти или зарегистрироваться
Файлы также можно получить в Telegram-боте по коду: 219962
Реклама
Всем доброго дня! У меня был небольшой перерыв в написании статей, так как навалилось много дел. Заканчивал стажировку, параллельно занимаясь поиском работы и дописывая свой сайт-блог, весна и хорошая погода - всё это сделало своё дело. Сейчас я возобновляю свою работу над статьями и хочу выпускать их примерно раз в две недели. Также в ближайшем будущем я хочу запустить свой сайт-блог и дублировать свои статьи там.
Итак, продолжим. В предыдущей статье "Сравнение интерпретатора Python и компилятора C" я затрагивал вскользь темы array и list, а также циклов while. Поэтому в данной статье я сравню array (массив) из языка C и list (список) из языка Python, обход которых я сделаю на цикле while. Давненько я не писал статьи и даже очень соскучился по этому процессу. Так что приступаю к продолжению этой интересной и захватывающей рубрики.
Сравнение array и list
Для начала начнём со знакомого многим питонистам list (список) из языка Python. Список - это неограниченная по размеру упорядоченная коллекция произвольных объектов. Для начала давайте создадим список в Python.
list_example = [1, 2, 3, 4, 5]
print(list_example)
Я создал список и назвал его list_example
так как он не будет привязан к конкретным данным или назначению. Давайте запустим в IDE и посмотрим что он выдаст в эмуляторе terminal на моём Debian. Я перешёл с Kubuntu на Debian 12, но различий между двумя этими системами для демонстрации кода не будет. При нажатии на кнопку run python file в IDE VS Code видим следующий вывод.
В terminal
[1, 2, 3, 4, 5]
Функция print
из строки print(list_example)
печатает нам содержимое списка [1, 2, 3, 4, 5]
, в котором находятся числа. Прежде чем перейти к написанию кода с аналогичным функционалом на C давайте разберёмся что такое array.
Массив, или array, в языке C - это структура данных, которая позволяет хранить последовательности элементов одного типа. Это определение уже намекает нам на то, что в массиве на языке C не может быть разных типов данных. Чуть позже я объясню почему, а сейчас создадим массив, похожий на список, что я писал выше на Python.
#include <stdio.h>
#include <string.h>
int main(){
int array_example[] = {1, 2, 3, 4, 5};
char buffer[100];
sprintf(
buffer,
"[%d, %d, %d, %d, %d]",
array_example[0],
array_example[1],
array_example[2],
array_example[3],
array_example[4]
);
printf("%s\n", buffer);
return 0;
}
Получился очень большой и странный код для взгляда питониста. Давайте скомпилируем и запустим его, далее разберём новые для вас строки кода.
В terminal
$ gcc ./array_c.c -o array_c
$ ./array_c
[1, 2, 3, 4, 5]
После компиляции и запуска данного кода мы видим тот же результат, что и в коде на Python в эмуляторе terminal [1, 2, 3, 4, 5]
. Давайте разберёмся, что мы вообще такое написали на С. Строка #include <string.h>
включает заголовочный файл для компилятора, который нужен для работы со строками. В нашем случае заголовочный файл нужен, чтобы использовать функцию sprintf
для формирования строки. Из предыдущей статьи вы помните, что строка int array_axample[] = {1, 2, 3, 4, 5};
создаст массив целых чисел. Прошу обратить внимание, что вы не можете класть туда любые типы данных как это делается в языке Python! Компилятору явно указывается, что в массиве у нас тип данных int
- целое число. Давайте взглянем на примере, что будет если положить в массив int
тип данных char
или символ. При форматировании укажем %c
спецификатор формата для char
. Для этого давайте слегка поправим код.
#include <stdio.h>
#include <string.h>
int main(){
int array_example[] = {1, 2, 3, 4, 'r'};
char buffer[100];
sprintf(
buffer,
"[%d, %d, %d, %d, %c]",
array_example[0],
array_example[1],
array_example[2],
array_example[3],
array_example[4]
);
printf("%s\n", buffer);
printf("размер int %d\n", sizeof(int));
printf("размер char %d\n", sizeof(char));
printf("размер char в массиве int %d\n", sizeof(array_axample[4]));
return 0;
}
Ещё раз компилируем и запустим код в terminal.
$ gcc ./array_c.c -o array_c
$ ./array_c
[1, 2, 3, 4, r]
размер int 4
размер char 1
размер char в массиве int 4
Запустив код, мы видим что в [1, 2, 3, 4, r]
есть символ char
, который добавился каким-то образом в массив int
. Как же это произошло если в C строгая типизация? Дело в том что массиве char
символ будет храниться как int
. Это означает что и занимать место он будет так же как и int
тип. Из нашего примера мы видим что размер char
символа у меня на пк 1 байт, а размер int
4 байт. Так же мы видим что размер char
в массиве int
типа 4 байт, а не 1. Это означает что мы в 4 раза будем тратить больше места на один символ. Это довольно не экономно с точки зрения ресурсов пк. Поэтому я вам не рекомендую этот подход. Перепишем код обратно на тот что был и снова перекомпилируем. Просто повторите предыдущий код, что был до изменений, и заново запустите команду компиляции. Всё должно работать как раньше.
Давайте разберём оставшиеся строки кода. Строка char buffer[100];
создаёт массив char
из 99 символов и 1 символ конца строки \0
. Это нужно для хранения символов строки char
. Цифра 100 на самом деле не количество символов, а размер массива в байтах. Размер 100 байт в нашем случае очень избыточен, поэтому рассчитаем точный размер 5 * 1 + 1 = 6. Количество символов умножаем на вес одного в байтах и прибавляем 1 байт для символа конца строки. Квадратные скобки и запятые в нашем буфере не хранятся. Таким образом мы экономим 94 байта на компьютере. Программы на C требуют, чтобы вы следили за размером, который занимают ваши данные. Немного непривычно после языка Python, не так ли? На самом деле в Python тоже нужно думать о размере данных, но из-за того что он динамический и много прощает, следить за этим гораздо сложнее. Далее в функцию sprintf
мы кладём наш буфер для хранения char
, форматируем строку "[%d, %d, %d, %d, %d]"
и для каждого спецификатора формата %d
берём каждое число по индексу из массива. Далее функция sprintf
заполнила наш buffer
данными char
в результате чего получился массив символов, последовательность которых образует строку. Ну и в конце выводим нашу строку в terminal (эмулятор терминала). В Python функция print
сама выводила нам содержимое списка, а в C нам пришлось для этого форматировать строку. Возможности языка C и возможности Python разные и иногда в C просто нет простого решения. Давайте попробуем на Python написать близкий внешне вариант. Для этого перепишем наш Python код. Напишу два варианта: более старый и более новый и быстрый.
from typing import List
list_example: List[int] = [1, 2, 3, 4, 5]
str_list = '[%d, %d, %d, %d, %d]' % (list_example[0], list_example[1], \
list_example[2], list_example[3], list_example[4])
print(str_list)
Первый вариант использует оператор %, который более родственный языку C. Обратите внимание: я использую аннотацию типов List[int]
, показывая, что у нас должен быть тип данных list
с числами int
типа внутри. Это работает на уровне соглашения между разработчиками и не влияет на саму работу кода. И если туда добавить другой тип данных естественно ничего не сломается. Результатом в переменной str_list
будет отформатированная оператором %
строка [1, 2, 3, 4, 5]
, которую мы потом выводим в terminal с помощью функции print
.
from typing import List
list_example: List[int] = [1, 2, 3, 4, 5]
str_list = f'[{list_example[0]}, {list_example[1]}, {list_example[2]}, '\
f'{list_example[3]}, {list_example[4]}]'
print(str_list)
Второй вариант использует f
строки, которые работают быстрее оператора %
и использование которых имеют более короткий и удобный синтаксис. Результат будет тот же самый что и в примере с оператором %
. Если ваша Python версия поддерживает f
строки, рекомендую использовать именно их.
Далее я хочу пройти по списку и массиву с помощью цикла while и вывести каждое из чисел в списке. Довольно просто и банально, но моя цель - показать отличия Python от C, а не усложнять сам код. После того как я покажу вам основы, буду ставить задачи посложнее и писать более сложный код. Для начала напишем код на более простом языке Python.
from typing import List
list_example: List[int] = [1, 2, 3, 4, 5]
i = 0
while i <= list_example.index(list_example[-1]):
print(list_example[i])
i += 1
У меня получился довольно простой и элегантный вариант в версии Python. Когда я пишу на Python, я не пытаюсь усложнить задачу, а наоборот - стараюсь пользоваться его средствами для упрощения себе работы. В данном случае я использую функцию index
для удобства. У нас есть переменная i
для индекса, по которому нужно брать элементы из списка list_example
. Строка while i <= list_example.index(list_example[-1]):
говорит нам о том, что мы не выйдем из цикла while
пока значение переменной i
не будет равно последнему индексу списка. Далее выводим данные на экран в строке print(list_example[i])
. После чего прибавляем на единицу переменную индекса i
. Давайте запустим наш код в VS Code и взглянем на результат.
1
2
3
4
5
Видим, что наш код отработал как нужно - вывел все числа списка. После чего цикл while
завершился. Пришло время написать код с похожим функционалом на C.
#include <stdio.h>
int main(){
int array_example[] = {1, 2, 3, 4, 5};
int i = 0;
int i_end = sizeof(array_example) / sizeof(array_example[0]) - 1;
while (i <= i_end){
printf("%d\n", array_example[i]);
++i;
}
return 0;
}
Давайте взглянем на наш C код и на отличия в работе его логики. У нас имеется переменная для индекса int i = 0;
. Строка получения длины списка вам уже известна из предыдущей главы и единственное здесь отличие - мы отнимаем единицу. Это нужно для того, чтобы получить последний индекс списка. Строка int i_end = sizeof(array_example) / sizeof(array_example[0]) - 1;
успешно получит число 4. Далее идёт условие работы цикла: while
i <= i_end
работает аналогично примеру из Python. Строка printf("%d\n", array_example[i]);
выведет число из списка в terminal. В конце ++i;
мы прибавляем единицу к переменной индекса i
. Вот тут хочу остановиться поподробнее. Дело в том, что есть два варианта: ++i
и i++
. Первый вариант ++i
это префиксная форма и она сначала увеличивает значение, а потом возвращает его. Второй вид i++
это суффиксная форма, которая сначала вернёт текущее значение, а потом увеличит его. Будьте внимательны при использовании, у них будут разные результаты и может быть ошибка в логике программы.
Теперь давайте скомпилируем и запустим программу.
$ gcc array_c.c -o array_c
$ ./array_c
1
2
3
4
5
Всё хорошо, программа отработала как нужно и вывела нам каждое число из массива.
Бонус
Как известно, в Python, увы, нет встроенного в язык типа данных array, но зато есть встроенный модуль array, который обеспечивает схожую функциональность. Давайте посмотрим как он работает на примере.
arr = array('i', [0]*5)
arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[3] = 4
arr[4] = 5
print(arr)
print(arr[0])
Первая строка arr = array('i', [0]*5)
создаёт аналог массива (по функциональности) и заполняет его пятью элементами нулями, i
указывает, что это массив с данными int
типа. Далее, как и в обычном list
, мы присваиваем значение по индексу arr[0] = 1
и т.д. Давайте запустим код и убедимся, что всё работает.
array('i', [1, 2, 3, 4, 5])
1
Мы видим, что у нас создался объект array, который заполнился данными. Далее мы выводим элемент цифру 1 по индексу 0. Согласитесь, не очень удобно заполнять нулями, а потом присваивать по каждому индексу значения? Мы можем сразу заполнить массив нужными значениями, уменьшив этим количество строк кода. Заодно давайте добавим новый элемент в массив, а так же добавим другой тип данных.
arr = array('i', [1, 2, 3, 4, 5])
print(arr)
arr.append(6)
print(arr)
arr.append('number')
print(arr)
Запустим наш новый код в terminal.
array('i', [1, 2, 3, 4, 5])
array('i', [1, 2, 3, 4, 5, 6])
Traceback (most recent call last):
File "/home/arduinum628/Документы/Helper_for_programmer/Articles/Code/Код из статьи array C list Python/list_python.py", line 43, in <module>
arr.append('number')
TypeError: 'str' object cannot be interpreted as an integer
При запуске нашего кода мы видим, что у нас создался такой же массив как и в примере выше. Далее мы добавили новый элемент с помощью метода append
. Потому что размер array динамический в данной библиотеке. Обратите внимание что в языке C массив имеет фиксированный размер и выйти за его размеры у вас не получиться. Таким образом мы ограничиваем ресурсы нашего ПК в языке C. В Array мы получили фиксированную типизацию для массива указав i
(int
). Поэтому мы получили ошибку при добавлении в него типа данных str
строки number
. Также возможности встроенной библиотеки array позволяют удалять элемент по индексу del arr[0]
.
Заключение
- Узнали, чем отличается array (массив) в C от list (список) в Python;
- Научились работать с
bufer
для хранения символов типа данныхchar
для строк; - Посмотрели как работает цикл
while
на обоих языках; - Затронули тему аннотации типов в Python;
- Бонусом посмотрели встроенный в Python модуль array, который может создавать подобие массива;
Arduinum628
21 июня 2024 г. 14:00
Статья была отредактирована для исправления некоторых неточностей.
Войдите чтобы добавить комментарий.