Модераторы: bsa

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Указатели, строки, классы и пр. Си++ новичкам посвящается... 
V
    Опции темы
ManiaK
  Дата 9.8.2005, 14:35 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Homo Sapience
***


Профиль
Группа: Комодератор
Сообщений: 1145
Регистрация: 3.8.2004
Где: ИУ5-93

Репутация: 2
Всего: 29



 ВВЕДЕНИЕ

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

    Эта статья не учит Си++ - предполагается, что читатель уже знаком с основной частью синтаксиса Си++, но не имеет опыта в программировании на нём или этот опыт незначителен. Проще говоря, статья ориентирована на новичков. Я буду по ходу дела объяснять некоторые важные моменты, на которые часто не обращают внимания новички, но все мои рассуждения будут уже содержать в себе базовые термины, такие как класс, метод, функция, переменная и прочее. Все эти понятия должны быть понятны читателю до прочтения данной статьи иначе оно не будет иметь никакого смысла. И ещё: все примеры в этой статье - не готовые решения, а лишь демострация идей; поэтому не стоит копировать их и пытаться компилировать без каких либо изменений - идеи надо просто усвоить.

    Ну, готовы?..

    ЧАСТЬ 1. СИ СТРОКИ ИЛИ НЕМНОГО ТЕОРИИ.

    Наверное треть всех вопросов, задаваемых новичками на различных форумах интернета, посвящённых Си или Си++, так или иначе касаются строк. Кстати, в названии параграфа я не ошибся - именно Си строки. Почему? потому что в Си++ есть классы и использование указателей бессмысленно (Си является практически идеальным подмножеством Си++ и потому почти всё, что справедливо для Си, справедливо и для Си++). В сущности, все проблемы со строками сводятся к проблемам указателей. Если человек не понимает, как работают указатели и что они из себя представляют, он никогда не поймёт, чем char отличается от char*. Если ещё более углубиться в проблему, то можно увидеть, что непонимание указателей связано зачастую с непонимаением работы самого компьютера. Действительно, а знаете ли вы как ваши программы выполняются компьютером? В Си, как и в Си++, этот вопрос очень важен, так как эти языки очень тесно взаимодействуют с аппаратным уровнем.

    Обычно ваша программа (бинарный объектный код, понятный процессору без трансляции) загружается в оперативную память и там исполняется. То есть, предположим, что ваша программа занимает 512 байт. Загрузчик программы определяет, куда лучше всего впихнуть её; предположим, он выбрал адрес 0x1000. Тогда последний байт вашей программы будет по адресу 0x1200 (0x1000 + 512). Некоторые преполагают, что в том же месте хранятся все переменные программы. Спешу вас разуверить, это не так. В сегменте кода (так правильно называется эта часть оперативной памяти) хранятся только машинные инструкции самой программы и (не обязательно) константные объекты. Проще говоря, только то, что не меняется в процессе выполнения программы. При попытке из вашей же программы поменять код ("поработать над собой") хорошая операционная система даст оплеуху и выкинет из списка задач, почистив за вами память. Где же хранятся переменные?..

Необходимо сначала разделить переменные на два рода - "динамические" и стековые. В чём разница? во-первых, в скорости выделения памяти под них: под стековые переменные память выделяется с гиганской скоростью и ещё быстрее высвобождается, в то время как при создании динамической переменной система ещё подумает: а где взять эту память?.. Данный вопрос очень важен, часто программисты "обманывают" компилятор, заключая динамический объект в статическую оболочку.

Второе достоинство стековых переменных в том, что они автоматически удаляются, коль скоро выполнение программы выходит за облась действия данной переменной. Иначе говоря, программисту не приходится следить за тем, когда отдавать память обратно системе - компилятор делает это за него сам. Но за всё приходится платить в нашем мире: при использовании стековых указателей приходится следить, чтобы в их личную жизнь никто не вмешивался и не брал указатели на них, иначе может получиться казус: переменной уже сто лет как нет, а указатель на неё до сих пор существует. Ничего не подозревающий клиент пытается обратиться по этому указателю к объекту и получает по шее от операционной системы - упс...

Что же такое динамические переменные? - это переменные, которые можно создавать и удалять в процессе работы программы. Например, нам нужно написать программу, которая будет читать файлы, указанные пользователем и как-то обрабатывать их. Для этого нам нужно записывать прочитанные данные в какой-то массив, причём мы заранее не можем сказать, какого размера должен быть этот массив. Как быть? использовать динамическую переменную.

В теории всё просто. При непосредственном выполнении ваша программа выясняет размер файла и запрашивает у операционной системы необходимый объём памяти. Но как нам "дадут" эту память? Просто! Операционная система выделяет в оперативной памяти область, размер которой равен запрошенному, и даёт вам адрес первого байта этой области (далее просто - адрес области) - этакий жетончик, по которому вы можете получить свои данные. Помимо этого она заносит в свои списки вашу программу, адрес области и её размер. Теперь вы полноправный владелец данной области памяти! Смотрите, как выглядит последний абзац в Си-коде:

Код

    int *i = alloc(1000);// Хотим блок памяти в тысячу байт


    В Си++ есть ещё вариант:

Код

    int *i = new int[1000];// Здесь сложнее - блок памяти, размером в тысячу int'ов


    В обоих примерах переменная i - статическая, а область памяти, адрес которой эта переменная содержит, - динамическая. Сложно? только первое время, на самом деле всё логично. Можно даже обнаглеть и написать такое:

Код

    unsigned long ic = (unsigned long)alloc(1000);// Ха-ха, в Си такое прокатывает
    unsigned long icpp = reinterpret_cast<unsigned long>(new int[1000]);// А так - в Си++


    В Си++ значительно более уважительно относятся к типам объектов и на любые преобразования, подобные этим, грустно улыбаются; от себя хочу добавить, что подобными фокусами по возможности лучше не заниматься. Размер unsigned long гарантированно совпадает с размером указателя. С указателями пользователь может выполнять некоторые арифметические операции; рассмотрим на примере:

Код

    int *p = new int;// Если скобок нет - объект один
    int *p2 = p;// Присвоение адреса из одного указателя другому
    p2 += 10;// Сдвиг указателя на десять позиций
    p2 = 1001;// Ошибка! слева тип - указатель, справа - числовая константа!


    Почему предпоследняя строчка работает, а последняя нет, хотя и в той и другой справа - числа? Есть одна маленькая хитрость. В предпоследней операции при сдвиге литера 10 имеет не числовой тип, а специальный тип ptr_diff. Это тип разницы между двумя адресами; переменные этого типа можно прибавлять/вычитать из указателей, но их нельзя присваивать. Иногда этот тип обозначают как difference_typе.

    Да, для любителей поглумиться над компилятором скажу сразу: операции умножения/деления над указателями запрещены.

    Наконец мы выделили блок нужного размера и записали туда прочитанные из файла данные, обработали их. Теперь нужно читать следующий файл, вероятно, другого размера. Что делать со "старым" блоком? Есть два варианта:

    1. Использовать функцию операционной системы для увеличения размера блока (обычно - realloc).
    2. Удалить старый блок ("вернуть" его системе) и запросить новый нужного размера.

    В Си это выглядит примерно так:

Код

    free(p);// Возвращаем память системе
    // p теперь указывает на нулевой адрес; гарантируется, что там ничего нет
    p = alloc(500);// Берём блок по-меньше


    В Си++ это может выглядеть ещё и так:

Код

    delete [] p2;// Квадратные скобки указывают на то, что удаляемый объект - массив
    // если их нет, считается, что объект одиночный
    p2 = new int[125];// Во так


    При выделении памяти оператором new под массив обычно помечается также и размер выделенного массива. Тогда при освобождении памяти оператором delete [] программа будет знать, сколько памяти вернуть системе. Вот такие ситуации не желательны:

Код

    int* pi = new int[12];
    delete pi;// Результат не определён, зависит от конкретного компилятора или ОС


    Лучше не делать этого. Чаще всего разработчики компиляторов перестраховываются и разницы между удалением массива и удалением одиночного объекта нет (и то - при использовании встроенных типов). Но по закону подлости, если сделать такую пакость, как в последнем примере, через несколько месяцев (лет?) вашу программу перекомпилируют на другом компиляторе или перегрузят операторы delete и.. лучше просто следовать известному в России принципу "заплати налоги - спи спокойно" под налогом подразумевая дополнительную проверку кода на такие вот тонкости.

    И последнее замечание по указателям: в Си динамически созданные объекты сами не удаляются. То есть, если вы создали какую-то переменную через alloc-функцию или new, она будет висеть в RAM до самого завершения работы программы. Такие маленькие недоработки называют утечками памяти и иногда они выливаются в полное исчерпание ресурсов системы и её зависание (в том случае если программа работает достаточно продолжительное время и регулярно "теряет" память).

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

    Первое и самое главное, что вам необходимо понять - это что в Си++, равно как и в Си, нет типа данных "строка". Поняли? ну я и не сомневался, разберём по-подробней...

    Си - самый логичный с точки зрения архитектуры ЭВМ язык программирования. Практически всё, что можно сделать на ассемблере, можно сделать и на Си. Не верите? Смотрите:

Код

    unsigned char* pch = (unsigned char*)0xBC00;
    memcpy(pch, _T("Hello, World!"), 26);


    Знаете, что сделают эти две строчки, если пустить их сразу после инициализации Биос? Выведут вам на экран строку "Hello, World!". Мы можем откомпилировать этот код, записать в первый сектор дискеты и загрузиться с неё. Здорово? Если попытаться проделать тоже под ОС, очень вероятно, что ваша программа просто "грохнется", т.к. любая уважающая себя ОС не даёт кому ни попадя лазить в системную область RAM.

    В Си++ всё выглядит почти также. Отличие только в том, что преобразовывать числовую константу в указатель приходится через reinterpret_cast. Ну не любят этого плюсники! Разумеется, всё это сказано только для общего развития, подобные вещи принято и правильно писать на Ассемблере, так как только в нём можно наиболее эффективно работать с аппаратурой напрямую. На данном примере вы должны были уже понять, что строка в Си/Си++ - это последовательность кодов из заданной таблицы символов. Не правда ли логично? Загляните в текстовый файл hex-редактором и вы увидите нечто вроде этого:

    
Цитата
cf ff f2 b8 f0 ea e0 20 e7 e0 20 eb fe e1 ee e7
    ed e0 f2 e5 eb fc ed ee f1 f2 fc 21 3a 2d 29 20
    d1 e8 2b 2b 20 f0 f3 eb e8 f2 21 20 cd e8 ea ee
    ec f3 20 ed e5 20 e3 ee e2 ee f0 e8 21 3a 29 29


    Проще говоря, вы увидите набор кодов символов. Точно также строки хранятся в Си/Си++ переменных. Придумайте, как можно текст представить иначе и очень может быть, что вам дадут нобелевскую премию. Шутка!..

    Любая строка в Си - это массив символов. Всё остальное вытекает из этого постулата.

Код

    char str[] = {'S', 't', 'r', 'i', 'n', 'g'};// Статический массив (статическая строка)
    str[3] = 'o';// Нормально. Теперь str содержит "Strong"
    // Внимание - ВАЖНО!
    char* pStr = "Constant String";// Указатель на константную (!!!) строку!
    pStr[3] = 'A';// Ошибка! результат зависит от реализации...
    char* pStr2 = new char[15];// Вот так нужно создавать динамические строки
    strcpy(pStr2, "Dinamic String");// После этого pStr2 будет указывать на
    // динамическую область памяти, содержащую строку "Dinamic String"


    Как известно, строки в Си заканчиваются нулевым символом (символом, имеющим код 0). Любая строка, взятая в двойные кавычки по определению заканчивается этим символом и потому размер "Dinamic String" не 14, а 15. Это нужно для того, чтобы программа, использующая эту строку, знала, где она кончается. Можно, конечно, для каждой строки таскать свою переменную, в которой содержится число символов, но это очень неудобно в тех случаях, когда приходится оперировать десятками различных строк. В Pascal'е другой принцип - первый символ строки содержит не код первого символа, а число символов в строке. Но тогда для стандартного байтного набора символов максимально возможной длинной строки будет 255 символов, а этого бывает мало.

    Рассмотрим ещё пару примеров работы со строками в Си стиле и закончим эту главу.

