понедельник, 23 августа 2010 г.

Расширение библиотеки GNU C для безопасного ввода строк.

    Не правильное применение функции scanf приводит к проблемам с безопасностью. Ниже приведён код, в котором произойдет переполнение буфера при введении 10 и более символов:  

    char str[10];
    scanf("%s", str);

    Чтобы избежать переполнения можно указать ширину поля:

    char str[10];
    scanf("%9s", str);

    В этом случае будет прочитано не более 9 символов, но если было введено больше символов, то в буфере stdin остаются лишние символы и перед следующим чтением буфер нужно очистить. Для избежания переполнения буфера, при использовании функции scanf, библиотека GNU C поддерживает не стандартное расширение, которое автоматически выделять достаточно память под вводимые данные. Для использования этого расширения нужно указать модификатор a как модификатор длинный %as:

    char *str;
    if ( scanf("%as", &str) == 1 )
    // указатель на char* указатель для адреса буфера
    {
        printf("%s\n", str);
        free(str);
    }

    При успешном выполнении функции scanf вызывающий код должен освободить выделенную память функцией free. Модификатор a не доступен если программа компилируется с флагом -std=c99 или -D_ISOC99_SOURCE. Начиная с версии 2.7 библиотека поддерживает модификатор m, который действует как a, но доступен при -std=c99.
    Вместо функции scanf рекомендуется пользоваться другим расширением библиотека GNU C, функцией getline, которая позволяет надёжно читать строку произвольной длинны и самостоятельно выделяет необходимый объёма памяти:

    ssize_t getline(char **lineptr, size_t *n, FILE *stream);
    lineptr — указатель на char* указатель для адреса динамически выделенного буфера.
                    Если инициализирован NULL, getline сам выделит память иначе должен
                    указывать на буфер выделенный с помощью функции malloc.
    n — указатель на размер буфера. Если буфер выделен с помощью malloc, *n должно
            содержать размер буфера, иначе 0.
    stream — поток, откуда читаются данные.


    Пример использования:

    #define _GNU_SOURCE
    #include <stdio.h>
    #include <stdlib.h>
    char* pUserName = NULL;
    size_t bytes_read, nbytes = 0;

    bytes_read = getline(&pUserName, &nbytes, stdin);
    if ( bytes_read != -1 )
    {
        printf("%s", pUserName);
    }

    if ( pUserName )
        free(pUserName);

    При достижении конца файла или ошибке функция возвращает -1. В случае успешного завершения функция возвращает число прочитанных символов (включая символ перевода строки, но не, включая пустой символ завершения) и может обновлять значения *lineptr и *n, если буфер изменялся.

вторник, 10 августа 2010 г.

Библиотека GNU Nana. Часть 1.

    GNU Nana это C/C++ библиотека, которая реализует проверку утверждений, ведения лога (можно выводить сообщения в файл, на терминал, другой процесс и буфер в памяти), измерения времени выполнения участка кода, трассировку выполнения программы и генерацию сокращённой формы программы в HTML файла (в нем будут содержаться заголовки функций и их предусловия и постусловия). Реализация утверждений в GNU Nana имеет минимальные издержки времени выполнения и размера добавочного кода, кроме того утверждения можно включать и выключать как при компиляции, так и в процессе выполнения программы.

    Проверка утверждений в стиле Си.
   Для использования этого интерфейса в программе, нужно подключить заголовочный файл nano.h. Ниже приведены основные макросы (в библиотеке так-же есть макросы которые кроме проверки условия позволяют задавать поведение при возникновении ошибочной ситуации.)
    Макрос void I(bool exprn). Если выражение exprn true, то программа является корректной.
    Макрос void N(bool exprn). Если выражение exprn false, то программа является корректной.

#include <nano.h>
void foo(int d)
{
    I(d != 0);
    x = y / d;
    ...
}

    В библиотеке реализованы так-же макросы позволяющие сохранить предыдущие значение переменной:
    Макрос void ID (Text decln).
    Макрос void IS (Text assignment).

#include <nano.h>
void foo(int* v, char z)
{
    ID(int* s = v);
    ID(char t);
    IS(t = z);
    ...
    I(s == v);
    I(t == z);
}

    Проверка утверждений в отладчике GDB.
    Утверждения проверяются только, если программа откомпилирована с флагом -g и для GDB создан сценарий.

    Макрос void DI(bool exprn) аналогично I().
    Макрос void DS(e) аналогично IS().

#include <stdio.h>
#include <nana.h>

int main(int ac, char** av)
{
    int a = 10;
    DS($s = a);
    printf("Hello! %d\n", a);
    DS(var a = 5);
    printf("Hello! %d\n", a);
    DI($s == a);
    return 0;
}

