Аргументы командной строки в разных языках программирования

C и C++

int  main(int argc, char* argv[])
{
    argc;    // количество аргументов + 1
    argv[0]; // имя программы
    argv[1]; // первый аргумент
    argv[2]; // второй аргумент
    // ...
}

Bash

#!/bin/bash

# $# — количество аргументов
#
# $* — аргументы одной строкой
# $@ — список аргументов

echo $0    # имя скрипта
echo $1    # первый аргумент
echo $2    # второй аргумент
# ...
echo $9    # девятый аргумент
echo $(10) # десятый аргумент
echo $(11) # одиннадцатый аргумент
# ...

Cmd.exe

echo %0 REM имя срипта
echo %1 REM первый аргумент
echo %2 REM первый аргумент
REM ...
echo %9 REM девятый аргумент

Python

import sys

sys.argv      # список аргументов
len(sys.argv) # количество аргументов + 1
sys.argv[0]   # имя скрипта
sys.argv[1]   # первый аргумент
sys.argv[2]   # второй аргумент
# ...

Java

public class CommandLine {
    static public void main(String args[]) {
        args.length; // количество аргументов
        args[0];     // первый аргумент
        args[1];     // второй аргумент
        // ...
    }
}

C Sharp

class MainClass
{
    static int Main(string[] args)
    {
        args.Length; // количество аргументов
        args[0];     // первый аргумент
        args[1];     // второй аргумент
        // ...
    }
}
Реклама

Обработка ошибок при выделение памяти с помощью new в C++

Есть два способа определить выделена ли память оператором new.

Способ 1. Обработка исключения

Если память не выделена, то бросается исключение std::bad_alloc.

Пример обработки исключения.

// bad_alloc example
#include <iostream>     // std::cout
#include <new>          // std::bad_alloc

int main () {
  try
  {
    int* myarray= new int[10000];
  }
  catch (std::bad_alloc& ba)
  {
    std::cerr << ba.what() << std::endl;
  }
  return 0;
}

Способ 2. Проверка указателя

При вызове new в качестве аргумента можно использовать константу std::nothrow. Тогда, исключение std::bad_alloc не испускается, а вместо него возвращается нулевой указатель.

Пример обработки нулевого указателя.

// nothrow example
#include <iostream>     // std::cout
#include <new>          // std::nothrow

int main () {
  std::cout << "Attempting to allocate 1 MiB... ";
  char* p = new (std::nothrow) char [1048576];

  if (!p) {
    std::cout << "Failed!\n";
  }
  else {
    std::cout << "Succeeded!\n";
    delete[] p;
  }

  return 0;
}

Ссылки

  1. Описание std::bad_alloc.
  2. Описание std::nothrow.

Правила кодирования в Qt

640px-qt_logo_2016-svg

При использовании Qt я предпочитаю использовать стиль кодирования, который используется в библиотеке, чтобы добиться единообразия кода.

Директивы препроцессора

Символ решетки всегда в начале строки, а имя директивы с отступом.

Пример из файла Qt5.9.0/5.9/gcc_64/include/QtCore/qobjectdefs.h.

# if defined(QT_NO_KEYWORDS)
#  define QT_NO_EMIT
# else
#   ifndef QT_NO_SIGNALS_SLOTS_KEYWORDS
#     define slots Q_SLOTS
#     define signals Q_SIGNALS
#   endif
# endif

Ссылки и указатели

Операторы & и * в обозначении типа смещены вправо и примыкают к имени.

Пример из файла Qt5.9.0/5.9/gcc_64/include/QtCore/qbytearray.h.

class Q_CORE_EXPORT QByteArray
{
    <...>
    QByteArray &prepend(char c);
    QByteArray &prepend(int count, char c);
    QByteArray &prepend(const char *s);
    QByteArray &prepend(const char *s, int len);
    QByteArray &prepend(const QByteArray &a);
    QByteArray &append(char c);
    QByteArray &append(int count, char c);
    QByteArray &append(const char *s);
    QByteArray &append(const char *s, int len);
    QByteArray &append(const QByteArray &a);
    <...>
}

Приватные поля

В имени приватного поля используется префикс m_.

Пример из файла Qt5.9.0/5.9/gcc_64/include/QtCore/qstring.h.

class QLatin1String
{
public:
    <...>
private:
    int m_size;
    const char *m_data;
};

Ссылки

О других правилах кодирования можно узнать по следующим ссылкам.

C++. Частичная специализация шаблонного класса

Может пригодится, когда нужно для какого-то типа по своему реализовать метод или методы шаблонного класса не специализируя и не переписывая весь класс.

Пример.

Имеется следующий шаблонный класс в файле value.h.

template<typename T>
class Value {
public:
    Value(const T &val): m_value(val) {}
    T value() const { return m_value; }
private:
    T m_value;
};

Реализуем метод value для типа int в файле value.cpp.

template<>
int Value<int>::value() const
{
    return m_value + 1;
}

ARM и специфичный для компиляторов код

Любой разработчик компиляторов расширяет язык программирования и расширяет по своему.

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

Ниже приведен пример кода для некоторых ARM-компиляторов.


#if defined (__CC_ARM)
    /* Код для компилятора Keil */
#elif defined (__ICCARM__)
    /* Код для компилятора IAR */
#elif defined ( __GNUC__ )
    /* Код для компилятора GNU */
#elif defined (__TASKING__)
    /* Код для компилятора TASKING */
#else
    /* Неизвестный компилятор */
    #error Unknown compiler
#endif

Библиотека на языке C для конвертации строк из кодировки UTF-8 в CP1251