Код

    char* pStr = new char[255];// Берём с запасом

    strcpy(pStr, "Тестовая строка");

    strcat(pStr, " #1");// Добавление к строке, pStr = pStr + " #1"
    strncpy(pStr, "String", 3);// Копирование n символов, pStr = "Str"
    strncat(pStr, "ings", 3);// Добавление n символов, pStr = pStr + "ing"
    size_t i = strlen(pStr);// Получение длины строки, i = 6
    strcmp(pStr, "string");// Сравнение строк, возвратит не 0 - разные строки
    stricmp(pStr, "string");// Сравнение без учёта регистра, возвратит 0
    // Сравнение первых n символов, также возвратит 0
    strncmp(pStr, "Strings", 6);
    char* pc = strchr(pStr, 'i');// Поиск первого вхождения, pc = pStr+3 = "ing"
    strcpy(pStr, "development");

    // Аналогично функции:
    strrchr// Поиск последнего вхождения
    strstr// Поиск первого вхождения строки
    strpbrk// Поиск первого вхождения одного из символов
    strspn// Число символов до любого символа из строки
    strcspn// Число символов до любого символа не из строки


Ну вот, на этом с Си строками можно закончить. Переходим к Си++. 

ЧАСТЬ 2. ПРОЦЕДУРНОЕ ПРОГРАММИРОВАНИЕ. ФУНКЦИИ, СТРУКТУРЫ, МАССИВЫ.

Вспоминая себя во времена моего изучения Си++, я часто удивляюсь, насколько логично проходило это изучение. На первых порах я брал чужие программы и пытался их модифицировать; классическая схема: сам пока ничего не можешь, но что-то творить хочется. Вскоре мне это, разумеется, надоело, я залез в книги и начал пробовать писать самостоятельно. Но как я писал - это заслуживает отдельного внимания! Я думаю, многие так начинали свой путь программиста...

Я не использовал классов. Я писал на Си++ лишь формально: в действительности же мои программы представляли собой набор функций - то, что даёт уже язык Си! Этот факт можно считать доказательством того, что начинать обучение Си++ следует не с классов, а с возможностей, составляющих фактически язык Си. Итак, процедурное программирование.

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

Итак, строение машин долгое время определяло их функции. Однако, за человеком давно замечена одна глупость: он всё пытается унифицировать. Иными словами, в один прекрасный день захотелось одному человеку объединить лопату и чайник в один предмет, причём так, чтобы можно было переключать их функциональность. Нажал кнопочку - чайник, нажал другую - лопата. Удобно!..

Так появились компьютеры. Да, это были, по сути своей, первые компьютеры. Устройство, которое следило за нажатием ваших кнопочек и переключением функций, фактически есть первая программа. Долго радовался человек на своё изобретение, однако и этому однажды пришёл конец. "Лучшее - враг хорошего" - говорят, и не зря...

Захотелось однажды человеку сделать так, чтобы можно было произвольно, в любой момент времени выбрать, как будет реагировать их машина, на нажатие той или иной кнопки. Что появилось? Правильно - перфокарты!.. Такие машины были уже полноценными компьютерами, потому как обладали не только постоянной памятью (коей являлась архитектура), но и переменной - перфокартами. Извините за столько затянувшееся бла-бла-бла, но теперь, мне кажется, вопросов вроде "что есть программа" должно не возникать - а это очень важно!..

Теперь посмотрим более современно. В любом компьютере существует исполнительное устройство (и часто - не одно), которое может выполнять фиксированный набор каких-то простых операций. Разберём это на примере нашего вездесущего чайника. Предположим, у нашей машины "автоматического чайника" есть следующий набор элементарных операций: открыть крышку, залить воду, включить нагреватель, выключить нагреватель, заорать "идиот, опять включил - и заснул!!!". Нам нужно составить несколько программ: кипичение воды без криков при окончании работы и с ними. Очевидно, что обе программы будут содержать блоки с одинаковыми наборами операций (открыть крышку, залить воду, включиться, выключиться) и только в конце второй программы будет приписано включение ора на всю ивановскую. "И что?" - скажем мы, "Не обломаемся продублировать эти одинаковые блоки!". Подождите, я же не договорил: у нас памяти на перфокарте только на шесть элементарных операций. Что делать будете?

Вот тут в дело вступают процедуры: мы обзываем наши первые четыре операции как "кипячение чайника" и прописываем отдельно, а в двух наших программах пишем только вызов этого блока: "закипятить чайник", "поорать". Функции отличаются от процедур только тем, что возвращают результат своих действий; например, после выполнения функции "закипятить чайник", можно куда-то записать, сколько времени происходило кипячение. Также, и функции и процедуры могут принимать параметры, например: "закипятить чайник водой с начальной температурой 20 градусов" - здесь параметром будет начальная температура; от начальных параметров, мы видим, будет меняться и возвращаемое значение - чем выше начальная температура, тем меньше времени потребуется на кипячение. А если мы пишем просто "закипятить чайник", то фактически мы предоставляем выбор начальной тепературы самой функции кипячения, мы не передаём параметра. Если функция способна сама выбрать это начальное значение при необходимости, то такой параметр называют параметром по умолчанию. Кстати, само разграничение понятий "процедура" и "функция" довольно условно и никто вас не имеет права щёлкнуть по носу, если вы вдруг употребить одно понятие взамен другого (хотя принято по возможности пользоваться вторым).

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

Придумали обзывать набор переменных одним именем и предоставлять возможность создавать сразу несколько переменных по заранее сформированному шаблону. Такой шаблон назвали структурой. Структура подобна штампу, который заметно облегчает жизнь бухгалтерам и юристам: им не приходится каждый раз писать, им необходимо просто припечатать штамп к листку и на последнем сразу появится нужная надпись, да ещё и в рамочке! Структуры - продвинутые штампы, они содержат в себе нечто, что можно изменять (некоторые штампы тоже содержат пустые поля, куда можно что-нибудь вписать). Это заранее заранее "непропечатанное" нечто и есть переменные, как вы должны были уже понять. Вы указываете только тип и набор переменных. Вот пример:
Код

struct Chaynik
{
    char model[255];// Модель
    int gab_x, gab_y, gab_z;// Габаритные размеры
    // Бла-бла-бла...
};

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

struct Chaynik ch1;// "Штампуем".. :-)

Переменная ch1 уже содержит в себе переменные model, gab_x и пр. Нам нужно только использовать специальный оператор, чтобы обратиться к ним:
Код

strcpy(ch1.model, "Bosch CH3000");
ch1.gab_x = 90, ch1.gab_y = 90, ch1.gab_z = 200;

Структуры также могут быть указываемы; и тогда используется другой оператор для доступа к содержимому структуры:
Код

struct Chaynik* pch1 = malloc(sizeof(struct Chaynik));
pch1->gab_x = 90, cph1->gab_y = 90, pch1->gab_z = 200;

struct Chaynik практически полноценный тип, такой же как int, double и char. Он не умеет только того, что не определено явно. К примеру, компилятор не знает, как ему прибавить к чайнику что-либо. Поэтому данная операция в процедурном программировании существовать не может:
Код

ch1 += 5;
ch1 += *pch1;// Тоже самое - каким образом это сделать?

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

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

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

int func(int* a, char* b)
{
    // ...
}

int func1(int a[], struct Chaynik b)
{
    // ...
}

int func2(int a[13], double b)
{
    // ...
}

Первые две функции с точки зрения ассемблера будут совершенно идеинтичным, различными у них будут только имена. Почему? Потому, что всё, что больше размера указателя (sizeof(int*)) обычно передаётся посредством указателя: сначала создаётся копия b, а потом в функцию отправляется только указатель на эту копию. int a[] и int* в Си же вообще идеинтичны, потому копия a создаваться не будет: вместо этого просто произойдёт передача указателя на первый элемент a. Казалось бы: где логика? На самом деле логика есть: когда мы объединили несколько переменных структурой в одну, мы тем самым сказали компилятору, чтобы он распространил на эту группу переменных теже правила, что действуют на обычные переменные (насколько это возможно, конечно). Одиночные переменные передаются "по значению" (то есть, передаётся не сам объект, а его копия - значение). Разделение же на int[] и int* в Си и Си++ чисто лексическое, компилятор интерпретирует и то однозначно за исключением случая создания переменной: тут указание константы в квадратных скобках явно свидетельствует об том, что будет создан статический массив. Также, было введено ещё одно удобное ограничение; взгляните на func2 - параметр a здесь будет передаваться точно также, как и в func и func1, но компилятор должен будет проверить, что вы передаёте ему массив именно из 13 элементов (разумеется, проверить это на этапе компиляции можно лишь при передаче статического объекта). Обычно правда компилятор языка Си разрешает нарушение этого правила и максимум - погрозит вам пальчиком при передаче указателя или статического объекта иного размера. В Си++ всё может быть значительно строже.

Вообще, передача параметров не устанавливается чётко ни стандартом Cи ни Си++: поведение может быть целиком и полностью определяемо компилятором. Но если вы не программируете микроконтроллеры или какие-нибудь экзотичные процессоры, вы вряд ли встретите серьёзные отступления от описанных правил.


 ! 
archimed7592
Любые просьбы решить задачку, помочь разобраться в проблеме и прочий оффтоп в этой теме будут неумолимо удаляться.
Для решения своих проблем создавайте отедльную тему.


Это сообщение отредактировал(а) archimed7592 - 29.12.2007, 13:38
PM MAIL WWW   Вверх
509harbore
Дата 21.9.2005, 20:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 2
Регистрация: 4.9.2005

Репутация: нет
Всего: нет



ManiaK Объясните мне разницу между указателеми и ссылками. smile
Насколько я понял из прочтения вашей статьи. Что указатель это переменная, которая содержит адрес другой переменой.
PM MAIL   Вверх
Гость_ManiaK
Дата 21.9.2005, 23:12 (ссылка)    |    (голосов: 0) Загрузка ... Загрузка ... Быстрая цитата Цитата


Unregistered











Цитата(509harbore @ 21.9.2005, 20:24)
Насколько я понял из прочтения вашей статьи. Что указатель это переменная, которая содержит адрес другой переменой.

Именно так.

Цитата(509harbore @ 21.9.2005, 20:24)
ManiaK Объясните мне разницу между указателеми и ссылками.

Ссылка - это просто синоним для переменной.

Код

int a = 13;
int& sa = a;
sa = 15;// Тоже самое, что и a = 15


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

Код

void func(int i)
{
    i = 15;
}

void main(void)
{
    int a = 13;
    func(a);// a в этом примере не изменяется!!!
}


В данном примере при передаче а в функцию func сначала создаётся копия a и она уже отдаётся на растерзание, в то время как сам исходный объект не меняется. А вот так можно явно указать компилятору, что нужно использовать сам объект (точнее - ссылку, хотя это одно и тоже), а не его копию:
Код

void func(int& i)
{
    i = 15;// Вот теперь a примет значение 15
}

}
  Вверх
maxim1000
Дата 22.9.2005, 00:56 (ссылка) |    (голосов:2) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник
Сообщений: 3334
Регистрация: 11.1.2003
Где: Киев

Репутация: 1
Всего: 110



ссылка ближе к указателю, чем к пременной
в общем-то именно так она и реализована на уровне компилятора
просто везде, где она используется, компилятор дорисовывает звездочку smile
Код

int x;
int &y=x;
y++;
x=y;

при компиляции будет сначала переделано в такое:
Код

int x;
int *z=&x;
(*z)++;
x=(*z);

поэтому, когда мы пишем
Код

y=x;

на самом деле получается
Код

