Skip to content

Latest commit

 

History

History
361 lines (287 loc) · 18.1 KB

File metadata and controls

361 lines (287 loc) · 18.1 KB

Создаем простой калькулятор

Что делает

На основании аргументов запуска, выполняет арифметические операции и выводит результат.

Пример выполнения:

  ./calc 10 x 5 
  result: 10 x 5 = 50

  ./calc 10 / 5
  result: 10 / 5 = 2
  
  ./calc 10 + 5
  result: 10 + 5 = 15

  ./calc 10 - 5
  result: 10 - 5 = 5

Что внутри

  • Процесс обработки аргументов, которые передаем для запуска.
  • Возвращение кода выхода в зависимости от успеха / неудачи.
  • Арифметические операции с аргументами.
  • Вывод результата.

Исходный код

calc.c

Ссылка

Пояснения

Подключение необходимых заголовков

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
  • stdio.h - необходимый для работы стандартного ввода/вывода, в нашем случае для функции printf(), которая нужна для вывода текста в терминале.
  • stdlib.h - основной заголовок стандартной библиотеки языка C, в нашем случае используем от туда функцию atoi() для перевода строк в целые числа.
  • string.h - заголовок для работы со строками, в нашем случае используем функцию strcmp() для сравнения двух строк.

Передача аргументов

Когда мы вызываем программу то ей передаются следующие аргументы:

  • [0] -> имя_программы
  • [1] -> аргумент #1 ?
  • [2] -> аргумент #2 ?
  • [n] -> аргумент #n ...

Аргумент #0 обязателен, он передается автоматически. Если мы вызываем программу так:

./calc 10

то набор аргументов будет таким:

  • [0] -> ./calc
  • [1] -> 10

итого всего 2 аргумента.

Передача аргументов происходит в функции main - входная точка в программу. Где происходит начальный запуск.

int main(int argc, char* argv[])

Аргументы функции main:

  • int argc - количество переданных аргументов (1, 2, 3 и так далее)
  • char* argv[] - значения аргументов, в виде списка строк.

Зная количество аргументов мы можем находить в списке нужный аргумент, не выходя за пределы.

Для программы нужно 4 аргумента, которые будут передаваться при запуске так:

./calc 10 x 5
  • [0] -> обязательный, передается по умолчанию (calc)
  • [1] -> левая цифра (10)
  • [2] -> символ операции ( x, :, +, -)
  • [3] -> правая цифра (5)

Поэтому нам нужно проверить, что аргументов именно 4, не больше ни меньше:

if(argc != 4)
{
    printf("arguments fail, example:\n$ ./calc.out 10 * 10\n");
    return 1;
}

Если количество аргументов не равно 4, то мы пишем понятную ошибку для пользователя с помощью функции printf() , это сообщение поможет нам понять как правильно вызывать функцию с примером. Так как это ошибка при выполнении программы, мы делаем return 1 для выхода из программы, с кодом 1, который означает ошибку выполнения.

Когда мы запускаем программу нужно понимать как она завершилась, успешно или неудачно. Принято возвращать коды из функции main:

  • при успехе делается return 0
  • при неудаче делается return 1

Отслеживание кода возврата в терминале Отследить последний код возврата программы в терминале можно с помощью команды:

echo $?

Эта команда покажет код выполнения последней вызванной программы.

Вычисление результата

Если с количеством аргументов все хорошо, то дальше сохраняем аргументы в переменные:

int result = -1;
int left = atoi(argv[1]);
int right = atoi(argv[3]);
char* operator = argv[2];

Пояснения:

  • int result - переменная для хранения результата вычислений.
  • int left - левая цифра, преобразуем строку в int с помощью функции atoi().
  • int right - правая цифра.
  • char* operator - арифметический оператор, который располагается между цифрами, ожидается что он будет иметь одно из значений: x, :, +, -.

Далее проверяем что за оператор был введен, с помощью функции сравнения строк strcmp(), и, если найдено совпадение, делаем соответствующую операцию:

  • x - умножение.
  • : - деление.
  • + - сложение.
  • - - вычитание.

Пример:

if(strcmp(operator, "x") == 0)
{
    result = left * right;
}

Если совпадения не найдено, то результат остается со значением -1 и выводим ошибку о неизвестном операторе и делаем выход из программы с ошибкой с помощью return 1:

if(result == -1)
{
    printf("arguments fail, invalid operator '%s' , expected: [ x , : , +, - ]\n", operator);
    return 1;
}

В конце программы выводим результат вычислений и делаем успешный выход из программы return 0:

printf("result: %d %s %d = %d\n", left, operator, right, result);
return 0;

2. Обработка аргументов запуска программы

Программа

hello_args

Что делает

На основании аргументов запуска, выводим определенное сообщение.

Пример выполнения

./hello_args -h intfloatbool
hello intfloatbool !!!

./hello_args -g intfloatbool
greetings intfloatbool !!!

./hello_args -m intfloatbool
mashala intfloatbool !!! 

Что внутри

  • проверка аргументов, с помощью функции getopt()
  • вывод сообщения, в зависимости от аргументов.

Исходный код

hello_args.c

Ссылка

Пояснения

Подключение необходимых заголовков

#include <stdio.h>
#include <unistd.h>
  • stdio.h - уже встречали, для вывода, функция printf().
  • unistd.h - предоставляет функции для работы с ОС, в нашем случае для функции getopt()

Способ передачи аргументов