Компиляция и результат работы:
gcc -g nt.c -o nt -lnana
nana nt.c > nt.gdb
gdb nt
(gdb) source
nt.gdb
Hello! 10
Hello! 5
"DI(""$s ==a"")" has failed at f:l with
#0 main (ac=1,av=0xbffff4f4) at nt.c:12

Сайт проекта
Контрактное программирование

суббота, 31 июля 2010 г.

Библиотека Dlib. Пример использования класса bigint

    Класс bigint позволяет манипулировать с целыми числами без знака, диапазон значений которых ограничивается только доступными ресурсами системы. Есть две реализации больших целых чисел: bigint_kernel_1 и bigint_kernel_2. Единственное отличие bigint_kernel_2, это использование быстрого преобразования Фурье для выполнения операции умножения.

#include <iostream>
#include "../dlib/bigint.h"

typedef dlib::bigint::kernel_2a BigInt;

BigInt factorial(BigInt num);

int main(int ac, char** av)
{
     std::cout << factorial(10000) << std::endl;
     return 0;
}

/*
Для большого значения num лучше использовать один из
алгоритмов приведенных на странице:
http://www.luschny.de/math/factorial/FastFactorialFunctions.htm
*/
BigInt factorial(BigInt num)
{
    BigInt result = 1;
    
    for ( BigInt i=num; 0<i; i-- )
        result *= i;

    return result;
}

Вывод команды ./bigint | wc -m
35672                                                                            

пятница, 30 июля 2010 г.

Библиотека Dlib

     Dlib - это переносимая С++ библиотека общего назначения. Библиотека распространяется под свободной лицензией  Boost Software License и включает в себя много полезных классов и функций. Основные возможности:
  • Потоки (переносимый API для работы с потоками, межпотоковое взаимодействие по средствам pipe, локальные данные потока, пул потоков, механизм запуска глобальных функций в отдельном потоке и др.)
  • Сетевое программирование (переносимый API для работы с TCP сокетами, TCP сервер, HTTP сервер и др.)
  • Графический интерфейс пользователя (переносимый и потокобезопасный GUI API)
  • Численные алгоритмы (матрицы и операции над ними, алгоритмы нелинейной оптимизации, целые числа с диапазоном ограниченным только ресурсами системы, генератор псевдослучайных чисел и др.)
  • Алгоритмы машинного обучения
  • Алгоритмы для вычислений и обучения байесовских сетей.
  • Обработка изображений (чтение и запись формата Windows BMP, выделение границ, компьютерное зрение и др.).
  • Алгоритмы сжатия и проверки целостности (CRC32, MD5, LZP и др.).
  • Тестирование (потокобезопасный класс ведения лога, модельное тестирование, макросы проверки предусловий).
  • Полезные классы общего назначения (разбор XML, разбор параметров командной строки, различные контейнеры, base64 и др.). 
Сайт проекта

Библиотека Dlib. Пример использования шаблона класса binary_search_tree.

    Бинарное дерево поиска имеет две реализации: с использованием AVL бинарных деревьев binary_search_tree_kernel_1 и красно-черные бинарные деревья binary_search_tree_kernel_2

template <typename domain, 
          typename range, 
          typename mem_manager = memory_manager<char>::kernel_1a, 
          typename compare = std::less<domain> 
         > 
class binary_search_tree;

domain — тип ключа, должен позволять использовать специализации swap и  std::less, а также иметь конструктор по умолчанию.
range — тип значения, должен позволять использовать специализации swap и иметь конструктор по умолчанию.

#include <iostream>
#include <stdlib.h>
#include "../dlib/binary_search_tree.h"

//используем реализацию AVL бинарных деревьев
typedef dlib::binary_search_tree<int, int>::kernel_1a bst;


/*Функция печатает информацию о высоте дерева и количестве узлов*/
void printTreeInfo(const bst& tree)
{
   std::cout << "Size: "  << tree.size();
   std::cout << "  Height: " << tree.height() << "\n";
}

int main(int argc, char** argv)
{
    bst tree;

// добавляем в дерево числа от 1 до 99.
    for ( int i = 1; i<100; i++ ){
       int r = 0;
       int d = i;
       tree.add(d, r);
    }
 
// поиск значения по ключу
    int* f = tree[99];
    if ( f ) std::cout << "find\n";
   
    printTreeInfo(tree);

// очистка дерева
    tree.clear();
 
    printTreeInfo(tree);

// добавляем в дерево случайные числа в диапазоне от 0 до 99
    for ( int i = 1; i<100; i++ ){
       int r = 0;
       int d = rand() % 100;
       tree.add(d, r);
    }
   
    printTreeInfo(tree);
   
    return 0;
} 
 
Вывод программы:
find
Size: 99  Height: 7
Size: 0   Height: 0
Size: 99  Height: 8