(*z)=x;

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

единственное, чем отличается ссылка от указателя - инициализация:
Код

int &y=x;

означает занесение адреса x в y
и это навсегда (пока не выйдем из этого блока)...


--------------------
qqq
PM WWW   Вверх
509harbore
Дата 22.9.2005, 20:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 2
Регистрация: 4.9.2005

Репутация: нет
Всего: нет



ManiaK ,maxim1000 ,благодарю вас за столь хорошие отвтеты. smile
А для чего служит необходимость разыменования указателей? smile
PM MAIL   Вверх
Void
Дата 22.9.2005, 20:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


λcat.lolcat
****


Профиль
Группа: Участник Клуба
Сообщений: 2206
Регистрация: 16.11.2004
Где: Zürich

Репутация: 2
Всего: 173



Цитата(509harbore @ 22.9.2005, 22:32)
А для чего служит необходимость разыменования указателей

Для того, чтобы получить доступ к объекту, на который они указывают. А зачем они тогда нужны были бы? smile


--------------------
“Coming back to where you started is not the same as never leaving.” — Terry Pratchett
PM MAIL WWW GTalk   Вверх
BlHol
Дата 3.5.2006, 12:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 126
Регистрация: 26.11.2005

Репутация: нет
Всего: нет



Маленький вопрос:

Вот это понятно:

Код

TComponent *Obj;


Указатель на объект класса.


А вот это как понимать?:

Код

(TComponent *)Sender;



Заранее благодарен.
С уважением. 
PM MAIL   Вверх
ManiaK
Дата 30.5.2006, 16:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Homo Sapience
***


Профиль
Группа: Комодератор
Сообщений: 1145
Регистрация: 3.8.2004
Где: ИУ5-93

Репутация: 2
Всего: 29



BlHol, абсолютно никакой разницы нет. 
PM MAIL WWW   Вверх
champion
Дата 22.6.2006, 17:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 272
Регистрация: 26.1.2005

Репутация: нет
Всего: 2



Я правильно понял, что указатель это переменная, которая ссылается на адрес, по которому находится содержание переменной,  другой переменной?  И каков смысл применения указателей? Приведите пожалуйста в пример код, из которого было бы видно, где их используют. 
 В статье я увидел int *i=alloc(1000); что есть alloc?
 И все остальное из этой кухни, что приводится в пример? 


--------------------
user posted image
PM MAIL   Вверх
slava72
Дата 26.6.2006, 12:11 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 22
Регистрация: 23.6.2006

Репутация: нет
Всего: 1



2ManiaK: продолжения похоже так и не дождутся новички ))

Тем не менее тема, почему не стоит пользоваться C-style строками в С++ ИМХО очень важна.
Итак попытка продолжения - разрешается пользоватся без ограничений, в т.ч. копировать, редактировать, etc. (например вставить после редактирвания в начальную статью).

Строки в стиле С++

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

В С у нас есть только базовый набор функций над участками памяти, исходя из предположения,
что строка - это набор символов (char или wchar_t), заканчивающийся нулевым символом ('\0').

Основные недостатки этого подхода:
Мы должны заранее распределять память (неважно каким образом), достаточную для помещения в нее строки (+1 символ) при всех манипуляциях над ней.
Если память ваделяется динамически - не забыть ее вернуть системе.
Использовать в коде strcpy(a,b) вместо a+b, strcmp(a,b) < 0  вместо a < b и т.д., что затрудняет читабельность программы (и время написания ;)