В примере с калькулятором мы сравнивали аргументы, как обычные строки, с помощью функции strcmp(). Это не совсем удобно, тем-более что существует стандарт передачи аргументов, с помощью дефиса ``-arg```.

Обычно, программы в Unix, принимают аргументы в таком формате и на то есть причины. Существует набор функций стандартной библиотеки, которые упрощают нам обработку аргументов, переданных таким образом.

Одна из таких функций - getopt().

Функция принимает три параметра:

  1. количесвто аргументов (int argc)
  2. массив значений (char* argv[])
  3. шаблон возможных аргументов, в виде строки (abc)

Ключевое здесь - третий параметр функции, шаблон, по которому происходит поиск аргументов.

Если поиск по шаблону удался (есть такой аргумент) то мы можем обработать его.

То есть эта функция проводит поиск наличия аргументов за нас, нам лишь остается обработать случай, для каждого возможного аргумента.

Обработка аргументов и вывод сообщения

while( ( opt = getopt( argc, argv, "hgm"  ) ) != -1 )

здесь происходит вся магия. Мы передали шаблон в виде строки "hgm", который можно описать так: проверить наличие аргумента -h или -g или -m.

Нетрудно заметить, что шаблон состоит как раз из этих трех символов, которые нас интересуют, в качестве аргументов. То есть наша программа работает с одним из аргументов: -h , -g или -m.

В зависимости от аргумента мы выводим сообщение и успешный код завершения return 0 , а в случае несовпадения (default) пишем ошибку и возвращаем код выполнения с неудачным результатом return 1 :

case 'h':
{
    printf("hello %s !!!\n", name);
    return 0;
}
case 'g':
{
    printf("greetings %s !!!\n", name);
    return 0;
}
case 'm':
{
    printf("mashala %s !!!\n", name);
    return 0;
}
default:
{       
    printf("invalid argument! Consider using: -h, -g, -m.\n");
    return 1;
}

3. Обработка ввода в программу от пользователя

Программа

handle_user_input

Что делает

Принимает текст от пользователя и выводит определенный ответ, на основании входящего текста.

Пример выполнения

./handle_user_input
hey # наш ввод, потом жмем enter 
your message: hey
number representation: 104 101 121

yeh # наш ввод, потом жмем enter
your message: yeh
number representation: 121 101 104

intfloatbool # наш ввод, потом жмем enter
Wow! You know the secret word! Congrats! 

Что внутри

  • считывание текста пользователя в программе.
  • обработка специального слова intfloatbool.
  • показ числового представления каждого символа.
  • использование функции exit().

Исходный код

handle_user_input.c

Ссылка

Пояснения

Подключение необходимых заголовков

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
  • stdio.h - обработка ввода/вывода , функциями fgets() для ввода и printf() для вывода.
  • string.h - работа со строками, сравнение функцией strcmp() и поиск количества символов функцией strcspn().
  • stdlib.h - стандартная библиотека, выход из программы функцией exit().

Определение макроса размера массива

#define BUFF_SIZE 256

Определив макрос BUFF_SIZE, мы можем его подставлять в коде, где нужно. Чтобы часто не писать магическое число 256, будем писать просто BUFF_SIZE. Компилятор подставит вместо BUFF_SIZE значение 256 сам. Это довольно распространенная практика в системном программировании.

В нашем случае макрос BUFF_SIZE означает размер массива символов, в который будет записываться пользовательский ввод.

А что будет если сделать ввод в программу больше чем 256 символов ?

Ничего, просто ограничение на количество. Программа обрежет ваш ввод до 256 символов.

В системном программировании на C, мы должны знать пределы заранее!

Сигнатура функции main()

int main(void)

Обратим внимание, что у нас тут нет параметров argc и argv, так как они нам не нужны (программа работает без аргументов). Вместо этого просто указываем void, который означает что функция работает без параметров.

Объявляем переменные строк

char secret_word[] = "intfloatbool";
char user_input[BUFF_SIZE];
  • secret_word это наше секретное слово, мой никнейм - intfloatbool, введя которое, вы получите особое сообщение от программы.
  • user_input это буфер, куда будет помещаться пользовательский ввод. Что пользователь ввел до 256 символов (макрос BUFF_SIZE), то и попадет в этот буфер.

Цикл чтения ввода пользователя

while( fgets( user_input, sizeof(user_input), stdin ) != NULL )

Здесь мы вызываем функцию fgets(), которая считывает ввод пользователя, до нажатия enter . Цикл while здесь для того, чтобы после первого ввода программа продолжала работать и принимать ввод. Пока мы явно ее не завершим, нажатием ctrl + D или ctrl + C.

Удаляем лишнее из строки ввода

user_input[ strcspn( user_input , "\n") ] = '\0';

Когда пользовательский ввод помещается в строку user_input, в ней дополнительно будет символ переноса строки "\n" (что очевидно, мы ведь нажали enter !) , чтобы он не мешал нашим дальнейшим расчетам, заменяем его на символ конца строки - '\0'.

Проверка на секретное слово

if( strcmp(user_input, secret_word) == 0 )
{
    printf("Wow! You know the secret word! Congrats! \n");
    exit(0);
}

Сравниваем строку ввода со строкой secret_word с помощью функции strcmp(). Если совпадение найдено, то пишем сообщение с поздравлением и выходим из программы с помощью функции exit(), передав ей 0 как успешное завершение!

Вывод числовых представлений каждго символа

for(int i = 0; i < sizeof(user_input); i++)
{
    char c = user_input[i];
    if(c == '\0')
    {
        break;
    }
    printf("%d ", user_input[i]);
}

Строка состоит из символов, поэтому строка это массив символов char str[]. Символ это всего-лишь число от -128 до 127 что дает нам в сумме 255 уникальных символов.

С помощью цикла for выводим цифровое значение кажого символа из ввода пользователя, проверяя символ на конец строки '\0', чтобы успешно завершить цикл (ведь строка закончилась) оператором break.

Следующая статья