Представьте себе: 1972 год, клёш в самом разгаре, и где-то в священных залах Bell Labs блестящий ум по имени Деннис Ритчи случайно создаёт один из самых влиятельных языков программирования в истории. Он и подумать не мог, что его «простой» язык системного программирования станет родоначальником бесчисленного количества современных языков программирования и основой всего: от операционной системы вашего смартфона до серверов, обеспечивающих работу ваших любимых сайтов.
Скромные начала: когда B было недостаточно
История языка C начинается не с самого C, а с генеалогического древа, больше похожего на мыльную оперу программирования. Наш рассказ начинается с ALGOL 1960 года, который представил революционную концепцию структурного программирования. Затем появился BCPL (Basic Combined Programming Language) в 1967 году, разработанный Мартином Ричардсом, за ним последовал язык B, созданный Кеном Томпсоном в 1970 году. Но вот где всё становится интереснее: B был бестипным, что звучит круто в теории, но было примерно так же практично, как строить небоскрёб из пластилина. Деннис Ритчи, работая вместе с командой разработчиков Unix в Bell Labs, понял, что им нужно что-то более надёжное, более мощное и, честно говоря, более типизированное.
Рождение легенды: 1972 год
В 1972 году Деннис Ритчи начал разработку C как языка реализации системы для новой операционной системы Unix. Время было выбрано идеально — Bell Labs выводила из эксплуатации амбициозный, но в конечном итоге чрезмерно разрекламированный проект Multics, и команде было нужно что-то компактное, эффективное и способное перестроить Unix с нуля. К 1973 году волшебство произошло. Операционная система Unix была переписана на C, доказав, что этот новый язык был не просто очередным академическим упражнением — он был готов к производству и достаточно мощным для серьёзного системного программирования.
Ключевые особенности, которые сделали C настоящим переломным моментом
Управление памятью низкого уровня: мечта человека, одержимого контролем
C представил нечто революционное: прямое управление памятью через указатели. Хотя это может показаться страшным (и так и есть, если не быть осторожным), это дало программистам беспрецедентный контроль над их системами.
#include <stdio.h>
#include <stdlib.h>
int main() {
// Прямое выделение памяти — здесь главный вы
int *numbers = (int*)malloc(5 * sizeof(int));
if (numbers == NULL) {
printf("Ошибка выделения памяти!\n");
return 1;
}
// Заполняем выделенную память
for (int i = 0; i < 5; i++) {
numbers[i] = i * i;
printf("numbers[%d] = %d\n", i, numbers[i]);
}
// Убирайтесь за собой — у C нет службы горничных
free(numbers);
return 0;
}
Структурированное программирование: приведение порядка в хаос
C способствовал структурированному программированию через функции, циклы и условные операторы, закладывая основу для модульного кода. Это было всё равно что дать программистам настоящий набор инструментов вместо одного молотка.
#include <stdio.h>
// Функция для вычисления факториала — модульная и переиспользуемая
int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
// Функция для проверки, является ли число простым
int is_prime(int num) {
if (num < 2) return 0;
for (int i = 2; i * i <= num; i++) {
if (num % i == 0) return 0;
}
return 1;
}
int main() {
int number = 7;
printf("Факториал числа %d: %d\n", number, factorial(number));
printf("%d является %s\n", number, is_prime(number) ? "простым" : "не простым");
return 0;
}
Стандартизация: от хаоса к порядку
Популярность C привела к тому, что мы могли бы назвать «диким западом» языков программирования — у каждого была своя версия, свои особенности и своё толкование того, каким должен быть C. Это было примерно так же устойчиво, как пытаться согнать кошек с завязанными глазами. На помощь пришёл ANSI (Американский национальный институт стандартов) в 1983 году, создав комитет X3J11 для установления официального стандарта C. К 1989 году появился ANSI C (также известный как C89), который позже был признан ISO как ISO/IEC 9899-1990.
Хронология эволюции
C89/C90: первая стандартизированная версия — представьте её как праздник совершеннолетия C
C99: добавлены встроенные функции, массивы переменной длины и однострочные комментарии (//
стиль)
C11: представлена поддержка многопоточности и другие современные функции
Давайте посмотрим на многопоточность C11 в действии:
#include <stdio.h>
#include <threads.h>
#include <unistd.h>
int worker_function(void *arg) {
int id = *(int*)arg;
for (int i = 0; i < 5; i++) {
printf("Поток %d: работает... %d\n", id, i);
thrd_sleep(&(struct timespec){.tv_sec=1}, NULL);
}
return 0;
}
int main() {
thrd_t threads;
int thread_ids = {1, 2, 3};
// Создаём потоки
for (int i = 0; i < 3; i++) {
if (thrd_create(&threads[i], worker_function, &thread_ids[i]) != thrd_success) {
printf("Не удалось создать поток %d\n", i);
return 1;
}
}
// Ждём завершения всех потоков
for (int i = 0; i < 3; i++) {
thrd_join(threads[i], NULL);
}
printf("Все потоки завершены!\n");
return 0;
}
Эффект ряби: как C повлиял на современное программирование
C не просто создал язык программирования — он создал династию программирования. Влияние C на современные языки программирования похоже на наблюдение мастер-класса по теме «Как запустить тысячу кораблей».
Объектно-ориентированный] C --> Java[Java
Независимый от платформы] C --> CSharp[C#
Ответ Microsoft] C --> Go[Go
Современные системы] C --> Rust[Rust
Безопасные системы памяти] CPP --> PHP[PHP
Веб-разработка] C --> JavaScript[JavaScript
Веб-скриптинг] Java --> Kotlin[Kotlin
Разработка для Android] style C fill:#ff6b6b style CPP fill:#4ecdc4 style Java fill:#45b7d1 style CSharp fill:#96ceb4
C++ (1985): C с классами (и многим другим)
Бьярн Страуструп взял C и подумал: «Что если мы добавим объектно-ориентированное программирование?» Результатом стал C++, который сохранил мощь C, добавив классы, объекты и наследование.
#include <iostream>
#include <string>
class Programmer {
private:
std::string name;
std::string favorite_language;
int coffee_cups_consumed;
public:
Programmer(std::string n, std::string lang)
: name(n), favorite_language(lang), coffee_cups_consumed(0) {}
void drink_coffee() {
coffee_cups_consumed++;
std::cout << name << " пьёт кофе. Всего: "
<< coffee_cups_consumed << " чашек\n";
}
void code() {