Для избавления от этих недостатков в C++ введен стандартный класс std::string, с которым можно работать почти также как с обычным типом (например int), и не задумываться о распределении памяти - все за вас сделает компилятор.
Код

  typedef std::string MyStr; // для любителей

  MyStr str_list[100]; // обявляем массив строк
  MyStr prefix = "эта строка содержит\""; // объявляем строку и инициализируем ее
  MyStr suffix = "\";\n";
  str_list[0] = "А это 1-я строка массива";
  
  if(str_list[0] < suffix) { ... } // операция сравнения 2-х строк
  str_list[1] = prefix + str_list[0]; // конкатенация (объединение) 2-х строк
  str_list[1] += suffix; // то же самое, что str_list[1] = str_list[1] + suffix;
  ...
  str_list[99] = "the last";
  for(int i = 0; str_list[i] != "the last" ; ++i) {
    ...
  }
  const char* pc1 = str_list[3].c_str(); // получаем неизменяемую С-строку
  const char* pc2 = str_list[5].c_str();
  if((stricmp(pc1, pc2) == 0) {// проверяем равенство строк без учета регистра
  }

....
bool Find_stri(
  std::string dets&,      // изменяемый аргумент, прередается по ссылке
  const std::string src1,  // не изменяемый аргумент, прередается по ссылке
  std::string src2         // передача по значению - так не стоит делать
)
  


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

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

Поэтому при написании своих функций в списоке аргументов желательно:
- Для входных параметров объявлять аргумент как const string param&
- для выходных (изменяемых) - как string param&

Использование строк в качестве char* (C-строк) - можно для функций, который _не_ модифицируют
строки (с использованием c_str() см. код).
Если же требуется буфер обмена с изменяемой памятью - его придется создавать самостоятельно,
но зато потом можно будет присваивать строке.

Зы: навеяно вопросом 
PM MAIL   Вверх
ManiaK
Дата 24.7.2006, 10:01 (ссылка) |    (голосов:2) Загрузка ... Загрузка ... Быстрая цитата Цитата


Homo Sapience
***


Профиль
Группа: Комодератор
Сообщений: 1145
Регистрация: 3.8.2004
Где: ИУ5-93

Репутация: 2
Всего: 29



Цитата(champion @  22.6.2006,  17:37 Найти цитируемый пост)
В статье я увидел int *i=alloc(1000); что есть alloc?

Читаем внимательней, коментарии не зря ставил:
Код

int *i = alloc(1000);// [b]Хотим блок памяти в тысячу байт[/b]

Т.е.: данной функцией мы можем "попросить" у операционной системы дать нам память.

Цитата(champion @  22.6.2006,  17:37 Найти цитируемый пост)
Я правильно понял, что указатель это переменная, которая ссылается на адрес, по которому находится содержание переменной,  другой переменной?

Да. Причём не обязательно другой переменной. У вас в программе может существовать указатель на память, которой не соответствует ни одна переменная. Каким образом мы получим доступ к этой памяти? Только с помощью указателя на неё. Это ответ на следующий вопрос:
Цитата(champion @  22.6.2006,  17:37 Найти цитируемый пост)
И каков смысл применения указателей?


Цитата(champion @  22.6.2006,  17:37 Найти цитируемый пост)
Приведите пожалуйста в пример код, из которого было бы видно, где их используют. 

Пожалуйста! - 
Код

char* i = alloc(1000); // Получаем 1000 байт
FILE* file = fopen("myfile.txt", "rb");// Получаем указатель на данные файла
fread(i, 1000, 1, file);// Читаем из файла, на который указывает file 1 блок данных размером в 1000 байт


[оффтоп]
Подписался на тему, теперь буду отвечать во-время smile
[/оффтоп]

Добавлено @ 10:11 
Цитата(BlHol @  3.5.2006,  12:37 Найти цитируемый пост)
А вот это как понимать?:

Пардон, если вот так:

Код

int* Sender = alloc(1000);
TComponent * p = (TComponent *)Sender;


То это т.н. преобразование типов. Название страшное, но, как обычно, идея элементарная: в первой строке мы создаём переменную-указатель типом int, во второй строке - типом TComponent. Си++ отличается от Си тем, что довольно строго относится ко взаимодействиям объектов разного типа. Если бы мы написали вот так вторую строку:
Код

TComponent * p = Sender;

То компилятор заявил бы нам, мол, парень, очко протри: у тебя слева помидор, а справа огурец: ты их потом не перепутаешь? А то может у тебя алергия на помидоры, а ты съешь указатель на огурец, который на самом деле указывает на помидор.

Когда мы пишем перед объектом "(Type*)", мы фактически говорим компилятору: думай шо хошь, но тип этого объекта - указатель на Type. У меня всё под контролем, мол... 
PM MAIL WWW   Вверх
NightmareZ
Дата 2.11.2006, 13:28 (ссылка) |    (голосов:2) Загрузка ... Загрузка ... Быстрая цитата Цитата


[хакер]
**


Профиль
Группа: Участник
Сообщений: 699
Регистрация: 10.8.2006

Репутация: 0
Всего: 13



Цитата(Lokky @  31.8.2006,  00:51 Найти цитируемый пост)
1. enum (пречисления);

Код

enum MyEnum
{
   One, Two, Three
};

Таким образом определяем тип данных MyEnum, который может принимать три значения: OneTwo и Three.
При этом OneTwo и Three - это целочисленные константы.
Далее MyEnum можно использовать, например, так:
Код

MyEnum hello = One;

switch (hello)
{
  case One:
    cout << "One";
  break;
  case Two:
    cout << "Two";
  break;
  case Three:
    cout << "Three";
}


Цитата(Lokky @  31.8.2006,  00:51 Найти цитируемый пост)
2. struct (структуры) и чем они отличаются от enum;

Структуры же - это совокупность переменных, объединённых под одним именем. Если мы определим структуру вот так:
Код

struct MyStruct
{
  int One, Two, Three;
};

то сможем создавать переменные, содержащие в себе три значения (OneTwo и Three).
Обращаться к отдельным переменным внутри структуры нужно через точку:
Код

MyStruct hello;

hello.One = 10;


Цитата(Lokky @  31.8.2006,  00:51 Найти цитируемый пост)
3. union (объединения).

Объединения позволяют хранить переменные разного типа в одной области памяти.
Код

union MyUnion
{
    struct {unsigned char One, Two;} Four;
    unsigned short int Three;
};

MyUnion hello;
Three = 256;

Если unsigned short int занимает два байта, а unsigned char - один, то, после данного присваивания, переменная One будет содержать ноль, а Two - единицу.


--------------------
NightmareZ.net - мой блог и сайт, мои проекты и прочий трэш
Ely-Art.ru - наша маленькая домашняя арт-студия
mugcraft.ru - кружки на любой вкус
PM WWW ICQ Skype GTalk AOL YIM   Вверх
Frekenbok
Дата 10.11.2006, 06:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 51
Регистрация: 29.10.2006

Репутация: нет
Всего: 1



Со всеми этими обсуждениями вторая глава как-то осталась незамеченной. Или всем все понятно? Мне вот не все.
ManiaK, батенька, да вы философ! (я имею в виду чайники)  smile 
Вот мои вопросы:
  •  В структуре поля хранятся в соседних ячейках памяти наподобие массивов?
  •  
    Цитата(ManiaK @  7.11.2006,  15:29 Найти цитируемый пост)
    int func(int* a, char* b)

    Почему char* b? Потому что первое поле структуры типа char? 
    Почему тогда...
    Цитата(ManiaK @  7.11.2006,  15:29 Найти цитируемый пост)
    int func2(int a[13], double b)

    Или здесь во второй параметр передается уже не структура?
  •  Объясни, плиз, поподробнее разницу между "точками" и "стрелками" для доступа к полям классов (кстати, только к полям или к методам тоже?). В Delphi вот точка да и все. А здесь как? Или можно просто ссылочку полезную дать...



PM MAIL   Вверх
archimed7592
Дата 10.11.2006, 07:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Архимед
****


Профиль
Группа: Завсегдатай
Сообщений: 2531
Регистрация: 12.6.2004
Где: Moscow

Репутация: 6
Всего: 93



Цитата(Frekenbok @  10.11.2006,  07:05 Найти цитируемый пост)
 В структуре поля хранятся в соседних ячейках памяти наподобие массивов?
да в соседних ячейках, но только при выполнении определенных условий. точнее говоря, в общем случае между полями структуры могут быть "дырки"(маленькие - 1-7 байт).
но это не массивы. массив - это N элементов одного заданного типа. структура же может иметь много полей и все могут быть разных типов, в том числе и массивами. структура - это как мусорный контейнер (совсем не в плохом слысле, просто в голову больше ниче не приходит), а массив - как корзина для яиц.
кстати, помимо того, что структура может содержать массивы ещё не редко встречаются массивы из структур.

Цитата(Frekenbok @  10.11.2006,  07:05 Найти цитируемый пост)
Почему char* b? Потому что первое поле структуры типа char? 
вступление: передача параметров как правило производится через регистры (размер регистра 4 байта). если регистров не хватает, то передача параметров производится через стек.
ещё одно вступление: указатель - это почти в любом случае 4 байта - просто номер первого (нулевого) байта в памяти (для int - это номер младшего байта).
ответ: нет. ManiaK сказал, что с точки зрения ассемблера - это одинаковые ф-ции (лучше было сказать сигнатуры)...ф-ции то разные, но вот способ их вызова одинаковый. т. е. способ их вызова при хорошо сложившихся звёздах будет одинаковый. т. е., когда мы вызываем любую из этих ф-ций происходит следующее: первый аргумент кладётся в регистр1, второй - в регистр2. и в том и в другом случае второй аргумент - это указатель. в первом случае - на char, во втором - на Chaynik. но! с точки зрения ассемблера разницы между указателем на char и указателем на Chaynik нету. в первом случае всё понятно, а вот во втором: почему указатель, мы ж структуру передаем? вот собственно говоря та мысль, которую пытался донести ManiaK: при передаче малых объектов (до 4 байт) они передаются как есть т. е. их копия лежит в регистре, а для больших объектов делается следующее: создается копия на стеке и в регистре передается уже указатель, а не сам объект.

Цитата(Frekenbok @  10.11.2006,  07:05 Найти цитируемый пост)
Или здесь во второй параметр передается уже не структура?
во второй параметр передается не структура, но некий объект, размер которого превышает 4 байта (8 байт) и потому разницы между сигнатурами всех трех ф-ций не будет - в первой укзатель передается явно, в друих двух компилятор делает так, что ты не замечаешь как делается копия и неявно передается указатель на неё.

Цитата(Frekenbok @  10.11.2006,  07:05 Найти цитируемый пост)
Объясни, плиз, поподробнее разницу между "точками" и "стрелками" для доступа к полям классов. В Delphi вот точка да и все.
просто в делфи нет указателей на классы smile в делфи class - это и есть указатель на object (есть там такой, но про него мало кто знает smile) т. е. в делфи на уровне типов принимается решение каким образом получать доступ к объекту. в си это немного иначе. в си компилятор "ленится для твоего же блага" и не принимает решений на уровне типов. полагаю, что ты усвоил, что указатель - это номер байта в памяти, и чтобы получить эту самую память (на которую указывает указатель) нужно его прежде разыменовать: 
Код
int x = 5;
int *px = &x;
int y = *px; //<< звездочка перед px разыменовывает указатель и получает само значение x
дык вот. стрелочка - это просто упрощение синтаксиса: стрелочка - это два в одном
1. разыменовать
2. взять поле.
т. е.
Код
struct S
{
    int x;
    int y;
};

S s;

s.x = 0;
s.y = 0;

S *ps = &s;

ps->x = 5; // изменяем s.x - так проще
(*ps).y = 10; // изменяем s.y - так тоже просто, но смотрится не оч. красиво...

int result = s.x + s.y; // result = 15


Цитата(Frekenbok @  10.11.2006,  07:05 Найти цитируемый пост)
(кстати, только к полям или к методам тоже?)
к методам тоже. к любым членам.


--------------------
If you have an apple and I have an apple and we exchange apples then you and I will still each have one apple. But if you have an idea and I have an idea and we exchange these ideas, then each of us will have two ideas.
© George Bernard Shaw
PM Jabber   Вверх
Frekenbok
Дата 12.11.2006, 08:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 51
Регистрация: 29.10.2006

Репутация: нет
Всего: 1



archimed7592, еще раз, для особо одаренных...  smile 

Цитата(archimed7592 @  10.11.2006,  07:21 Найти цитируемый пост)
int x = 5;
int *px = &x;
int y = *px; //<< звездочка перед px разыменовывает указатель и получает само значение x


Насколько я понимаю, указатель должен указывать на уже существующий объект (переменную и т.п.) или на NULL. Возьмем для примера класс TButton (С++ Builder). Я получаю доступ к полям и методам кнопки через указатель? А "где" сам объект? Компилятор создает объект и указатель на него, а я работаю только с указателем?
PM MAIL   Вверх
Daevaorn
Дата 12.11.2006, 10:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2155
Регистрация: 29.11.2004
Где: Москва

Репутация: нет
Всего: 70



Цитата(Frekenbok @  12.11.2006,  09:51 Найти цитируемый пост)
Насколько я понимаю, указатель должен указывать на уже существующий объект (переменную и т.п.) или на NULL.

Указаетль указывает на область памяти. Куда именно ему всё равно. Просто если там не будет объекта - будут проблемы
Цитата(Frekenbok @  12.11.2006,  09:51 Найти цитируемый пост)
Я получаю доступ к полям и методам кнопки через указатель?

Да
Цитата(Frekenbok @  12.11.2006,  09:51 Найти цитируемый пост)
А "где" сам объект? 

Находится по адресу накоторой указывает указатель
Цитата(Frekenbok @  12.11.2006,  09:51 Найти цитируемый пост)
Компилятор создает объект и указатель на него

Создает поле в классе формы с типом "указатель на объект кнопка"
Цитата(Frekenbok @  12.11.2006,  09:51 Найти цитируемый пост)
а я работаю только с указателем? 

Да
PM MAIL WWW   Вверх
ManiaK
Дата 14.11.2006, 15:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Homo Sapience
***


Профиль
Группа: Комодератор
Сообщений: 1145
Регистрация: 3.8.2004
Где: ИУ5-93

Репутация: 2
Всего: 29



Цитата(Frekenbok @  12.11.2006,  08:51 Найти цитируемый пост)
Компилятор создает объект и указатель на него, а я работаю только с указателем?


Нифига подобного. В Си/Си++ компилятор вообще ничего сам не делает. ТЫ создаёшь указатель, ТЫ, если хочешь, создаёшь объект, ТЫ выбираешь с кем тебе работать - с указателем или с объектом. И, наконец, ТЫ выбираешь когда твой объект должен будет удалён из памяти. Ни один из этих пунктов не будет за тебя выполнен компилятором, если ты не создашь объект сам, а попытаешься обратиться к нему по указателю, то результат может быть самым неожиданным (в зависимости от того, что находится в данный момент времени в качестве значения указателя).
PM MAIL WWW   Вверх
archimed7592
Дата 14.11.2006, 23:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Архимед
****


Профиль
Группа: Завсегдатай
Сообщений: 2531
Регистрация: 12.6.2004
Где: Moscow

Репутация: 6
Всего: 93



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


--------------------
If you have an apple and I have an apple and we exchange apples then you and I will still each have one apple. But if you have an idea and I have an idea and we exchange these ideas, then each of us will have two ideas.
© George Bernard Shaw
PM Jabber   Вверх
Frekenbok
Дата 15.11.2006, 13:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 51
Регистрация: 29.10.2006

Репутация: нет
Всего: 1



Цитата(ManiaK @  14.11.2006,  15:00 Найти цитируемый пост)
ТЫ создаёшь указатель, ТЫ, если хочешь, создаёшь объект,

ManiaK,  ты имеешь ввиду, если Я кидаю компонент на форму (Builder), то Я и создаю объект и указатель? 


PM MAIL   Вверх
archimed7592
Дата 15.11.2006, 21:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Архимед
****


Профиль
Группа: Завсегдатай
Сообщений: 2531
Регистрация: 12.6.2004
Где: Moscow

Репутация: 6
Всего: 93



угу. в codebehind сможешь найти что-то вроде Label1 = new Label1 (...); хотя, я совсем не исключаю, что у билдера со своими замутами и __published не будет такой строки, но по сути объект именно создается...


--------------------
If you have an apple and I have an apple and we exchange apples then you and I will still each have one apple. But if you have an idea and I have an idea and we exchange these ideas, then each of us will have two ideas.
© George Bernard Shaw
PM Jabber   Вверх
Frekenbok
Дата 16.11.2006, 12:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 51
Регистрация: 29.10.2006

Репутация: нет
Всего: 1



как передать в функцию указатель на строку в качестве параметра-переменной (чтобы в функции он передвинулся и в main это было видно, но не глобальный указатель)? сама функция возвращает число (позиция указателя). 
PM MAIL   Вверх
archimed7592
Дата 16.11.2006, 12:58 (ссылка) |    (голосов:2) Загрузка ... Загрузка ... Быстрая цитата Цитата


Архимед
****


Профиль
Группа: Завсегдатай
Сообщений: 2531
Регистрация: 12.6.2004
Где: Moscow

Репутация: 6
Всего: 93



Код

int func (char *&ptr)
{
ptr += strlen (ptr) > 5 ? 5 : strlen (ptr);
}

main ()
{
char *p = "abcdefgh", p2 = "abc";
func (p); func (p2);
printf ("%s\n%s\n", p, p2);
}



--------------------
If you have an apple and I have an apple and we exchange apples then you and I will still each have one apple. But if you have an idea and I have an idea and we exchange these ideas, then each of us will have two ideas.
© George Bernard Shaw
PM Jabber   Вверх
ManiaK
Дата 17.11.2006, 09:49 (ссылка)  | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Homo Sapience
***


Профиль
Группа: Комодератор
Сообщений: 1145
Регистрация: 3.8.2004
Где: ИУ5-93

Репутация: 2
Всего: 29



C языке Си придётся пользоваться указателем на указатель.

Добавлено @ 09:56 
Цитата(Frekenbok @  15.11.2006,  13:14 Найти цитируемый пост)
ManiaK,  ты имеешь ввиду, если Я кидаю компонент на форму (Builder), то Я и создаю объект и указатель? 

Я вообще про формы ничего не говорил. Если ты кидаешь компонент на форму - то, что там произойдёт зависит от компилятора, на уровне языка у тебя ещё ничего не будет. Я имел ввиду вот что:

Код

int* pi;// ТЫ создаёшь указатель
pi = new int;// ТЫ создаёшь объект
delete pi;// ТЫ удаляешь объект


Компилятор за тебя ни то, ни другое, ни третье делать не будет.

Код

int* pi;// создали указатель
*pi = 10;// Фигу! объекта-то ещё нет!


Вот что я, собственно, хотел сказать. Если есть какие-то непонимания в понятиях объект/указатель в Си/Си++ - то это уже вопрос к первой статье, по которой, покуда все молчат, я посчитал, всё понятно.

Цитата(archimed7592 @  14.11.2006,  23:52 Найти цитируемый пост)
ты забыл сказать, что это касается динамических объектов.

Да, конечно. Для статических нам нужно только изъявить желание и мы тут же получим объект и нам не нужно будет следить за тем, чтобы память им занимаемая где-нибудь не "улетела". Но это всё вроде было уже описано в первой статье. Если что-то непонятно - я поправлю. Тока скажите.
PM MAIL WWW   Вверх
Frekenbok
Дата 18.11.2006, 05:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 51
Регистрация: 29.10.2006

Репутация: нет
Всего: 1



Цитата(archimed7592 @  16.11.2006,  12:58 Найти цитируемый пост)
int func (char *&ptr)

archimed7592, спасибо, функция у меня есть. Интересовала именно передача параметра char *&ptr. Как-то еще не приходилось этим сочетанием пользоваться smile
PM MAIL   Вверх
archimed7592
Дата 18.11.2006, 16:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Архимед
****


Профиль
Группа: Завсегдатай
Сообщений: 2531
Регистрация: 12.6.2004
Где: Moscow

Репутация: 6
Всего: 93



Цитата(Frekenbok @  18.11.2006,  06:07 Найти цитируемый пост)
 Интересовала именно передача параметра char *&ptr
вот тебе и передача...оттуда же
Цитата(archimed7592 @  16.11.2006,  13:58 Найти цитируемый пост)
char *p = "abcdefgh";
func (p);




--------------------
If you have an apple and I have an apple and we exchange apples then you and I will still each have one apple. But if you have an idea and I have an idea and we exchange these ideas, then each of us will have two ideas.
© George Bernard Shaw
PM Jabber   Вверх
drLans
Дата 2.12.2006, 15:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 22
Регистрация: 11.8.2006

Репутация: нет
Всего: нет



Люди поясните, плз, в чем отличие динамического выделения памяти от обычного задания того же массива в тексте программы?
PM MAIL   Вверх
Partizan
Дата 2.12.2006, 17:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Let's do some .NET
****


Профиль
Группа: Модератор
Сообщений: 2828
Регистрация: 19.12.2005
Где: Санкт-Петербург

Репутация: 2
Всего: 67



Цитата(drLans @ 2.12.2006,  15:56)
Люди поясните, плз, в чем отличие динамического выделения памяти от обычного задания того же массива в тексте программы?

разные области памяти задействуются...
при объявлении массив в тексте программы в compile-time память под него выделяется в стэке...
динамическая память выделяется из кучи...


--------------------
СУВ,
       Partizan.
PM MAIL WWW ICQ Skype GTalk Jabber   Вверх
nickless
Дата 2.12.2006, 17:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Гентозавр
****


Профиль
Группа: Участник Клуба
Сообщений: 2976
Регистрация: 29.8.2005
Где: Germany

Репутация: 2
Всего: 181



Цитата(drLans @ 2.12.2006,  14:56)
Люди поясните, плз, в чем отличие динамического выделения памяти от обычного задания того же массива в тексте программы?

А еще динамически выделенную память нужно не забывать освобождать 

Это сообщение отредактировал(а) nickless - 2.12.2006, 17:36


--------------------
user posted image

Real men don't use backups, they post their stuff on a public ftp server and let the rest of the world make copies
- Linus Torvalds
PM MAIL   Вверх
drLans
Дата 2.12.2006, 17:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 22
Регистрация: 11.8.2006

Репутация: нет
Всего: нет



Partizan
так стек по идее быстрее.  smile 


Daevaorn
не могу пока.  smile 
Ибо не вдавался в особенности (и главное смысл) использования new/delete.

Я не понимаю, что может быть лучше этого:
Код

int massiv[] = {1,2,3};

PM MAIL   Вверх
Daevaorn
Дата 2.12.2006, 18:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2155
Регистрация: 29.11.2004
Где: Москва

Репутация: нет
Всего: 70



Цитата(drLans @  2.12.2006,  18:54 Найти цитируемый пост)
так стек по идее быстрее.  

Гораздо.
Цитата(drLans @  2.12.2006,  18:54 Найти цитируемый пост)
Я не понимаю, что может быть лучше этого:

А когда не знаешь зарание количество элементов? Тут то и пригождается динамическое выделение.
PM MAIL WWW   Вверх
maxim1000
Дата 2.12.2006, 19:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник
Сообщений: 3334
Регистрация: 11.1.2003
Где: Киев

Репутация: 1
Всего: 110



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

если вернуться к C++, то ещё одно достоинство (которое уже упоминали) - автоматическое удаление выделенного объекта при выходе из блока, так что забыть это сделать невозможно

лично я стараюсь по минимуму использовать динамическую память и другое выделение ресурсов, которые потом нужно явно освобождать, только когда без этого сложно обойтись (как уже привели пример с неизвестным количеством элементов) или это сильно повышает эффективность (когда она критична), но даже в этих случаях (особенно когда программа сложная) не помешает упаковать управление этими ресурсами в какой-нибудь класс, который будет следить за ними (для всяких массивов чаще всего подходит std::vector)...


--------------------
qqq
PM WWW   Вверх
UnrealMan
Дата 2.12.2006, 20:39 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 722
Регистрация: 30.3.2006

Репутация: 5
Всего: 32



Цитата(drLans @  2.12.2006,  15:56 Найти цитируемый пост)
Люди поясните, плз, в чем отличие динамического выделения памяти от обычного задания того же массива в тексте программы? 

А под статически объявленный массив память тоже может выделяться динамически (если он объявлен как член класса и память под объект такого класса выделяется динамически).

По поводу отличий ничего говорить не буду, ибо вопрос – суть боян. В И-нете полно литературы – читай и просвещайся. Всё равно по-нормальному научиться чему-то без книг, задавая подобные вопросы на форумах, вряд ли получится.

Б. Страуструп: «Язык программирования С++ (2-е дополненное издание)» 3.2.6 Свободная память.

Цитата(maxim1000 @  2.12.2006,  19:46 Найти цитируемый пост)
при использовании стека если объект1 создан раньше объекта2, то уничтожен он будет позже

Необязательно. Из-за оптимизации это правило может быть нарушено, например:

Код
struct A
{
    A() { static int i; cout << "A()  a" << (n = ++i) << endl; }
    ~A() { cout << "~A() a" << n << endl; }
    int n;
};

A func()
{
    A a1;
    A a2;
    return a2;
}

int main()
{
    A a = func();
}

вывод (MinGW-3.4.4):

Код
A()  a1
A()  a2
~A() a1
~A() a2

– т.е. объект a2 функции func может быть создан прямо на месте объекта a в функции main и уничтожен только по завершении main.
PM MAIL   Вверх
ali01
Дата 24.8.2007, 04:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 5
Регистрация: 29.7.2007

Репутация: нет
Всего: нет



А где же про классы? есть вопрос если в классе создаются динамические переменные, в деструкторе надо вызывать delete?
PM MAIL   Вверх
bsa
Дата 24.8.2007, 09:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 85
Всего: 196



Цитата(ali01 @ 24.8.2007,  04:52)
А где же про классы? есть вопрос если в классе создаются динамические переменные, в деструкторе надо вызывать delete?

Если где-то сделан new, то обязательно должен быть выполнен соответствующий delete. Иначе никак. И не важно где, в классах, структурах или функциях.
PM   Вверх
ali01
Дата 24.8.2007, 21:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 5
Регистрация: 29.7.2007

Репутация: нет
Всего: нет



Деструктор сам этого не делает? А когда автоматически создаешь класс например в VC там создается пустой конструктор, я так понял надо самому там прописывать delete?
PM MAIL   Вверх
Daevaorn
Дата 24.8.2007, 21:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2155
Регистрация: 29.11.2004
Где: Москва

Репутация: нет
Всего: 70



Цитата(ali01 @  24.8.2007,  22:24 Найти цитируемый пост)
Деструктор сам этого не делает? 

нет. delete он не вызывает для динамических объектов.
Цитата(ali01 @  24.8.2007,  22:24 Найти цитируемый пост)
А когда автоматически создаешь класс например в VC там создается пустой конструктор, я так понял надо самому там прописывать delete? 

по логике скорей всего не в конструктор, а в деструктор.
PM MAIL WWW   Вверх
ali01
Дата 24.8.2007, 23:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 5
Регистрация: 29.7.2007

Репутация: нет
Всего: нет



Да, запутался. Я понял, спасибо. Раньше думал деструктор сам все сделаетsmile
PM MAIL   Вверх
ali01
Дата 2.9.2007, 03:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 5
Регистрация: 29.7.2007

Репутация: нет
Всего: нет



Цитата(bsa @ 24.8.2007,  09:35)
Цитата(ali01 @ 24.8.2007,  04:52)
А где же про классы? есть вопрос если в классе создаются динамические переменные, в деструкторе надо вызывать delete?

Если где-то сделан new, то обязательно должен быть выполнен соответствующий delete. Иначе никак. И не важно где, в классах, структурах или функциях.

Просматривал код wxFormBuilder, пример конструктора: 
Код

MainFrame::MainFrame(wxWindow *parent, int id)
: wxFrame(parent,id,wxT("wxFormBuilder v.0.1"),wxDefaultPosition,wxSize(1000,800))
{

        wxIcon ico;
        ico.CopyFromBitmap( AppBitmaps::GetBitmap(wxT("app")));
        SetIcon( ico );

        wxString date(wxT(__DATE__));
        wxString time(wxT(__TIME__));
        SetTitle(wxT("wxFormBuilder (Build on ") + date +wxT(" - ")+ time + wxT(")"));

        SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));

        wxMenu *menuFile = new wxMenu;
        menuFile->Append(ID_NEW_PRJ, wxT("&New"), wxT("create an empty project"));
  
