« Поставить закладку » « Сделать стартовой »

« Форумы » « Блоги » « Статьи » « Новости » « Файлы » « Realcoding IRC » « Site map » « Поиск »


Главная Главная
Анонсы Анонсы
Форумы Форумы
Каталог Каталог
Поиск Поиск
Опросы Опросы
Книжный магазин Книжный магазин
Реклама на сайте
Публикации Публикации
Партнеры Партнеры
Карта Карта сайта
Рассылки Рассылки
RSS экспорт
Настройки Настройки
О нас пишут О нас пишут
Контакты Контакты
Гостевая книга Гостевая книга



ПнВтСрЧтПтСбВс
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
    Популярное
Компоненты: Edit

Глава 3. Выражения, операторы и передача управления

Описание функций C (Си) / C++ - time

Обобщённые численные операции

Глава 3. Использование внешних данных.

Глава 8. Ссылки

XNS. Основы технологии, Доступ к среде, Сетевой уровень, Транспортный уровень, Протоколы высших уровней

Вспомогательные функции

Функция AccessResource

Задание информации о разработчиках проекта




    Архив файлов



    Сообщества

    Документация

    Кто на сайте
Вы не зарегистрированы.
Имя:

Пароль:

Запомнить

Регистрация позволит Вам пользоваться дополнительными сервисами.
Сейчас на сайте:
Гостей: 163
Пользователей: 1
  • bess

  • Статьи:: C/С++ :: Введение в язык C++ :: Турне по C++ :: Описания и Константы :: Типы



    отправить ссылку другу версия для печати  Обсудить на форуме

    Типы



    • Основные Типы

    • Неявное Преобразование Типа

    • Производные Типы

    • Тип void

    • Указатели

    • Вектора

    • Указатели и Вектора

    • Структуры

    • Эквивалентность типов

    • Ссылки

    • Регистры



    Каждое имя (идентификатор) в C++ программе имеет ассоциированный с ним тип. Этот тип определяет, какие операции можно применять к имени (то есть к объекту, на который оно ссылается), и как эти операции интерпретируются.

    Например:

    int error number;
    float real(complex* p);

    Поскольку error_number описано как int, его можно присваивать, использовать в арифметических выражениях и т.д. Тогда как функция real может вызываться с адресом complex в качестве параметра. Можно взять адрес любого из них. Некоторые имена, вроде int и complex, являются именами типов. Обычно имя типа используется в описании для спецификации другого имени. Единственные отличные от этого действия над именем типа - это sizeof (для определения количества памяти, которая требуется для хранения объекта типа) и new (для размещения объекта типа в свободной памяти).

    Например:

    
    


    main()
    {
    int* p = new int;
    cout << "sizeof(int) = " << sizeof(int) "\n";
    }

    
    


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

    
    


    float f;
    char* p;
    //...
    long ll = long(p); // преобразует p в long
    int i = int(f); // преобразует f в int

    Основные Типы

    В C++ есть набор основных типов, которые соответствуют наиболее общим основным единицам памяти компьютера и наиболее общим основным способам их использования:


       

    • char
       
    • short int
       
    • int
       
    • long int


    для представления целых различных размеров,


       

    • float
       
    • double


    для представления чисел с плавающей точкой,


       

    • unsigned char
       
    • unsigned short int
       
    • unsigned int
       
    • unsigned long int


    для представления беззнаковых целых, логических значений, битовых массивов и т.п. Для большей компактности записи можно опускать int в комбинациях из нескольких слов, что не меняет смысла; так, long означает long int, и unsigned означает unsigned int. В общем, когда в описании опущен тип, он предполагается int.

    Например:

    const a = 1;
    static x;

    все определяют объект типа int.

    Целый тип char наиболее удобен для хранения и обработки символов на данном компьютере; обычно это 8-битовый байт. Размеры объектов C++ выражаются в единицах размера char, поэтому по определению sizeof(char)==1. В зависимости от аппаратного обеспечения char является знаковым или беззнаковым целым. Тип unsigned char, конечно, всегда беззнаковый, и при его использовании получаются более переносимые программы, но из-за применения его вместо просто char могут возникать значительные потери в эффективности.
    Причина того, что предоставляется более чем один целый тип, более чем один беззнаковый тип и более чем один тип с плавающей точкой, в том, чтобы дать возможность программисту воспользоваться характерными особенностями аппаратного обеспечения. На многих машинах между различными разновидностями основных типов существуют значительные различия в потребностях памяти, временах доступа к памяти и временах вычислений. Зная машину обычно легко, например, выбрать подходящий тип для конкретной переменной. Написать действительно переносимую программу нижнего уровня сложнее. Вот все, что гарантируется относительно размеров основных типов:

    1==sizeof(char)<=sizeof(short)<= sizeof(int)<=sizeof(long)
    sizeof(float)<=sizeof(double)

    Однако обычно разумно предполагать, что в char могут храниться целые числа в диапазоне 0...127 (в нем всегда могут храниться символы машинного набора символов), что short и int имеют не менее 16 бит, что int имеет размер, соответствующий целой арифметике, и что long имеет по меньшей мере 24 бита. Предполагать что-либо помимо этого рискованно, и даже эти эмпирические правила применимы не везде.

    Беззнаковые (unsigned) целые типы идеально подходят для применений, в которых память рассматривается как массив битов. Использование unsigned вместо int с тем, чтобы получить еще один бит для представления положительных целых, почти никогда не оказывается хорошей идеей. Попытки гарантировать то, что некоторые значения положительны, посредством описания переменных как unsigned, обычно срываются из-за правил неявного преобразования.

    Например:

    unsigned surprise = -1;

    допустимо (но компилятор обязательно сделает предупреждение).

    Неявное Преобразование Типа

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

    Существуют случаи, в которых информация может теряться или искажаться. Присваивание значения одного типа переменной другого типа, представление которого содержит меньшее число бит, неизбежно является источником неприятностей. Допустим, например, что следующая часть программы выполняется на машине с двоичным дополнительным представлением целых и 8-битовыми символами:

    int i1 = 256+255;
    char ch = i1 // ch == 255
    int i2 = ch; // i2 == ?

    В присваивании ch=i1 теряется один бит (самый значимый!), и ch будет содержать двоичный код "все-единицы" (т.е. 8 единиц); при присваивании i2 это никак не может превратиться в 511! Но каким же может быть значение i2? На DEC VAX, где char знаковые, ответ будет -1; на AT&T 3B-20, где char беззнаковые, ответ будет 255. В C++ нет динамического (т.е. действующего во время исполнения) механизма для разрешения такого рода проблем, а выяснение на стадии компиляции вообще очень сложно, поэтому программист должен быть внимателен.

    Производные Типы

    Другие типы модно выводить из основных типов (и типов, определенных пользователем) посредством операций описания:
     

    * указатель
    & ссылка
    [] вектор
    () функция


    и механизма определения структур.

    Например:

    int* a;
    float v[10];
    char* p[20]; // вектор из 20 указателей на символ
    void f(int);
    struct str { short length; char* p; };

    Основная идея состоит в том, что описание производного типа отражает его использование.

    Например:

    int v[10]; // описывает вектор
    i = v[3]; // использует элемент вектора
    int* p; // описывает указатель
    i = *p; // использует указываемый объект

    Вся сложность понимания записи производных типов проистекает из того, что операции * и & префиксные, а операции [] () постфиксные, поэтому для формулировки типов в тех случаях, когда приоритеты операций создают затруднения, надо использовать скобки. Например, поскольку приоритет у [] выше, чем у *, то

    int* v[10]; // вектор указателей
    int (*p)[10]; // указатель на вектор

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

    Описание каждого имени, вводимого в программе, может оказаться утомительным, особенно если их типы одинаковы. Но можно описывать в одном описании несколько имен. В этом случае описание содержит вместо одного имени список имен, разделенных запятыми. Например, два имени можно описать так:

    int x, y; // int x; int y;

    При описании производных типов можно указать, что операции применяются только к отдельным именам (а не ко всем остальным именам в этом описании).

    Например:

    int* p, y; // int* p; int y; НЕ int* y;
    int x, *p; // int x; int* p;
    int v[10], *p; // int v[10]; int* p;

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

    Тип void

    Тип void (пустой) синтаксически ведет себя как основной тип. Однако использовать его можно только как часть производного типа, объектов типа void не существует. Он используется для того, чтобы указать, что функция не возвращает значения, или как базовый тип для указателей на объекты неизвестного типа.

    void f() // f не возвращает значение
    void* pv; // указатель на объект неизвестного типа

    Переменной типа void* можно присваивать указатель любого типа. На первый взгляд это может показаться не особенно полезным, поскольку void* нельзя разыменовать, но именно это ограничение и делает тип void* полезным. Главным образом, он применяется для передачи указателей функциям, которые не позволяют сделать предположение о типе объекта, и для возврата из функций нетипизированных объектов. Чтобы использовать такой объект, необходимо применить явное преобразование типа. Подобные функции обычно находятся на самом нижнем уровне системы, там, где осуществляется работа с основными аппаратными ресурсами.

    Например:

    void* allocate(int size); // выделить
    void deallocate(void*); // освободить
    f() {
    int* pi = (int*)allocate(10*sizeof(int));
    char* pc = (char*)allocate(10);
    //...
    deallocate(pi);
    deallocate(pc);
    }

    Указатели

    Для большинства типов T T* является типом указатель на T. То есть, в переменной типа T* может храниться адрес объекта типа T. Для указателей на вектора и указателей на функции вам, к сожалению, придется пользоваться более сложной записью:

    int* pi;
    char** cpp; // указатель на указатель на char
    int (*vp)[10]; // указатель на вектор из 10 int"ов
    int (*fp)(char, char*); // указатель на функцию
    // получающую параметры (char, char*)
    // и возвращающую int

    Основная операция над указателем - разыменование, то есть ссылка на объект, на который указывает указатель. Эта операция также называется косвенным обращением. Операция разыменования - это унарное * (префиксное).

    Например:

    char c1 = "a";
    char* p = &c1; // в p хранится адрес c1
    char c2 = *p; // c2 = "a"

    Переменная, на которую указывает p,- это c1, а значение, которое хранится в c1, это "a", поэтому присваиваемое c2 значение *p есть "a".

    Над указателями можно осуществлять некоторые арифметические действия. Вот, например, функция, подсчитывающая число символов в строке (не считая завершающего 0):

    int strlen(char* p)
    {
    int i = 0;
    while (*p++) i++;
    return i;
    }

    Другой способ найти длину состоит в том, чтобы сначала найти конец строки, а затем вычесть адрес начала строки из адреса ее конца:

    int strlen(char* p)
    {
    char* q = p;
    while (*q++) ;
    return q-p-1;
    }

    Вектора

    Для типа T T[size] является типом "вектор из size элементов типа T". Элементы индексируются (нумеруются) от 0 до size-1.

    Например:

    float v[3]; // вектор из трех float: v[0], v[1], v[2]
    int a[2][5]; // два вектора из пяти int
    char* vpc; // вектор из 32 указателей на символ

    Цикл для печати целых значений букв нижнего регистра можно было бы написать так:

    extern int strlen(char*);
    char alpha[] = "abcdefghijklmnoprstuvwxyz";
    main()
    {
    int sz = strlen(alpha);
    for (int i=0; i.

    Функция strlen() использовалась для подсчета числа символов в alpha; вместо этого можно было использовать значение размера alpha . Если применяется набор символов ASCII, то выдача выглядит так:

    "a" = 97 = 0141 = 0x61
    "b" = 98 = 0142 = 0x62
    "c" = 99 = 0143 = 0x63
    ...

    Заметим, что задавать размер вектора alpha необязательно; компилятор считает число символов в символьной строке, указанной в качестве инициализатора. Использование строки как инициализатора для вектора символов - удобное, но к сожалению и единственное применение строк. Аналогичное этому присваивание строки вектору отсутствует.

    Например:

    char v[9];
    v = "строка"; // ошибка

    ошибочно, поскольку присваивание не определено для векторов.

    Конечно, для инициализации символьных массивов подходят не только строки. Для остальных типов нужно применять более сложную запись. Эту запись можно использовать и для символьных векторов.

    Например:

    int v1[] = { 1, 2, 3, 4 };
    int v2[] = { "a", "b", "c", "d" };

    char v3[] = { 1, 2, 3, 4 };
    char v4[] = { "a", "b", "c", "d" };

    Заметьте, что v4 - вектор из четырех (а не пяти) символов; он не оканчивается нулем, как того требуют соглашение и библиотечные подпрограммы. Обычно применение такой записи ограничивается статическими объектами.

    Многомерные массивы представляются как вектора векторов, и применение записи через запятую, как это делается в некоторых других языках, дает ошибку при компиляции, так как запятая (,) является операцией последования . Попробуйте, например, сделать так:

    int bad[5,2]; // ошибка

    и так:

    int v[5][2];
    int bad = v[4,1]; // ошибка
    int good = v[4][1]; // ошибка

    Описание char v[2][5]; описывает вектор из двух элементов, каждый из которых является вектором типа char[5]. В следующем примере первый из этих векторов инициализируется первыми пятью буквами, а второй - первыми пятью цифрами.

    char v[2][5] = {
    "a", "b", "c", "d", "e",
    "0", "1", "2", "3", "4"
    }

    main() {
    for (int i = 0; i<2; i++) {
    for (int j = 0; j<5; j++)
    cout << "v[" << i << "][" << j
    << "]=" << chr(v[i][j]) << " ";
    cout << "\n";
    }
    }

    это дает в результате

    v[0][0]=a v[0][1]=b v[0][2]=c v[0][3]=d v[0][4]=e
    v[1][0]=0 v[1][1]=1 v[1][2]=2 v[1][3]=3 v[1][4]=4

    Указатели и Вектора

    Указатели и вектора в C++ связаны очень тесно. Имя вектора можно использовать как указатель на его первый элемент, поэтому пример с алфавитом можно было написать так:
     

     


    char alpha[] = "abcdefghijklmnopqrstuvwxyz";
    char* p = alpha;
    char ch;

    while (ch = *p++)
    cout << chr(ch) << " = " << ch
    << " = 0" << oct(ch) << "\n";
     

     


    Описание p можно было также записать как

    char* p = α[0];

    Эта эквивалентность широко используется в вызовах функций, в которых векторный параметр всегда передается как указатель на первый элемент вектора; так, в примере

    extern int strlen(char*);
    char v[] = "Annemarie";
    char* p = v;
    strlen(p);
    strlen(v);

    функции strlen в обоих вызовах передается одно и то же значение. Вся штука в том, что этого невозможно избежать; то есть не существует способа описать функцию так, чтобы вектор v в вызове функции копировался.

    Результат применения к указателям арифметических операций +, -, ++ или -- зависит от типа объекта, на который они указывают. Когда к указателю p типа T* применяется арифметическая операция, предполагается, что p указывает на элемент вектора объектов типа T; p+1 означает следующий элемент этого вектора, а p-1 - предыдущий элемент. Отсюда следует, что значение p+1 будет на sizeof(T) больше значения p.

    Например, выполнение
     

     


    main()
    {
    char cv[10];
    int iv[10];
    char* pc = cv;
    int* pi = iv;
    cout << "char* " << long(pc+1)-long(pc) << "\n";
    cout << "int* " << long(ic+1)-long(ic) << "\n";
    }
     

     


    дает

    char* 1
    int* 4

    поскольку на моей машине каждый символ занимает один байт, а каждое целое занимает четыре байта. Перед вычитанием значения указателей преобразовывались к типу long с помощью явного преобразования типа. Они преобразовывались к long, а не к "очевидному" int, поскольку есть машины, на которых указатель не влезет в int (то есть, sizeof(int)).

    Структуры

    Вектор есть совокупность элементов одного типа; struct является совокупностью элементов (практически) произвольных типов.

    Например:
     

     


    struct address { // почтовый адрес
    char* name; // имя "Jim Dandy"
    long number; // номер дома 61
    char* street; // улица "South Street"
    char* town; // город "New Providence"
    char* state[2]; // штат "N" "J"
    int zip; // индекс 7974
    }
     

     


    определяет новый тип, названный address (почтовый адрес), состоящий из пунктов, требующихся для того, чтобы послать кому-нибудь корреспонденцию (вообще говоря, address не является достаточным для работы с полным почтовым адресом, но в качестве примера достаточен). Обратите внимание на точку с запятой в конце; это одно из очень немногих мест в C++, где необходимо ставить точку с запятой после фигурной скобки, поэтому люди склонны забывать об этом.

    Переменные типа address могут описываться точно также, как другие переменные, а доступ к отдельным членам получается с помощью операции . (точка).

    Например:

    address jd;
    jd.name = "Jim Dandy";
    jd.number = 61;

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

    Например:
     

     


    address jd = {
    "Jim Dandy",
    61, "South Street",
    "New Providence", {"N","J"}, 7974
    };
     

     



    Однако обычно лучше использовать конструктор . Заметьте, что нельзя было бы инициализировать jd.state строкой "NJ". Строки оканчиваются символом "\0", поэтому в "NJ" три символа, то есть на один больше, чем влезет в jd.state.

    К структурным объектам часто обращаются посредством указателей используя операцию ->.

    Например:
     

     


    void print_addr(address* p)
    {
    cout << p->name << "\n"
    << p->number << " " << p->street << "\n"
    << p->town << "\n"
    << chr(p->state[0]) << chr(p->state[1])
    << " " << p->zip << "\n";
    }
     

     


    Объекты типа структур можно присваивать, передавать как параметры функции и возвращать из функции в качестве результата.

    Например:
     

     


    address current;
    address set_current(address next)
    {
    address prev = current;
    current = next;
    return prev;
    }
     

     


    Остальные осмысленные операции, такие как сравнение (== и !=) не определены. Однако пользователь может определить эти операции.
    Размер объекта структурного типа нельзя вычислить просто как сумму его членов. Причина этого состоит в том, что многие машины требуют, чтобы объекты определенных типов выравнивались в памяти только по некоторым зависящим от архитектуры границам (типичный пример: целое должно быть выравнено по границе слова) или просто гораздо более эффективно обрабатывают такие объекты, если они выравнены в машине. Это приводит к "дырам" в структуре. Например, (на моей машине) sizeof (address) равен 24, а не 22, как можно было ожидать.

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

    Например:
     

     


    struct link{
    link* previous;
    link* successor;
    }
     

     


    Новые объекты структурного типа не могут быть описываться, пока все описание не просмотрено, поэтому
     

     


    struct no_good {
    no_good member;
    };
     

     


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

    Например:
     

     


    struct list; // должна быть определена позднее
    struct link {
    link* pre;
    link* suc;
    link* member_of;
    };
    struct list {
    link* head;
    }

    Без первого описания list описание link вызвало бы к синтаксическую ошибку.

    Эквивалентность типов

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

    Например:

    struct s1 { int a; };
    struct s2 { int a; };

    есть два разных типа, поэтому

    s1 x;
    s2 y = x; // ошибка: несоответствие типов

    Структурные типы отличны также от основных типов, поэтому

    s1 x;
    int i = x; // ошибка: несоответствие типов

    Однако, существует механизм для описания нового имени для типа без введения нового типа. Описание с префиксом typedef описывает не новую переменную данного типа, а новое имя этого типа.

    Например:

    typedef char* Pchar;
    Pchar p1, p2;
    char* p3 = p1;

    Это может служить удобной сокращенной записью.

    Ссылки

    Ссылка является другим именем объекта. Главное применение ссылок состоит в спецификации операций для типов, определяемых пользователем. Они могут также быть полезны в качестве параметров функции. Запись x& означает ссылка на x.

    Например:
     

     


    int i = 1;
    int& r = i; // r и i теперь ссылаются на один int
    int x = r // x = 1
    r = 2; // i = 2;
     

     


    Ссылка должна быть инициализирована (должно быть что-то, для чего она является именем). Заметьте, что инициализация ссылки есть нечто совершенно отличное от присваивания ей.

    Вопреки ожиданиям, ни одна операция на ссылку не действует. Например,
     

     


    int ii = 0;
    int& rr = ii;
    rr++; // ii увеличивается на 1
     

     



    допустимо, но rr++ не увеличивает ссылку; вместо этого ++ применяется к int, которым оказывается ii. Следовательно, после инициализации значение ссылки не может быть изменено; она всегда ссылается на объект, который ей было дано обозначать (денотировать) при инициализации. Чтобы получить указатель на объект, денотируемый ссылкой rr, можно написать &rr.

    Очевидным способом реализации ссылки является константный указатель, который разыменовывается при каждом использовании. Это делает инициализацию ссылки тривиальной, когда инициализатор является lvalue (объектом, адрес которого вы можете взять). Однако инициализатор для &T не обязательно должен быть lvalue, и даже не должен быть типа T. В таких случаях:


       

    1. Во-первых, если необходимо, применяются преобразование типа;
       
    2. Затем полученное значение помещается во временную переменную; и
       
    3. Наконец, ее адрес используется в качестве значения инициализатора.


    Рассмотрим описание

    double& dr = 1;

    Это интерпретируется так:
     

     


    double* drp; // ссылка, представленная как указатель
    double temp;
    temp = double(1);
    drp = &temp;
     

     


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

     


    int x = 1;
    void incr(int& aa) { aa++; }
    incr(x) // x = 2
     

     


    По определению семантика передачи параметра та же, что семантика инициализации, поэтому параметр aa функции incr становится другим именем для x. Однако, чтобы сделать программу читаемой, в большинстве случаев лучше всего избегать функций, которые изменяют значение своих параметров. Часто предпочтительно явно возвращать значение из функции или требовать в качестве параметра указатель:
     

     


    int x = 1;
    int next(int p) { return p+1; }
    x = next(x); // x = 2

    void inc(int* p) { (*p)++; }
    inc(&x); // x = 3
     

     


    Ссылки также можно применять для определения функций, которые могут использоваться и в левой, и в правой части присваивания. Опять, большая часть наиболее интересных случаев этого обнаруживается в проектировании нетривиальных определяемых пользователем типов. Для примера давайте определим простой ассоциативный массив. Вначале мы определим структуру пары следующим образом:
     

     


    struct pair {
    char* name;
    int val;
    };
     

     


    Основная идея состоит в том, что строка имеет ассоциированное с ней целое значение. Легко определить функцию поиска find(), которая поддерживает структуру данных, состоящую из одного pair для каждой отличной от других строки, которая была ей представлена. Для краткости представления используется очень простая (и неэффективная) реализация:
     

     


    const large = 1024;
    static pair vec[large+1};

    pair* find(char* p)
    /*
    поддерживает множество пар "pair":
    ищет p, если находит, возвращает его "pair",
    иначе возвращает неиспользованную "pair"
    */
    {
    for (int i=0; vec[i].name; i++)
    if (strcmp(p,vec[i].name)==0) return &vec[i];
    if (i == large) return &vec[large-1];
    return &vec[i];
    }
     

     


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

     


    int& value(char* p)
    {
    pair* res = find(p);
    if (res->name == 0) { // до сих пор не встречалось:
    res->name = new char[strlen(p)+1]; // инициализировать
    strcpy(res->name,p);
    res->val = 0; // начальное значение 0
    }
    return res->val;
    }
     

     


    Для данной в качестве параметра строки value() находит целый объект (а не значение соответствующего целого); после чего она возвращает ссылку на него. Ее можно использовать, например, так:
     

     


    const MAX = 256; // больше самого большого слова
    main()
    // подсчитывает число вхождений каждого слова во вводе
    {
    char buf[MAX];
    while (cin>>buf) value(buf)++;
    for (int i=0; vec[i].name; i++)
    cout << vec[i].name << ": " << vec [i].val << "\n";
    }
     

     


    На каждом проходе цикл считывает одно слово из стандартной строки ввода cin в buf , а затем обновляет связанный с ней счетчик с помощью find(). И, наконец, печатается полученная таблица различных слов во введенном тексте, каждое с числом его встречаемости. Например, если вводится

    aa bb bb aa aa bb aa aa

    то программа выдаст:

    aa: 5
    bb: 3

    Легко усовершенствовать это в плане собственного типа ассоциированного массива с помощью класса с перегруженной операцией выбора

    Регистры

    Во многих машинных архитектурах можно обращаться к (небольшим) объектам заметно быстрее, когда они помещены в регистр. В идеальном случае компилятор будет сам определять оптимальную стратегию использования всех регистров, доступных на машине, для которой компилируется программа. Однако это нетривиальная задача, поэтому иногда программисту стоит дать подсказку компилятору. Это делается с помощью описания объекта как register.

    Например:

    register int i;
    register point cursor;
    register char* p;

    Описание register следует использовать только в тех случаях, когда эффективность действительно важна. Описание каждой переменной как register засорит текст программы и может даже увеличить время выполнения (обычно воспринимаются все инструкции по помещению объекта в регистр или удалению его оттуда).

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








    Подгрузка через AJAX HTML-кода, содержащег....

    AJAX

    При разработке CMS S.Builder наша команда активно использовала AJAX. Теперь вот решили поделиться накопленным опытом. Начнем с этого хабратопика. Не буду здесь затрагивать различные фреймворки и библиотеки. Свой код всегда роднее. Для работы с AJAX-ом в S.Builder написана библиотека sbAJAX. Можете качать и пользоваться :). В этом файле есть функция sbEvalJS. Для тех, кто не знает, объясню. При подгрузке через AJAX и вставке на страницу HTML-кода, содержащего JavaScript, JavaScript выполняться не будет или полезут баги. Эта функция как раз решает поставленную задачу.


    Подробнее... | Рубрика: AJAX | Добавлено: 19.11.2008

    Обзор нового релиза самой мощной Ajax библ....

    AJAX

    Хотя наш обзор немного запоздал, оригинальный Dojo 1.2 вышел в релизной версии ещё 6-го октября, но сейчас мы наверстаем упущенное. И так, Dojo Toolkit — это самая мощная и гибкая ajax-библиотека из всех, что есть на рынке, она активно развивается и имеет большое комьюнити. Кстати, это самое комьюнити, совместно с компанией Sitepen, имеет ещё несколько проектов, среди которых и Cometd и некоторые другие, не менее интересные, о которых мы скоро вам расскажем. Сегодня же все внимание на флагманский продукт — Dojo 1.2.


    Подробнее... | Рубрика: AJAX | Добавлено: 19.11.2008

    Firebug 1.3 и 1.4 alpha — что нового и инт....

    Вебмастеру

    Если вы профессиональный веб-разработчик и постоянно имеете дело с разработкой и отладкой сложных AJAX приложений, то наверняка знаете и используете Firebug — плагин для браузера Firefox, предназначенный для отладки и исследования веб-приложений. Текущая его версия, 1.2х достаточно стабильная и функциональна, чтобы помочь в 99% проблем, которые могут возникнуть при разработке. Но и этот инструмент не лишён если не недостатков, то некоторых фич, которые могли бы облегчить работу. И даже идеальный инструмент можно сделать ещё более идеальным, как бы это не звучало.


    Подробнее... | Рубрика: Вебмастеру | Добавлено: 19.11.2008

    Остальные статьи:

    Релиз Microsoft Silverlight 2.0. Что новог...
    XML документация в C#
    Курсоры в MySQL 5
    Microsoft опубликовала подробности о сесси...
    Microsoft делится подробностями о том, что...
    Тестируем новый javascript от нового брауз...
    MySQL Query Cache
    Использование провайдеров компиляции в As...
    Чего мы ждем от C# 4.0
    Delphi 2009 и C++Builder 2009
    Джоэл Спольски и Джеф Этвуд запустили новы...
    Поиск кода Google /* что нового? */
    10 jQuery скриптов для улучшения интерфейс...
    Генераторы отчетов FastReport 4 и QuickRep...
    День программиста — набор стерeотипов
    Индусские програмисты
    Вышел Django 1.0
    Портативная версия Google Chrome Portable
    Исходные коды .Net Frameword 3.5 SP1 для о...
    Пишем правильный online WYSIWYG-редактор


    Цитата дня (все,добавить):

    Портал фрилансеров

    работа на дому


        Рубрикатор

    Программирование

    C/С++
    Обучение
    Windows API
    XAML
    Моделирование
    Паттерны
    Visual Basic 7 .NET
    WxWidgets
    Функции WinApi
    Функции С++
    Разработка под Mac OS
    Eiffel
    Visual Studio 2008
    UI дизайн
    Алгоритмы
    Конкурсные статьи
    Turbo Pascal
    Visual Studio
    CASE-средства
    Visual Studio 2005
    Без VCL
    Delphi
    Тех. документация
    Тестирование
    Software Testing
    ООП
    TCP/IP
    Google Android
    Windows Installer
    .NET Framework
    Драйвера
    C# C Sharp
    Справка
    Проектирование
    Информ. системы
    Visual Basic
    Assembler
    Оптимизация кода
    Gtk+
    Компоненты
    Реинжиниринг
    Управление проектами
    Extreeme programming
    Lotus Notes
    Алгебраическое проектирование


    Интернет технологии

    PHP
    Perl
    ASP
    WAP
    Cookies
    SSI
    CGI
    Web Servers
    VB Script
    DNS
    CSS
    XML
    Html
    Java Script
    Java2ME
    Firewall
    Flash
    .htaccess
    Apache
    VRML
    Протоколы
    Поисковые системы
    Технология JAVA
    Учебник по PHP
    Учебник по JavaScript
    Учебник по XML
    Java Q&A
    AJAX
    DHTML
    XHTML
    Dreamweaver
    Web 2.0
    Python
    Вебмастеру
    Cisco
    Ruby on Rails
    Silverlight

    Базы данных

    Access
    InterBase
    MySQL
    Oracle
    ADO .NET
    Основы SQL
    Учебник по Access 2002
    MS
    Microsoft FoxPro
    Доступ к данным
    XML в MS SQL Server 2000
    ODBC и MyODBC
    Обучение
    Caché
    DB2
    PostgresSQL
    Sybase
    Теория
    Хранилища данных
    Безопасность
    Реляционные данные
    MySQL и mSQL

    Остальное:

    Разное
    Обзоры книг
    Безопасность
    Графика и дизайн
    Юмор
    Linux
    Фракталы
    Microsoft Axapta
    Многоядерность
    Сети
    Microsoft Office
    Работа
    MS-DOS
    Криптография
    Графика и игроделание
    Новости SDK
    Системы защиты
    Учебник по AutoCad
    CVS
    Windows XP
    Windows Server 2003
    Windows Vista
    Windows 7
    Мероприятия