Написал легкую библиотеку utf8_to_cp1251 для конвертации из UTF-8 в CP1251 для применения в программах для микроконтроллеров. Охватывает языки от С89 до C++.

Библиотека и ее описание опубликованы на Github.

Статические переменные вместо глобальных на Си/Си++

Использование глобальных переменных считается дурным тоном, потому что создает много проблем. Избавиться от них можно с помощью ключевого слова static, которое создает статические переменные.

Есть простой способ избавиться от большинства глобальных переменных.

void fun(void) {
    static int A = 10;
    ...
}

Переменная A располагается в глобальной области памяти, но имеет локальную область видимости (в пределах функции fun). То есть переменная сохраняет свое значение между вызовами функции. Начальное значение переменной равно 10.

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

static int A = 10;

void setA(const int value) {
    A = value;
}

int getA(void) {
    return A;
}

Область видимости переменной A — только этот файл. Никаким способом к ней нельзя получить доступ из других файлов. Функции setA и getA позволяют получить и установить значение переменной.

Приоритеты прерываний в Cortex-M и их настройка в STM32Cube

STM32Cube — это набор библиотек фирмы STM для своих микроконтроллеров (STM32). Эти библиотеки пришли на смену библиотекам SPL.

Микроконтроллеры STM32 сделаны на базе ядра ARM Cortex-M. Это ядро служит основой микроконтроллеров и у других производителей.

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

Приоритет прерывания зависит от 3 факторов:

Читать далее Приоритеты прерываний в Cortex-M и их настройка в STM32Cube

Форматная строка для представления даты и времени в разных системах

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

В таблице ниже приведена форматная строка для получения даты и времени в следующем виде:

2016.06.14 23:10:06

(14 июня 2016 год, 23 часа 10 минут 6 секунд. Год из 4 цифр. Месяц, день месяца, часы, минуты и секунды из двух цифр, дополняются нулями. Часы в 24 формате).

Система шаблонов Форматная строка
date (Linux) %Y.%m.%d %H:%M:%S
C/C++ %Y.%m.%d %H:%M:%S
Python %Y.%m.%d %H:%M:%S
SQLite %Y.%m.%d %H:%M:%S
ISO 8601 YYYY.MM.DD hh:mm:ss
Qt yyyy.MM.dd hh:mm:ss
C# yyyy.MM.dd HH:mm:ss

Переполнение при отсчете времени в STM32Cube

Ранее я писал об отсчете времени в STM32Cube.

STM32Cube считает время в миллисекундах. Время хранится в беззнаковой целочисленной 32-битной переменной:

static __IO uint32_t uwTick;

Это означает, что через 49 суток 17 часов 2 минуты 47 секунд и 295 миллисекунд произойдет переполнение этой переменной.

Хорошо, если устройство не может так долго работать, например, гарантировано сядет батарейка. Но, что если устройство должно работать непрерывно месяцами?

Время используется библиотекой в операциях ввода/вывода для организации таймаутов. Кроме того, библиотека предоставляет функцию задержки HAL_Delay().

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

Посмотрим, на код, отвечающий за обработку времени:

файл stm32***_it.c:

void SysTick_Handler(void)
{
  HAL_IncTick();
}

файл stm32***_hal.c:

static __IO uint32_t uwTick;

__weak void HAL_IncTick(void)
{
  uwTick++;
}

__weak uint32_t HAL_GetTick(void)
{
  return uwTick;
}

__weak void HAL_Delay(__IO uint32_t Delay)
{
  uint32_t tickstart = 0;
  tickstart = HAL_GetTick();
  while((HAL_GetTick() - tickstart) < Delay)
  {
  }
}

Обработчик прерывания SysTick_Handler() вызывается каждую миллисекунду, и вызывает функцию HAL_IncTick(), которая просто увеличивает uwTick на единицу (инкрементирует). Функция HAL_GetTick() возвращает текущее время в миллисекундах; эта функция обильно используется библиотекой следующим образом:

uint32_t tickstart = HAL_GetTick();
...
if((HAL_GetTick()-tickstart) >= Timeout)
{ ... }

Операция сравнения времени довольно проста. Аналогичная операция используется в функции HAL_Delay().

Видите обработку переполнения? И я не вижу. А она есть.

Рассмотрим функцию HAL_Delay(). Для начала ситуацию без переполнения.

Допустим:

Delay = 100,
tickstart = 1000.

Разница

HAL_GetTick()-tickstart

будет расти от 0 до 100. Цикл будет прерван, когда HAL_GetTick() вернет 1100:

1100 - 1000 = 100

100 < 100 = ложь,

Теперь рассмотрим ситуацию с переполнением.

Допустим:

Delay = 100,
tickstart = 4294967290
.

Максимальное число в беззнаковой 32-битной переменной равно 4294967295. То есть до переполнения остается всего 5 инкрементов. При 6-м инкременте HAL_GetTick() вернет 0.

Что тогда вернет операция

HAL_GetTick()-tickstart

то есть, что будет если из 0 вычесть 4294967290?

Обе переменные беззнаковые и целочисленные. И операция вычитания вернет 6. А дальше, когда HAL_GetTick() возвратит 1, операция вычитания вернет 7.

То есть разница между текущим временем и tickstart все время будет расти, как и в нормальной ситуации. Функция задержки отработает правильно.

Важно, что такое поведение стандартизировано и должно реализовываться любым компилятор C и C++. Можете ознакомиться с обсуждением этого вопроса на Stack Overflow.

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

Вывод

Обработка времени в библиотеке STM32Cube происходит корректно, несмотря на то, что переменная uwTick, хранящая время, может переполнится.