[skip]

        wxMenu *menuEdit = new wxMenu;
        menuEdit->Append(ID_UNDO, wxT("&Undo \tCtrl+Z"), wxT("Undo changes"));

[skip]

        wxMenu *menuHelp = new wxMenu;
        menuHelp->Append(ID_ABOUT, wxT("&About...\tF1"), wxT("Show about dialog"));


        // now append the freshly created menu to the menu bar...
        wxMenuBar *menuBar = new wxMenuBar();
        menuBar->Append(menuFile, wxT("&File"));
        menuBar->Append(menuEdit, wxT("&Edit"));
        menuBar->Append(menuHelp, wxT("&Help"));

        // ... and attach this menu bar to the frame
        SetMenuBar(menuBar);
        wxBoxSizer *top_sizer = new wxBoxSizer(wxVERTICAL);

        ///////////////

        wxSplitterWindow *v_splitter = new wxSplitterWindow(this,-1,wxDefaultPosition,wxDefaultSize, wxSP_3DSASH | wxSP_LIVE_UPDATE);
        wxPanel *left = new wxPanel(v_splitter,-1);//,wxDefaultPosition, wxDefaultSize,wxSIMPLE_BORDER);
        wxBoxSizer *left_sizer = new wxBoxSizer(wxVERTICAL);

        wxPanel *right = new wxPanel(v_splitter,-1);
        v_splitter->SplitVertically(left,right,300);

        wxSplitterWindow *h_splitter = new wxSplitterWindow(left,-1,wxDefaultPosition,wxDefaultSize, wxSP_3D | wxSP_LIVE_UPDATE);//wxSP_BORDER);

        wxPanel *tree_panel = new wxPanel(h_splitter,-1);
        Title *tree_title = new Title(tree_panel,wxT("Object Tree"));

[skip]

        toolbar->Realize();

        SetSizer(top_sizer);
//      top_sizer->SetSizeHints(this);
        SetAutoLayout(true);
        Layout();
        Fit();

        //SetSize(wxSize(1000,800));
        RestorePosition(wxT("mainframe"));
        //Centre();
        Refresh();

        // añadimos el manejador de las teclas rápidas de la aplicación
        // realmente este es el sitio donde hacerlo ?????
        //m_objTree->AddCustomKeysHandler(new CustomKeysEvtHandler(data));
        AppData()->AddHandler( this->GetEventHandler() );
};



и деструктора:
Код


MainFrame::~MainFrame()
{/*
 #ifdef __WXFB_DEBUG__
 wxLog::SetActiveTarget(m_old_log);
 m_log->GetFrame()->Destroy();
 #endif //__WXFB_DEBUG__
 */

        // Eliminamos los observadores, ya que si quedara algún evento por procesar
        // se produciría un error de acceso no válido debido a que los observadores
        // ya estarían destruidos

        AppData()->RemoveHandler( this->GetEventHandler() );
}


Там вообще нет delete. Или может я что-то не понял? 
PM MAIL   Вверх
archimed7592
Дата 2.9.2007, 03:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Архимед
****


Профиль
Группа: Завсегдатай
Сообщений: 2531
Регистрация: 12.6.2004
Где: Moscow

Репутация: 6
Всего: 93



ali01, посмотри деструкторы базовых классов. Там(чуть ли не на самом верху иерархии) должны быть примерно следующие строки:
Код
for (SomeContainer::iterator i = children.begin(), end = children.end(); i != end; ++i)
    delete *i;

Во всяком случае в известных мне тулкитах(Qt, VCL) все компоненты хранятся и удаляются единообразно(т.о. позволяя не заботиться о MM... хорошо это или плохо - вопрос другой темы).


--------------------
If you have an apple and I have an apple and we exchange apples then you and I will still each have one apple. But if you have an idea and I have an idea and we exchange these ideas, then each of us will have two ideas.
© George Bernard Shaw
PM Jabber   Вверх
archimed7592
Дата 28.12.2007, 15:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Архимед
****


Профиль
Группа: Завсегдатай
Сообщений: 2531
Регистрация: 12.6.2004
Где: Moscow

Репутация: 6
Всего: 93




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

Спасибо zkv за то, что подчистил тему :).


Это сообщение отредактировал(а) archimed7592 - 8.1.2008, 16:02


--------------------
If you have an apple and I have an apple and we exchange apples then you and I will still each have one apple. But if you have an idea and I have an idea and we exchange these ideas, then each of us will have two ideas.
© George Bernard Shaw
PM Jabber   Вверх
anatox91
Дата 27.1.2008, 19:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


программист-самоучка
**


Профиль
Группа: Участник
Сообщений: 699
Регистрация: 12.1.2008
Где: ++Украина.Крым++

Репутация: 1
Всего: 13



ManiaK, большое спасибо за интересную статью и своеобразный стиль изложения  smile 

Тема разделена: Возвращение указателя на локальный объект, warning C4172: returning address of local variable or temporary.

Это сообщение отредактировал(а) archimed7592 - 3.2.2008, 00:52


--------------------

The code is the design ©

Sony VAIO VGN-FW480J

user posted image
PM MAIL ICQ   Вверх
Annuta
Дата 27.8.2008, 15:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 174
Регистрация: 3.10.2006
Где: Dubna

Репутация: нет
Всего: 1



У меня такой вопрос - есть ли разница между этими двумя объявлениями. Какое из них более рационально  ?
TMinuit *min = new TMinuit(6);    
TMinuit min(6);
--------------------
Программист - это комбинация лени и логики !
PM MAIL   Вверх
bsa
Дата 27.8.2008, 15:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 85
Всего: 196



Цитата(Annuta @ 27.8.2008,  15:04)
У меня такой вопрос - есть ли разница между этими двумя объявлениями. Какое из них более рационально  ?
TMinuit *min = new TMinuit(6);    
TMinuit min(6);

Второе.
Если есть желание обсудить этот вопрос, то лучше создать отдельную тему
PM   Вверх
XeS
Дата 30.8.2008, 01:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 16
Регистрация: 6.4.2008

Репутация: нет
Всего: нет



Маленький вопросик по работе с указателями

Код

int* p[10];


это как я понимаю массив из 10 указателей, т.е я с ним работаю так

Код

p[0] = new int;
p[1] = new int;
delete p[0];
delete p[1];


а конструкция такого вида

Код

int (*p)[10]


это указатель на массив из 10 int

я с ним работаю так

Код

p = new int;
p[0] = 1;
p[1] = 2;
delete p;


я прав????
PM MAIL   Вверх
J0ker
Дата 14.10.2008, 03:11 (ссылка)  | (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 986
Регистрация: 17.9.2008

Репутация: 9
Всего: 14



Цитата(XeS @  30.8.2008,  01:12 Найти цитируемый пост)
я прав???? 

нет
можно так:
Код

int (*p)[10];
p = reinterpret_cast<int(*)[10]>(new int[10]);
p[0] = 1;
p[1] = 2;
delete[] p;


технически int* и int(*)[n] - разные типы, и данная запись несколько бессмысленна... ну разве что вам надо передавать p в функцию принимающую конкретно int(*)[n]
но той-же функциональностью обладает следующий код:
Код

int *p = new int[10];
p[0] = 1;
p[1] = 2;
delete[] p;



--------------------
user posted image
PM MAIL   Вверх
UnrealMan
Дата 16.10.2008, 13:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 722
Регистрация: 30.3.2006

Репутация: 5
Всего: 32



Цитата(J0ker @  14.10.2008,  04:11 Найти цитируемый пост)
можно так

Проще так:

Код
p = new int[1][10];

PM MAIL   Вверх
boombasta
Дата 30.10.2008, 12:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 12
Регистрация: 23.10.2008

Репутация: нет
Всего: нет



привет всем!

что-то немогу найти ответов на 2 моих вопроса:
1. че отличается 
Код
CoreInterface* _core;

от
Код
CoreInterface *_core;


2. что здесь написано:
Код
getObject = (PluginInterface*(*)(CoreInterface*)) lib->resolve("getObject");

что за конструкция такая PluginInterface*(*)??

3. и здесь 
Код
PluginInterface* (*getObject)(CoreInterface*);

тут типа обьявление переменной getObject но не понятно что за (CoreInterface*)?

З.Ы. PluginInterface и CoreInterface - это классы
PM MAIL   Вверх
bsa
Дата 30.10.2008, 16:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 85
Всего: 196



1. ничем не отличается.
2 и 3. Это тип указателя на функцию, которая принимает указатель на CoreInterface и возвращает указатель на PluginInterface. Соответственно, PluginInterface* (*getObject)(CoreInterface*) - это указатель на функцию.

Я, например, всегда делаю так, чтобы не вводить никого в замешательство:
Код
typedef PluginInterface* (*GetObjectProcPtr)(CoreInterface*); //создаем новый тип "указатель на функцию"
GetObjectProcPtr getObject; //определяем указатель этого типа

PM   Вверх
boombasta
Дата 30.10.2008, 17:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 12
Регистрация: 23.10.2008

Репутация: нет
Всего: нет



Код
PluginInterface* (*getObject)(CoreInterface*);    
getObject = (PluginInterface*(*)(CoreInterface*)) lib->resolve("getObject");

со строкой 1 понятно (спсб bsa ;)) обьявляется указательна функцию, которая в качестве аргумента принимает указатель на CoreInterface, а возвращает указатель на PluginInterface.

а что происходит в строке 2?
насколько я понимаю lib->resolve("getObject") возвражает ссылку на функцию?
а что такое вот это: (PluginInterface*(*)(CoreInterface*)) ? - это жжет мне мозг  smile  как-то это можно переписать по проще - пусть больше строк, но как-то понятнее smile (или хотябы просто по-другому, может так будет понятнее smile)

дольше по тексту getObject юзается как обычная функция:
PluginInterface* getObject(CoreInterface*);
PM MAIL   Вверх
bsa
Дата 30.10.2008, 18:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 85
Всего: 196



Цитата(boombasta @ 30.10.2008,  17:30)
а что такое вот это: (PluginInterface*(*)(CoreInterface*)) ? - это жжет мне мозг  smile

Это преобразование к типу "указатель на функцию" - PluginInterface* (*)(CoreInterface*).
Что делать для того, чтобы было понятно я показал:
Код
typedef PluginInterface* (*GetObjectProcPtr)(CoreInterface*); //создаем новый тип "указатель на функцию"
GetObjectProcPtr getObject; //определяем указатель этого типа
getObject = (GetObjectProcPtr) lib->resolve("getObject");


Вообще-то обобщенно тип "указатель на функцию" выглядит так: <return_type> (*)(<arg1_type>, <arg2_type>, ...)
Сам указатель (переменная типа "указатель на функцию") определяется так: <return_type> (*<var_name>)(<arg1_type>, <arg2_type>, ...)

P.S.: почитай книжку для начинающих
PM   Вверх
Goliaf777
Дата 2.12.2008, 17:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 70
Регистрация: 19.11.2008

Репутация: нет
Всего: нет



Вопрос.Почему нужно  using namespace std если в приводимых кодах на форуме пишется std::cout.Объясните ЧаЙнИгУ!!))) smile 
PM MAIL ICQ Skype Jabber   Вверх
bsa
Дата 2.12.2008, 20:42 (ссылка) |    (голосов:2) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 85
Всего: 196



Цитата(Goliaf777 @ 2.12.2008,  17:14)
Вопрос.Почему нужно  using namespace std если в приводимых кодах на форуме пишется std::cout.Объясните ЧаЙнИгУ!!))) smile

по-хорошему, надо писать std::cout, std::cin, std::cerr, std::endl... Но если тебе лень и пишешь маленькую программу, которую не собираешься в дальнейшем улучшать, то можно написать после всех инклюдов using namespace std, и это избавит тебя от необходимости писать приставку std::. Если у тебя нет желания писать std, но не хочется глобально раскрывать стандартное пространство имен, то можешь использовать using namespace std внутри функции (точнее, внутри почти любых операторных скобок). Как вариант, можно раскрыть только необходимые элементы пространства имен:
Код
using std::cout;
using std::endl;
После этого можно использовать cout и endl без указания std::.
PM   Вверх
Goliaf777
Дата 2.12.2008, 21:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 70
Регистрация: 19.11.2008

Репутация: нет
Всего: нет



Огромное спасибо!!! smile 
PM MAIL ICQ Skype Jabber   Вверх
XeS
Дата 20.4.2009, 23:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 16
Регистрация: 6.4.2008

Репутация: нет
Всего: нет



Помогите разобраться, почему не меняеться адрес переменной, допустим есть такой код:

Код

void f(int* b)
{
      int* i = new int;
      
      *i = 321;

      b = i;
}

int main()
{
       int* a = new int;
       
       *a = 123;
      
       f(a);

       printf("%a", *a);

}


на экране будет 123, а не 321, а если я перепешу код вот так то все будет норм:

Код

void f(int** b)
{
      int* i = new int;
      
      *i = 321;

      *b = i;
}

int main()
{
       int* a = new int;
       
       *a = 123;
      
       f(&a);

       printf("%a", *a);

}


с чем это связано и почему? smile

и второй вопрос:

пусть у меня есть переменные такого типа:

int*** a;
int* b;

что бы привести b к типу a, я должен сделать такое:

int*** a;
int** c;
int* b;

c = &b;
a = &c;

можно ли это сделать по другому, что-бы меньше писать? smile
PM MAIL   Вверх
bsa
Дата 21.4.2009, 13:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 85
Всего: 196



XeS
Код
void f(int* b) //b - копия это указателя, который ты передаешь в качестве аргумента
{
      int* i = new int;
      *i = 321;
      b = i;
}
Чтобы можно было изменить аргумент, то нужно сделать b ссылкой:
Код
void f(int* &b) //только для С++!
{
      int* i = new int;
      *i = 321;
      b = i;
}
или так:
Код
void f(int* *b) //вызывать надо так: int *a = new int; f(&a);
{
      int* i = new int;
      *i = 321;
      *b = i;
}
Цитата(XeS)
пусть у меня есть переменные такого типа:

int*** a;
int* b;

что бы привести b к типу a...
По хорошему, не нужно приводить b к типу a! Но если так неймется, то только тем способом, что сам ты и написал. Только это не приведением "типов". Потому что фактически ты не передаешь значение b. Ты передаешь указатель на переменную, которая кодержит указатель на b;
Обычно, такие конструкции можно встретить в качестве параметров функций (когда функция может изменить значение аргумента) или для определения многомерных динамических массивов.

Это сообщение отредактировал(а) bsa - 21.4.2009, 13:15
PM   Вверх
master123
Дата 1.5.2009, 20:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 5
Регистрация: 13.4.2009

Репутация: нет
Всего: нет



Спс... Мне помогло
________________________________
http://linecinema.ru/

Это сообщение отредактировал(а) master123 - 1.5.2009, 20:10
PM MAIL   Вверх
XAKERs89
Дата 26.8.2009, 20:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 2
Регистрация: 24.8.2009

Репутация: нет
Всего: нет



можно ли создать массив структур с помощью указателей?
Код

struct date *y=new struct date[3];
 

Это сообщение отредактировал(а) XAKERs89 - 26.8.2009, 21:13
PM MAIL   Вверх
andrew_121
Дата 26.8.2009, 21:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Кодофей
****


Профиль
Группа: Завсегдатай
Сообщений: 3448
Регистрация: 3.1.2008

Репутация: 10
Всего: 33



XAKERs89, Ссылок? Или указателей?
Уточните вопрос.


--------------------
Удалил аккаунт. Прощайте!
PM MAIL   Вверх
XAKERs89
Дата 26.8.2009, 21:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 2
Регистрация: 24.8.2009

Репутация: нет
Всего: нет



оЙ..сори запутался (((  указателей
PM MAIL   Вверх
andrew_121
Дата 26.8.2009, 21:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Кодофей
****


Профиль
Группа: Завсегдатай
Сообщений: 3448
Регистрация: 3.1.2008

Репутация: 10
Всего: 33



Код

struct date **y=new struct date*[3];




--------------------
Удалил аккаунт. Прощайте!
PM MAIL   Вверх
ПанкПрогаммист
Дата 25.9.2009, 13:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 1
Регистрация: 25.9.2009

Репутация: нет
Всего: нет



Нефига не понял)) сушствует книга с++ для чаиников??  smile 
PM MAIL   Вверх
bsa
Дата 25.9.2009, 13:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 85
Всего: 196



ПанкПрогаммист, попробуй тут посмотреть: Часто задаваемые вопросы
PM   Вверх
Axsandr
Дата 28.3.2010, 18:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 2
Регистрация: 23.3.2010

Репутация: нет
Всего: нет



Цитата(ПанкПрогаммист @ 25.9.2009,  13:42)
Нефига не понял)) сушствует книга с++ для чаиников??  smile

язык программирования С++ Бьерн Страуструп 
В принципе очень доступно все написано....


_____________________
Для тех, кто интересуется forex есть выгодные условия торговли.


Это сообщение отредактировал(а) Axsandr - 29.3.2010, 13:28
PM MAIL   Вверх
EgoBrain
Дата 26.6.2012, 12:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 537
Регистрация: 23.3.2008
Где: Комната

Репутация: нет
Всего: 2



Цитата(maxim1000 @ 22.9.2005,  00:56)
ссылка ближе к указателю, чем к пременной
в общем-то именно так она и реализована на уровне компилятора
просто везде, где она используется, компилятор дорисовывает звездочку smile
...

А что происходит при использовании ссылки на сложные типы данных, например структуры?
Компилятор меняют ссылку на указатель, и вместо оператора "." вставляет оператор "->"?
PM MAIL ICQ Skype   Вверх
baldina
Дата 26.6.2012, 12:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 15
Всего: 101



ничего он не меняет. maxim1000 имел в виду семантику, связанную с реализацией, но не саму реализацию.
впрочем, если считать, что в ассемблерном виде может (и скорее всего будет) использоваться косвенная адресация, то можно и так сказать
PM MAIL   Вверх
kolesnle
Дата 3.6.2013, 18:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Упертый сишник
*


Профиль
Группа: Участник
Сообщений: 93
Регистрация: 22.3.2013

Репутация: нет
Всего: нет



Цитата(EgoBrain @  26.6.2012,  12:07 Найти цитируемый пост)
Компилятор меняют ссылку на указатель, и вместо оператора "." вставляет оператор "->"? 

Код

(*pos).y ...

PM MAIL   Вверх
rvpxbnkx
Дата 18.7.2013, 16:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 1
Регистрация: 18.7.2013

Репутация: нет
Всего: нет




Поясните пожалуйста. Программа на си. Я так понимаю массив это указатель на первый элемент + число элементов. В примере char  *dic[][40] получается трехмерный массив. Потом он приобразуется с p = (char **)dic;
Вот я и не понимаю почему трехмерный массив станет 2-х мерным. И еще Не совсем понятно чем такая конструкция char (*p)[10]; отличается от char *s[]

Код
/* Простой словарь. */
#include <stdio.h>
#include <string.h>
#include <ctype.h>

/* список слов и их значений */
char  *dic[][40] = {
  "атлас", "Том географических и/или топографических карт.",
  "автомобиль", "Моторизоравонное средство передвижения.",
  "телефон", "Средство связи.",
  "самолет", "Летающая машина.",
  "", ""  /* нули, завершающие список */
};

int main(void)
{
  char word[80], ch;
  char **p;

  do {
    puts("\nВведите слово: ");
    scanf("%s", word);

    p = (char **)dic;

    /* поиск слова в словаре и вывод его значения */
    do {
      if(!strcmp(*p, word)) {
        puts("Значение:");
        puts(*(p+1));
        break;
      }
      if(!strcmp(*p, word)) break;
      p = p + 2;  /* продвижение по списку */
    } while(*p);
    if(!*p) puts("Слово в словаре отсутствует.");
    printf("Будете еще вводить? (y/n): ");
    scanf(" %c%*c", &ch);
  } while(toupper(ch) != 'N');

  return 0;
}

PM MAIL   Вверх
bsa
Дата 29.7.2013, 17:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 85
Всего: 196



char *dic[][40] массив массивов по 40 указателей на char. char ** - это указатель на указатель на char. Т.е. когда идет это преобразование ты отказываешься от одного измерения.
Обычно, подобные вещи делают так: const char *dic[] = { ... };

А вообще, почитай про указатели и ссылки еще тут.
PM   Вверх
eses
Дата 30.7.2013, 13:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 1
Регистрация: 8.10.2012

Репутация: нет
Всего: нет




Модератор: Сообщение скрыто.

PM MAIL WWW   Вверх
gendalf7771
Дата 22.11.2013, 01:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 31
Регистрация: 19.7.2012
Где: Архангельск

Репутация: нет
Всего: нет



Привет всем.
Есть незамысловатый код:
Код

int main()
{
   ... //здесь создал разносортные указатели и выделил им память
   ...
   if(...)//проблема, надо закрыть приложение
   {
      ... //надо освободить память
      return 1;
   }
   ...
   if(...)//проблема, надо закрыть приложение
   {
      ... //надо освободить память
      return 1;
   }
   ...
   if(...)//проблема, надо закрыть приложение
   {
      ... //надо освободить память
      return 1;
   }
   ...
   ... //освобождаю память для всех указателей
   return 0;
}


Как бы так покомпактнее сделать, чтобы в этих if'ах не копипастить освобождение памяти для всех указателей? Я пытался с запретным goto прыгать в конец программы сразу к освобождению, да не заладилось чегой-то...
И если в этих условиях вообще о таком не заикаться, то будет ли утечка памяти?
PM MAIL   Вверх
baldina
Дата 22.11.2013, 01:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 15
Всего: 101



в main() уже ничего не надо освобождать, ОС освободит.
ну а вообще - сделайте локальный объект, освобождайте в деструкторе. при возврате из функции деструктор вызовется автоматически.
PM MAIL   Вверх
gendalf7771
Дата 22.11.2013, 02:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 31
Регистрация: 19.7.2012
Где: Архангельск

Репутация: нет
Всего: нет



baldina, то есть если, скажем, у меня в программе большущие динамические массивы char наклёпаны, то в рамках main() можно в принципе забить на delete?

И вопрос вдогонку. Код:

Код

char* anyFunc(...)
{
   ...
   char *szTemp = new *char[iSize];
   strcpy(szTemp, szTemp2);
   return szTemp;
}


Вот бывает иногда параметров у функции итак навалом, ну не хочу я szTemp передавать среди прочих, а на выходе функции массив получить надо. Есть ли адекватный способ получить желаемый массив с нормальный освобождением памяти?
PM MAIL   Вверх
bsa
Дата 22.11.2013, 10:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 85
Всего: 196



gendalf7771, если ты можешь использовать возможности C++11, то рекомендую использовать std::unique_ptr:
Код
std::unique_ptr<MyClass> ptr1(new MyClass);
std::unique_ptr<int> ptr2(new int[100]);
Если ты ограничен только C++03 или описанный выше подход не нравится, то тогда можно использовать std::vector и std::auto_ptr (только очень внимательно прочитай документацию на этот класс).
PM   Вверх
baldina
Дата 22.11.2013, 10:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 15
Всего: 101



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

Цитата(gendalf7771 @  22.11.2013,  02:03 Найти цитируемый пост)
адекватный способ получить желаемый массив с нормальный освобождением памяти

возвращать объект. например, unique_ptr
PM MAIL   Вверх
gendalf7771
  Дата 22.11.2013, 20:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 31
Регистрация: 19.7.2012
Где: Архангельск

Репутация: нет
Всего: нет



Большое спасибо за ответы!
PM MAIL   Вверх
TarasProger
Дата 12.8.2015, 15:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 104
Регистрация: 5.8.2015

Репутация: нет
Всего: нет



Цитата(ManiaK @  9.8.2005,  14:35 Найти цитируемый пост)
Как известно, строки в Си заканчиваются нулевым символом (символом, имеющим код 0). Любая строка, взятая в двойные кавычки по определению заканчивается этим символом и потому размер "Dinamic String" не 14, а 15. Это нужно для того, чтобы программа, использующая эту строку, знала, где она кончается. Можно, конечно, для каждой строки таскать свою переменную, в которой содержится число символов, но это очень неудобно в тех случаях, когда приходится оперировать десятками различных строк. В Pascal'е другой принцип - первый символ строки содержит не код первого символа, а число символов в строке. Но тогда для стандартного байтного набора символов максимально возможной длинной строки будет 255 символов, а этого бывает мало.
Количество символов можно хранить в двух первых байтах, и в четырёх, и в восьми, и даже в sizeof(char*) первых байт, чтоб уж точно хватило на любую возможную строку. Проблема здесь другая:
1. Если на одной платформе будет количество символов в 4-х первых байтах, а на другой - в восьми, и такая строка будет как есть сохранена в бинарный файл, или передана по сети, то код первого символа точно прочитается не правильно, а может быть не правильно интерпретируется и количество символов.
2. Если на одной платформе один порядкок байт, а на другой - совсем другой, то не правильно интерпретируется количество символов.
Терминальный же ноль в этом плане универсален. И что неудобного в структуре 
Код
struct 
{
 int count;
 char *p;
};
?

Добавлено через 7 минут и 40 секунд
Цитата(ManiaK @  9.8.2005,  14:35 Найти цитируемый пост)
   Си - самый логичный с точки зрения архитектуры ЭВМ язык программирования. Практически всё, что можно сделать на ассемблере, можно сделать и на Си. Не верите? Смотрите:

Выделить всёкод C++
1:
2:
    
    unsigned char* pch = (unsigned char*)0xBC00;
    memcpy(pch, _T("Hello, World!"), 26);

Вот только вызов функции, возможно на языке ассемблера написанной, - не показатель. А попробуйте помимо функций и асм-вставок вызхвать программное прерываение, или присвоить значение регистру процессора. Или соверешить переход в зависимости не от результата сравнения, а от флага результата арифметической операции, например, в случае переполнения. На c нельзя делать вообще ничего, что можно делать на языке ассемблера. А так то свой memcpy можно написать и для паскаля, и для бейсика, и для любого другого языка, допускающего побочные эффекты подпрограмм, даже стекового.

Добавлено через 14 минут и 50 секунд
Цитата(ManiaK @  9.8.2005,  14:35 Найти цитируемый пост)
Разумеется, всё это сказано только для общего развития, подобные вещи принято и правильно писать на Ассемблере, так как только в нём можно наиболее эффективно работать с аппаратурой напрямую.
И это на фоне утверждения о том, что на c якобы можно делать всё тоже самое. Эффектичность чистого c кода, кстати, обязана быть максимально возможной, это было главное требование ТЗ на язык. Максимальная эффктивность кода + возможность делать всё тоже самое, что и на языке ассемблера = на ассемблере нельзя ничего делать эффективнее, в лучшем случае столько же эффективно, но не эффективнее. Но почему то c нуждается в поддержке вставок на языке ассемблера.

PM MAIL   Вверх
TarasProger
Дата 12.8.2015, 16:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 104
Регистрация: 5.8.2015

Репутация: нет
Всего: нет



Цитата(ManiaK @  9.8.2005,  14:35 Найти цитируемый пост)
Вспоминая себя во времена моего изучения Си++, я часто удивляюсь, насколько логично проходило это изучение. На первых порах я брал чужие программы и пытался их модифицировать; классическая схема: сам пока ничего не можешь, но что-то творить хочется. Вскоре мне это, разумеется, надоело, я залез в книги и начал пробовать писать самостоятельно. Но как я писал - это заслуживает отдельного внимания! Я думаю, многие так начинали свой путь программиста...
Я первые месяца полтора не то что изменить что либо в чужой программе, а даже понять в них хоть что то не мог, а мог как раз только писать сам. Потом со знанием алгоритмов и их классов пришло и умение читать чужие программы. Умение же что то менять в чужих программах пришло только лет через 7 с умением абстрагироваться от конркретного стиля, а раньше я мог только прочитать, полностью переписать и тогда уже дополнять.

PM MAIL   Вверх
TarasProger
Дата 14.8.2015, 09:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 104
Регистрация: 5.8.2015

Репутация: нет
Всего: нет



Цитата(ManiaK @  9.8.2005,  14:35 Найти цитируемый пост)
тут указание константы в квадратных скобках явно свидетельствует об том, что будет создан статический массив.
Хочу отметить, что "статический массив" здесь - не синоним "статическая переменная, являющаяся массивом", хотя массив - тоже переменая. Массивы бывают:
1. Константные, в этом случае и количество элементов массива - константа, и сами элементы - константы.
2. Статические, в этом случае количество элементов массива - константа, но сами элементы - переменные. Весь массив при этом может лежать на стеке, если это не противорчит ни стандарту языка, ни спецификации конкретного диалекта, тогда при слишком большой глубине рекурсии рекурсивной функции с локальным массивом может происходить переполнение. В программах на Object Pascal я данное явление наблюдал дважды.
3. Динамические, тогда и количество элементов - переменная, и сами элементы - переменные.
4. Разреженные, тогда количество фактически хранимых элементов может быть меньше диапазона индексов.
5. Логические, каждый такой массив есть массив, номинально состоящий из всех элементов разреженного массива, количество элементов логического массива соответствует диапазону элементов разреженного массива. В принципе можно считать, что логический массив есть представление интерфейса разреженного массива.
6. Физические, это вообще не обязательно массивы, физический массив есть контейнер, содержащий все фактичеки хранимые элементы разреженного массива. Можно считать, что физический массив есть контейнер, на котором реализован разреженный массив, но без собственно реализации операций, отвечающих за разрежение. Физический массив может быть стаческим, или динамическим массивом, линейным или кольцевым списком (но не очередью, или стеком), массивом линейных или кольцевых списков (но не очередей, или стеков), деревом массивом деревьев, линейным или кольцевым списком (но не очередей, или стеков) деревьев деревом массивов, деревом линейных или кольцевых списков (но не очередей, или стеков) и много чем ещё, сколь угодно экзотическим. Но это обязательно контейнер и он обязательно допускает неразрушающее произвольное чтение элемента.
Очевидно, что разреженные и логические массивы явяются динамическими. Только константный массив целиком - константа, любой другой массив целиком - переменная. Но хотя массив - тоже переменная, но особая. В оличие от скалярных переменных, имеющих одно значение каждая, и струкутр, содержащих каэжая несколько переменных, чьи значения складываются в комплексное, но всёж одно значение всей структуры, массив вообще не имеет единого значения, весь массив имеет как раз только множество значений всех своих элементов и эти значения не складываются в нечто цельное. Это не сложная переменная, а перменная-контейнер. И термин "статический" в случае массива имеет иное значение, относящееся не к памяти, в которой он хранится, а к количеству элементов.

Это сообщение отредактировал(а) TarasProger - 14.8.2015, 09:48
PM MAIL   Вверх
TarasProger
Дата 14.8.2015, 09:59 (ссылка) |  (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 104
Регистрация: 5.8.2015

Репутация: нет
Всего: нет



Цитата(ManiaK @  9.8.2005,  14:35 Найти цитируемый пост)
Итак, строение машин долгое время определяло их функции. Однако, за человеком давно замечена одна глупость: он всё пытается унифицировать. Иными словами, в один прекрасный день захотелось одному человеку объединить лопату и чайник в один предмет, причём так, чтобы можно было переключать их функциональность. Нажал кнопочку - чайник, нажал другую - лопата. Удобно!..

Так появились компьютеры. Да, это были, по сути своей, первые компьютеры. Устройство, которое следило за нажатием ваших кнопочек и переключением функций, фактически есть первая программа. Долго радовался человек на своё изобретение, однако и этому однажды пришёл конец. "Лучшее - враг хорошего" - говорят, и не зря...
Computer от английского to compute - вычислять. Значит вычислитель. А реагирование на кнопки - это ещё не вычисление, первый копьютер - это вообще арифмометр, второй - калькулятор.

PM MAIL   Вверх
TarasProger
Дата 14.8.2015, 13:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 104
Регистрация: 5.8.2015

Репутация: нет
Всего: нет



Цитата(ManiaK @  9.8.2005,  14:35 Найти цитируемый пост)
Кстати, само разграничение понятий "процедура" и "функция" довольно условно и никто вас не имеет права щёлкнуть по носу, если вы вдруг употребить одно понятие взамен другого (хотя принято по возможности пользоваться вторым).
Процелура и функция различаются способом возврата значения. Если через имя самой подпрограммы - это функция, иначе - процедура. 
Код
val('3.14', a);
 - вызов процедуры, 
Код
a:=val('3.14');
 - вызов функции. Процедура может возращать значение через глобальную переменную, через модифицируемый параметр, через память, адрес которой передан в параметре-указтеле. Но её собственное имя не может быть указано идентификатором того опенда другого оператора, в котором ожидается результат работы процедуры. А имя функции в этом месте указать можно. На низком уровне нет функций, а есть только процедуры, просто некоторые из них умеют оставлять результаты своей работы в строго определённых местах согласно соглашению о вызовах. Это может быть регистр ax, вершина стека на момент сразу после возврата из функции, место в памяти сразу перед точкой входа в функцию, или что угодно ещё, прописанное в соглашении о вызовах, которым пользуется компилятор языка более высокого уровня. На высоком уровне могут быть и токо функции, как на c/c++, и только процедуры, и оба вида подпрограмм. На object pascal грань между ними вроде бы вообще стёрта: можно функцию вызвать как процедуру, возвращающую результата через первый параметр со стандартным именем Result. Если нет процедур, но есть функции, то вместо процедур в языке релаизованы фунции, формально возвращающие специциальные значения без данных. Если нет функций, но есть процедуры, то надо придётся, как на языке ассемблера, обходиться процедурами и изобретать способы возврата результатов их работы. Если поддерживаются оба вида подпрограмм, то и оба синтаксиса. Поэтому в зависимости от языка может быть принят любой из двух терминов, или все три.
PM MAIL   Вверх
TarasProger
Дата 14.8.2015, 14:37 (ссылка) |  (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 104
Регистрация: 5.8.2015

Репутация: нет
Всего: нет



Цитата(Гость_ManiaK @  21.9.2005,  23:12 Найти цитируемый пост)
Насколько я понял из прочтения вашей статьи. Что указатель это переменная, которая содержит адрес другой переменой. Именно так.
Не всегда так. Во-первых указатель может содержать и адрес функции. А во-вторых указатель может быть и констатой. Указатель - это величина, содержащая адрес чего то другого. Это может быть:
1. Переменная, хранящая адрес другой переменной. Возможно другого указателя.
2. Константа, хранящая адрес переменной. Возможно другого указателя.
3. Переменная, хранящая адрес константы. Возможно другого указателя.
4. Константа, хранящая адерес другой константы. Возможно другого указателя.
5. Переменная, хранящая адрес функции.
6. Константа, хранящая адрес функции.
Указатель, хранящий адрес другого указателя, называется кратным указателем. Указатель, хранящий адерс другого указателя, в котором уже харнится адрес не указателья, называется двойным указателем. Указатель, хранящий адерс другого указателя, в котором опять хрантся адрес другого указателя, в котором уже харнится адрес не указателья, называется тройным указателем. Кратный - собирательное название. Кратность указателя - это количество указателей, включая данный, адреса которых нужны, чтоб по цепочке добраться до не указателя. Здесь: 
Код
int ****p;
 p - указатель на int кратности 4, то есть указатель на указатель на указатель на указатель на int.

Добавлено через 11 минут и 12 секунд
Цитата(Гость_ManiaK @  21.9.2005,  23:12 Найти цитируемый пост)
В ассемблерном коде (после компиляции) ссылок не существует. Это - абстракция, реально никакой памяти ссылки не занимают. Для чего они нужны? самый простой и в тоже время наиболее употребительный пример - передача объекта в функцию не копированием, а ссылкой (то есть передача самого объекта):
Вот как раз параметры ссылочных типов - это на самом деле такие константные указатели, которые не надо разыменовывать. Ни сайзоф, ни взятие адреса этого не покажут, так как настоящая, но временная и безымянная ссылка получается при разыменовании указателья, а параметр ссылочного типа при каждом использовании разыменуется по умолчанию. Но информацию о том, где переменная лежит, надо ведь как то в функцию передать и другого способа кроме указателя для этого на низком уровне нет.

Добавлено через 14 минут и 47 секунд
Цитата(maxim1000 @  22.9.2005,  00:56 Найти цитируемый пост)
ссылка ближе к указателю, чем к пременной
в общем-то именно так она и реализована на уровне компилятора
просто везде, где она используется, компилятор дорисовывает звездочку smile
Код
int x;
int &y=x;
y++;
x=y;
Эта ссылка ближе как раз к переменной, такая ссылка - это просто дублирующая запись с тем же адресом в таблице переменных компилятора. Ничего общего с указателем, хранящим адрес на этапе исполнения, она не имеет.

PM MAIL   Вверх
TarasProger
Дата 14.8.2015, 14:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 104
Регистрация: 5.8.2015

Репутация: нет
Всего: нет



Цитата(ManiaK @  30.5.2006,  16:49 Найти цитируемый пост)
BlHol, абсолютно никакой разницы нет.  
Есть, причём, огромная. Декларация указателя и приведение к указательному типу.

Добавлено через 14 минут и 29 секунд
Цитата(ManiaK @  24.7.2006,  10:01 Найти цитируемый пост)
Да. Причём не обязательно другой переменной. У вас в программе может существовать указатель на память, которой не соответствует ни одна переменная. Каким образом мы получим доступ к этой памяти? Только с помощью указателя на неё. Это ответ на следующий вопрос:
Не путайте безымянные переменные с другими сущностями. 
Код
void f()
{
 int a[20];
 int *p;
 p=new int;// В этой строке значением p станованится адрес динимаческой безымянной переменной.
 delete p;
 p=&(a[5]);// В этой строке значением p станованится адрес безымянной переменной, но не динамической, а элемента другой именованной автоматической переменной.
}
. Переменная существует в обоих случаях, но у неё нет собственного имени. В одном случае переменная создана динамически и получить доступ к ней можно только по указателю, в другом безымеянна только конкретная переменная, но весь массив имеет имя, является обычной автоматической переменной и лежит на стеке, а доступ к её элементу можно получить двояко:
1. По указателю.
2. Вычислив адрес элемента по адерсу всего массива и индексу элемента.
Но если 
Код
 const double a=3.14;
 const double *p;
 p=&a;
, то по адресу в p нет переменной вообще. К величине по этому адресу можно получить доступ по имени самой величины, но ни какая переменная там не лежит, а лежит там константа. А в этом: 
Код
typedef void (*pf)(double);
int main ()
{
 pf p;
 p=f;
 случае p хранит адрес функции.
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Для новичков"
JackYF
bsa

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

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


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, bsa.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Для новичков | Следующая тема »


 




[ Время генерации скрипта: 0.5349 ]   [ Использовано запросов: 23 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.