|
Модераторы: bsa |
|
ManiaK |
|
||||||||||||||||||||||||||||||||||||
Homo Sapience Профиль Группа: Комодератор Сообщений: 1145 Регистрация: 3.8.2004 Где: ИУ5-93 Репутация: 2 Всего: 29 |
ВВЕДЕНИЕ
В этой статье я бы хотел научить людей, недавно занявшихся изучением Си++, сразу трём вещам: работать со строками (в более общем виде - с указателями), писать свои классы и перегружать некоторые операторы в этих классах, которые иногда заметно упрощают жизнь, но для новичков представляются чем-то загадочным и непонятным. Мы напишем свой строковый класс и, таким образом, охватим сразу три эти интересные темы. В конце мы посмотрим, что уже есть в стандартной библиотеке Си++ и сравним с тем, что сделами мы. Эта статья не учит Си++ - предполагается, что читатель уже знаком с основной частью синтаксиса Си++, но не имеет опыта в программировании на нём или этот опыт незначителен. Проще говоря, статья ориентирована на новичков. Я буду по ходу дела объяснять некоторые важные моменты, на которые часто не обращают внимания новички, но все мои рассуждения будут уже содержать в себе базовые термины, такие как класс, метод, функция, переменная и прочее. Все эти понятия должны быть понятны читателю до прочтения данной статьи иначе оно не будет иметь никакого смысла. И ещё: все примеры в этой статье - не готовые решения, а лишь демострация идей; поэтому не стоит копировать их и пытаться компилировать без каких либо изменений - идеи надо просто усвоить. Ну, готовы?.. ЧАСТЬ 1. СИ СТРОКИ ИЛИ НЕМНОГО ТЕОРИИ. Наверное треть всех вопросов, задаваемых новичками на различных форумах интернета, посвящённых Си или Си++, так или иначе касаются строк. Кстати, в названии параграфа я не ошибся - именно Си строки. Почему? потому что в Си++ есть классы и использование указателей бессмысленно (Си является практически идеальным подмножеством Си++ и потому почти всё, что справедливо для Си, справедливо и для Си++). В сущности, все проблемы со строками сводятся к проблемам указателей. Если человек не понимает, как работают указатели и что они из себя представляют, он никогда не поймёт, чем char отличается от char*. Если ещё более углубиться в проблему, то можно увидеть, что непонимание указателей связано зачастую с непонимаением работы самого компьютера. Действительно, а знаете ли вы как ваши программы выполняются компьютером? В Си, как и в Си++, этот вопрос очень важен, так как эти языки очень тесно взаимодействуют с аппаратным уровнем. Обычно ваша программа (бинарный объектный код, понятный процессору без трансляции) загружается в оперативную память и там исполняется. То есть, предположим, что ваша программа занимает 512 байт. Загрузчик программы определяет, куда лучше всего впихнуть её; предположим, он выбрал адрес 0x1000. Тогда последний байт вашей программы будет по адресу 0x1200 (0x1000 + 512). Некоторые преполагают, что в том же месте хранятся все переменные программы. Спешу вас разуверить, это не так. В сегменте кода (так правильно называется эта часть оперативной памяти) хранятся только машинные инструкции самой программы и (не обязательно) константные объекты. Проще говоря, только то, что не меняется в процессе выполнения программы. При попытке из вашей же программы поменять код ("поработать над собой") хорошая операционная система даст оплеуху и выкинет из списка задач, почистив за вами память. Где же хранятся переменные?.. Необходимо сначала разделить переменные на два рода - "динамические" и стековые. В чём разница? во-первых, в скорости выделения памяти под них: под стековые переменные память выделяется с гиганской скоростью и ещё быстрее высвобождается, в то время как при создании динамической переменной система ещё подумает: а где взять эту память?.. Данный вопрос очень важен, часто программисты "обманывают" компилятор, заключая динамический объект в статическую оболочку. Второе достоинство стековых переменных в том, что они автоматически удаляются, коль скоро выполнение программы выходит за облась действия данной переменной. Иначе говоря, программисту не приходится следить за тем, когда отдавать память обратно системе - компилятор делает это за него сам. Но за всё приходится платить в нашем мире: при использовании стековых указателей приходится следить, чтобы в их личную жизнь никто не вмешивался и не брал указатели на них, иначе может получиться казус: переменной уже сто лет как нет, а указатель на неё до сих пор существует. Ничего не подозревающий клиент пытается обратиться по этому указателю к объекту и получает по шее от операционной системы - упс... Что же такое динамические переменные? - это переменные, которые можно создавать и удалять в процессе работы программы. Например, нам нужно написать программу, которая будет читать файлы, указанные пользователем и как-то обрабатывать их. Для этого нам нужно записывать прочитанные данные в какой-то массив, причём мы заранее не можем сказать, какого размера должен быть этот массив. Как быть? использовать динамическую переменную. В теории всё просто. При непосредственном выполнении ваша программа выясняет размер файла и запрашивает у операционной системы необходимый объём памяти. Но как нам "дадут" эту память? Просто! Операционная система выделяет в оперативной памяти область, размер которой равен запрошенному, и даёт вам адрес первого байта этой области (далее просто - адрес области) - этакий жетончик, по которому вы можете получить свои данные. Помимо этого она заносит в свои списки вашу программу, адрес области и её размер. Теперь вы полноправный владелец данной области памяти! Смотрите, как выглядит последний абзац в Си-коде:
В Си++ есть ещё вариант:
В обоих примерах переменная i - статическая, а область памяти, адрес которой эта переменная содержит, - динамическая. Сложно? только первое время, на самом деле всё логично. Можно даже обнаглеть и написать такое:
В Си++ значительно более уважительно относятся к типам объектов и на любые преобразования, подобные этим, грустно улыбаются; от себя хочу добавить, что подобными фокусами по возможности лучше не заниматься. Размер unsigned long гарантированно совпадает с размером указателя. С указателями пользователь может выполнять некоторые арифметические операции; рассмотрим на примере:
Почему предпоследняя строчка работает, а последняя нет, хотя и в той и другой справа - числа? Есть одна маленькая хитрость. В предпоследней операции при сдвиге литера 10 имеет не числовой тип, а специальный тип ptr_diff. Это тип разницы между двумя адресами; переменные этого типа можно прибавлять/вычитать из указателей, но их нельзя присваивать. Иногда этот тип обозначают как difference_typе. Да, для любителей поглумиться над компилятором скажу сразу: операции умножения/деления над указателями запрещены. Наконец мы выделили блок нужного размера и записали туда прочитанные из файла данные, обработали их. Теперь нужно читать следующий файл, вероятно, другого размера. Что делать со "старым" блоком? Есть два варианта: 1. Использовать функцию операционной системы для увеличения размера блока (обычно - realloc). 2. Удалить старый блок ("вернуть" его системе) и запросить новый нужного размера. В Си это выглядит примерно так:
В Си++ это может выглядеть ещё и так:
При выделении памяти оператором new под массив обычно помечается также и размер выделенного массива. Тогда при освобождении памяти оператором delete [] программа будет знать, сколько памяти вернуть системе. Вот такие ситуации не желательны:
Лучше не делать этого. Чаще всего разработчики компиляторов перестраховываются и разницы между удалением массива и удалением одиночного объекта нет (и то - при использовании встроенных типов). Но по закону подлости, если сделать такую пакость, как в последнем примере, через несколько месяцев (лет?) вашу программу перекомпилируют на другом компиляторе или перегрузят операторы delete и.. лучше просто следовать известному в России принципу "заплати налоги - спи спокойно" под налогом подразумевая дополнительную проверку кода на такие вот тонкости. И последнее замечание по указателям: в Си динамически созданные объекты сами не удаляются. То есть, если вы создали какую-то переменную через alloc-функцию или new, она будет висеть в RAM до самого завершения работы программы. Такие маленькие недоработки называют утечками памяти и иногда они выливаются в полное исчерпание ресурсов системы и её зависание (в том случае если программа работает достаточно продолжительное время и регулярно "теряет" память). Что ж, я считаю указатели мы в общих чертах разобрали - можно переходить к тому, с чего начали - к строкам. Первое и самое главное, что вам необходимо понять - это что в Си++, равно как и в Си, нет типа данных "строка". Поняли? ну я и не сомневался, разберём по-подробней... Си - самый логичный с точки зрения архитектуры ЭВМ язык программирования. Практически всё, что можно сделать на ассемблере, можно сделать и на Си. Не верите? Смотрите:
Знаете, что сделают эти две строчки, если пустить их сразу после инициализации Биос? Выведут вам на экран строку "Hello, World!". Мы можем откомпилировать этот код, записать в первый сектор дискеты и загрузиться с неё. Здорово? Если попытаться проделать тоже под ОС, очень вероятно, что ваша программа просто "грохнется", т.к. любая уважающая себя ОС не даёт кому ни попадя лазить в системную область RAM. В Си++ всё выглядит почти также. Отличие только в том, что преобразовывать числовую константу в указатель приходится через reinterpret_cast. Ну не любят этого плюсники! Разумеется, всё это сказано только для общего развития, подобные вещи принято и правильно писать на Ассемблере, так как только в нём можно наиболее эффективно работать с аппаратурой напрямую. На данном примере вы должны были уже понять, что строка в Си/Си++ - это последовательность кодов из заданной таблицы символов. Не правда ли логично? Загляните в текстовый файл hex-редактором и вы увидите нечто вроде этого:
Проще говоря, вы увидите набор кодов символов. Точно также строки хранятся в Си/Си++ переменных. Придумайте, как можно текст представить иначе и очень может быть, что вам дадут нобелевскую премию. Шутка!.. Любая строка в Си - это массив символов. Всё остальное вытекает из этого постулата.
Как известно, строки в Си заканчиваются нулевым символом (символом, имеющим код 0). Любая строка, взятая в двойные кавычки по определению заканчивается этим символом и потому размер "Dinamic String" не 14, а 15. Это нужно для того, чтобы программа, использующая эту строку, знала, где она кончается. Можно, конечно, для каждой строки таскать свою переменную, в которой содержится число символов, но это очень неудобно в тех случаях, когда приходится оперировать десятками различных строк. В Pascal'е другой принцип - первый символ строки содержит не код первого символа, а число символов в строке. Но тогда для стандартного байтного набора символов максимально возможной длинной строки будет 255 символов, а этого бывает мало. Рассмотрим ещё пару примеров работы со строками в Си стиле и закончим эту главу.
Ну вот, на этом с Си строками можно закончить. Переходим к Си++. ЧАСТЬ 2. ПРОЦЕДУРНОЕ ПРОГРАММИРОВАНИЕ. ФУНКЦИИ, СТРУКТУРЫ, МАССИВЫ. Вспоминая себя во времена моего изучения Си++, я часто удивляюсь, насколько логично проходило это изучение. На первых порах я брал чужие программы и пытался их модифицировать; классическая схема: сам пока ничего не можешь, но что-то творить хочется. Вскоре мне это, разумеется, надоело, я залез в книги и начал пробовать писать самостоятельно. Но как я писал - это заслуживает отдельного внимания! Я думаю, многие так начинали свой путь программиста... Я не использовал классов. Я писал на Си++ лишь формально: в действительности же мои программы представляли собой набор функций - то, что даёт уже язык Си! Этот факт можно считать доказательством того, что начинать обучение Си++ следует не с классов, а с возможностей, составляющих фактически язык Си. Итак, процедурное программирование. Для чего нужны программы? - вот самый простой по формулировке и самый сложный по своему смыслу вопрос программирования. Отвлечёмся от метафизических прений и опустимся на землю. Компьютеры необходимы для выполнения различных операций без участия человека. Когда-то, на заре информационных технологий, существовали машины, которые были созданны для определённых операций, причём функциональность таких машин определялась их строением. Например: строение лопаты определяет её назначение - копку, строение чайника - хранение вязких веществ и т.д. Конечно, с дуру можно в чайник засунуть голову, а лопатой попробовать писать вместо ручки.. если у вас возникают подобные мысли, то, как ни странно, программирование для вас идеально подходит! Почему - выяснится позже. Итак, строение машин долгое время определяло их функции. Однако, за человеком давно замечена одна глупость: он всё пытается унифицировать. Иными словами, в один прекрасный день захотелось одному человеку объединить лопату и чайник в один предмет, причём так, чтобы можно было переключать их функциональность. Нажал кнопочку - чайник, нажал другую - лопата. Удобно!.. Так появились компьютеры. Да, это были, по сути своей, первые компьютеры. Устройство, которое следило за нажатием ваших кнопочек и переключением функций, фактически есть первая программа. Долго радовался человек на своё изобретение, однако и этому однажды пришёл конец. "Лучшее - враг хорошего" - говорят, и не зря... Захотелось однажды человеку сделать так, чтобы можно было произвольно, в любой момент времени выбрать, как будет реагировать их машина, на нажатие той или иной кнопки. Что появилось? Правильно - перфокарты!.. Такие машины были уже полноценными компьютерами, потому как обладали не только постоянной памятью (коей являлась архитектура), но и переменной - перфокартами. Извините за столько затянувшееся бла-бла-бла, но теперь, мне кажется, вопросов вроде "что есть программа" должно не возникать - а это очень важно!.. Теперь посмотрим более современно. В любом компьютере существует исполнительное устройство (и часто - не одно), которое может выполнять фиксированный набор каких-то простых операций. Разберём это на примере нашего вездесущего чайника. Предположим, у нашей машины "автоматического чайника" есть следующий набор элементарных операций: открыть крышку, залить воду, включить нагреватель, выключить нагреватель, заорать "идиот, опять включил - и заснул!!!". Нам нужно составить несколько программ: кипичение воды без криков при окончании работы и с ними. Очевидно, что обе программы будут содержать блоки с одинаковыми наборами операций (открыть крышку, залить воду, включиться, выключиться) и только в конце второй программы будет приписано включение ора на всю ивановскую. "И что?" - скажем мы, "Не обломаемся продублировать эти одинаковые блоки!". Подождите, я же не договорил: у нас памяти на перфокарте только на шесть элементарных операций. Что делать будете? Вот тут в дело вступают процедуры: мы обзываем наши первые четыре операции как "кипячение чайника" и прописываем отдельно, а в двух наших программах пишем только вызов этого блока: "закипятить чайник", "поорать". Функции отличаются от процедур только тем, что возвращают результат своих действий; например, после выполнения функции "закипятить чайник", можно куда-то записать, сколько времени происходило кипячение. Также, и функции и процедуры могут принимать параметры, например: "закипятить чайник водой с начальной температурой 20 градусов" - здесь параметром будет начальная температура; от начальных параметров, мы видим, будет меняться и возвращаемое значение - чем выше начальная температура, тем меньше времени потребуется на кипячение. А если мы пишем просто "закипятить чайник", то фактически мы предоставляем выбор начальной тепературы самой функции кипячения, мы не передаём параметра. Если функция способна сама выбрать это начальное значение при необходимости, то такой параметр называют параметром по умолчанию. Кстати, само разграничение понятий "процедура" и "функция" довольно условно и никто вас не имеет права щёлкнуть по носу, если вы вдруг употребить одно понятие взамен другого (хотя принято по возможности пользоваться вторым). Программирование, основанное на функциях и процедурах и называют процедурным. Однако довольно часто в это понятие включают ещё кое-что. О том, что такое переменная и указатель говорилось в предыдущей статье, так что не имеет смысла повторяться. Переменные предоставляют возможность хранить информацию, но предоставляют очень примитивно: нам доступны лишь целые числа, строки и дробные числа. А как мы сможем описать параметры чайника? Для этого нам потребуется несколько переменных, но передавать их все отдельно в функции неудобно. Да и создавать придётся для каждого экземпляра чайника сразу несколько переменных.. но опять эти люди!.. Придумали обзывать набор переменных одним именем и предоставлять возможность создавать сразу несколько переменных по заранее сформированному шаблону. Такой шаблон назвали структурой. Структура подобна штампу, который заметно облегчает жизнь бухгалтерам и юристам: им не приходится каждый раз писать, им необходимо просто припечатать штамп к листку и на последнем сразу появится нужная надпись, да ещё и в рамочке! Структуры - продвинутые штампы, они содержат в себе нечто, что можно изменять (некоторые штампы тоже содержат пустые поля, куда можно что-нибудь вписать). Это заранее заранее "непропечатанное" нечто и есть переменные, как вы должны были уже понять. Вы указываете только тип и набор переменных. Вот пример:
Теперь мы не будем отдельно создавать переменные "модель", "габаритные размеры" и пр. для каждого чайника. Вместо этого мы напишем:
Переменная ch1 уже содержит в себе переменные model, gab_x и пр. Нам нужно только использовать специальный оператор, чтобы обратиться к ним:
Структуры также могут быть указываемы; и тогда используется другой оператор для доступа к содержимому структуры:
struct Chaynik практически полноценный тип, такой же как int, double и char. Он не умеет только того, что не определено явно. К примеру, компилятор не знает, как ему прибавить к чайнику что-либо. Поэтому данная операция в процедурном программировании существовать не может:
Задавая себе вопрос "как?" можно довольно точно определить, будет ли компилятор воспринимать вашу запись или нет, хотя иногда бывают неожиданности. В любом случае знайте: если после компиляции вашей гениальной программы компилятор покатывается со смеху, указывая на строчку в вашей программе, где производится какая-либо операция над структурой - значит компилятор такую операцию не понимает (заметим, что над тем, что они что-то не понимают, смеются только люди довольно глупые, но это не значит, что разработчики вашего компилятора одни из них). Опустимся теперь на следующий уровень: разберём детали. Впервую очередь в процедурном программировании необходимо выяснить, каким образом происходит передача переменных в функции. Передача стандартных, "одиночных" переменных и указателей должна быть понятна и так, а вот как обстоит дело с массивами и структурами?.. Человеческий ум убог и ничего принципиально нового придумать не может. Вместо этого он решает текущие проблемы уже изобретёнными инструментами, подстраивая их (тот самый случай, когда чайник используют вместо лопаты). Передача массивов и структур происходит банальной передачей указателя на них. Смотрите:
Первые две функции с точки зрения ассемблера будут совершенно идеинтичным, различными у них будут только имена. Почему? Потому, что всё, что больше размера указателя (sizeof(int*)) обычно передаётся посредством указателя: сначала создаётся копия b, а потом в функцию отправляется только указатель на эту копию. int a[] и int* в Си же вообще идеинтичны, потому копия a создаваться не будет: вместо этого просто произойдёт передача указателя на первый элемент a. Казалось бы: где логика? На самом деле логика есть: когда мы объединили несколько переменных структурой в одну, мы тем самым сказали компилятору, чтобы он распространил на эту группу переменных теже правила, что действуют на обычные переменные (насколько это возможно, конечно). Одиночные переменные передаются "по значению" (то есть, передаётся не сам объект, а его копия - значение). Разделение же на int[] и int* в Си и Си++ чисто лексическое, компилятор интерпретирует и то однозначно за исключением случая создания переменной: тут указание константы в квадратных скобках явно свидетельствует об том, что будет создан статический массив. Также, было введено ещё одно удобное ограничение; взгляните на func2 - параметр a здесь будет передаваться точно также, как и в func и func1, но компилятор должен будет проверить, что вы передаёте ему массив именно из 13 элементов (разумеется, проверить это на этапе компиляции можно лишь при передаче статического объекта). Обычно правда компилятор языка Си разрешает нарушение этого правила и максимум - погрозит вам пальчиком при передаче указателя или статического объекта иного размера. В Си++ всё может быть значительно строже. Вообще, передача параметров не устанавливается чётко ни стандартом Cи ни Си++: поведение может быть целиком и полностью определяемо компилятором. Но если вы не программируете микроконтроллеры или какие-нибудь экзотичные процессоры, вы вряд ли встретите серьёзные отступления от описанных правил.
Это сообщение отредактировал(а) archimed7592 - 29.12.2007, 13:38 |
||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||
509harbore |
|
|||
Новичок Профиль Группа: Участник Сообщений: 2 Регистрация: 4.9.2005 Репутация: нет Всего: нет |
ManiaK Объясните мне разницу между указателеми и ссылками.
Насколько я понял из прочтения вашей статьи. Что указатель это переменная, которая содержит адрес другой переменой. |
|||
|
||||
Гость_ManiaK |
|
||||||||||
Unregistered |
Именно так.
Ссылка - это просто синоним для переменной.
В ассемблерном коде (после компиляции) ссылок не существует. Это - абстракция, реально никакой памяти ссылки не занимают. Для чего они нужны? самый простой и в тоже время наиболее употребительный пример - передача объекта в функцию не копированием, а ссылкой (то есть передача самого объекта):
В данном примере при передаче а в функцию func сначала создаётся копия a и она уже отдаётся на растерзание, в то время как сам исходный объект не меняется. А вот так можно явно указать компилятору, что нужно использовать сам объект (точнее - ссылку, хотя это одно и тоже), а не его копию:
} |
||||||||||
|
|||||||||||
maxim1000 |
|
||||||||||
Эксперт Профиль Группа: Участник Сообщений: 3334 Регистрация: 11.1.2003 Где: Киев Репутация: 1 Всего: 110 |
ссылка ближе к указателю, чем к пременной
в общем-то именно так она и реализована на уровне компилятора просто везде, где она используется, компилятор дорисовывает звездочку
при компиляции будет сначала переделано в такое:
поэтому, когда мы пишем
на самом деле получается
получается, что изменить значение адреса невозможно единственное, чем отличается ссылка от указателя - инициализация:
означает занесение адреса x в y и это навсегда (пока не выйдем из этого блока)... -------------------- qqq |
||||||||||
|
|||||||||||
509harbore |
|
|||
Новичок Профиль Группа: Участник Сообщений: 2 Регистрация: 4.9.2005 Репутация: нет Всего: нет |
ManiaK ,maxim1000 ,благодарю вас за столь хорошие отвтеты.
А для чего служит необходимость разыменования указателей? |
|||
|
||||
Void |
|
|||
λcat.lolcat Профиль Группа: Участник Клуба Сообщений: 2206 Регистрация: 16.11.2004 Где: Zürich Репутация: 2 Всего: 173 |
Для того, чтобы получить доступ к объекту, на который они указывают. А зачем они тогда нужны были бы? -------------------- “Coming back to where you started is not the same as never leaving.” — Terry Pratchett |
|||
|
||||
BlHol |
|
||||
Шустрый Профиль Группа: Участник Сообщений: 126 Регистрация: 26.11.2005 Репутация: нет Всего: нет |
Маленький вопрос:
Вот это понятно:
Указатель на объект класса. А вот это как понимать?:
Заранее благодарен. С уважением. |
||||
|
|||||
ManiaK |
|
|||
Homo Sapience Профиль Группа: Комодератор Сообщений: 1145 Регистрация: 3.8.2004 Где: ИУ5-93 Репутация: 2 Всего: 29 |
BlHol, абсолютно никакой разницы нет.
|
|||
|
||||
champion |
|
|||
Опытный Профиль Группа: Участник Сообщений: 272 Регистрация: 26.1.2005 Репутация: нет Всего: 2 |
Я правильно понял, что указатель это переменная, которая ссылается на адрес, по которому находится содержание переменной, другой переменной? И каков смысл применения указателей? Приведите пожалуйста в пример код, из которого было бы видно, где их используют.
В статье я увидел int *i=alloc(1000); что есть alloc? И все остальное из этой кухни, что приводится в пример? |
|||
|
||||
slava72 |
|
|||
Новичок Профиль Группа: Участник Сообщений: 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), и не задумываться о распределении памяти - все за вас сделает компилятор.
определено множество стандартных функция для работы со строками, при желании можно писать свои, но здесь есть одна тонкость - передача параметров. Хотя внешне обект строка похож на обычный int, тем не менее для каждого нового объекта распределяется память, вызываются конструкторы/деструкторы и т.д. Поэтому при написании своих функций в списоке аргументов желательно: - Для входных параметров объявлять аргумент как const string param& - для выходных (изменяемых) - как string param& Использование строк в качестве char* (C-строк) - можно для функций, который _не_ модифицируют строки (с использованием c_str() см. код). Если же требуется буфер обмена с изменяемой памятью - его придется создавать самостоятельно, но зато потом можно будет присваивать строке. Зы: навеяно вопросом |
|||
|
||||
ManiaK |
|
||||||||||||
Homo Sapience Профиль Группа: Комодератор Сообщений: 1145 Регистрация: 3.8.2004 Где: ИУ5-93 Репутация: 2 Всего: 29 |
Читаем внимательней, коментарии не зря ставил:
Т.е.: данной функцией мы можем "попросить" у операционной системы дать нам память.
Да. Причём не обязательно другой переменной. У вас в программе может существовать указатель на память, которой не соответствует ни одна переменная. Каким образом мы получим доступ к этой памяти? Только с помощью указателя на неё. Это ответ на следующий вопрос:
Пожалуйста! -
[оффтоп] Подписался на тему, теперь буду отвечать во-время [/оффтоп] Добавлено @ 10:11 Пардон, если вот так:
То это т.н. преобразование типов. Название страшное, но, как обычно, идея элементарная: в первой строке мы создаём переменную-указатель типом int, во второй строке - типом TComponent. Си++ отличается от Си тем, что довольно строго относится ко взаимодействиям объектов разного типа. Если бы мы написали вот так вторую строку:
То компилятор заявил бы нам, мол, парень, очко протри: у тебя слева помидор, а справа огурец: ты их потом не перепутаешь? А то может у тебя алергия на помидоры, а ты съешь указатель на огурец, который на самом деле указывает на помидор. Когда мы пишем перед объектом "(Type*)", мы фактически говорим компилятору: думай шо хошь, но тип этого объекта - указатель на Type. У меня всё под контролем, мол... |
||||||||||||
|
|||||||||||||
NightmareZ |
|
||||||||||
[хакер] Профиль Группа: Участник Сообщений: 699 Регистрация: 10.8.2006 Репутация: 0 Всего: 13 |
Таким образом определяем тип данных MyEnum, который может принимать три значения: One, Two и Three. При этом One, Two и Three - это целочисленные константы. Далее MyEnum можно использовать, например, так:
Структуры же - это совокупность переменных, объединённых под одним именем. Если мы определим структуру вот так:
то сможем создавать переменные, содержащие в себе три значения (One, Two и Three). Обращаться к отдельным переменным внутри структуры нужно через точку:
Объединения позволяют хранить переменные разного типа в одной области памяти.
Если unsigned short int занимает два байта, а unsigned char - один, то, после данного присваивания, переменная One будет содержать ноль, а Two - единицу. -------------------- NightmareZ.net - мой блог и сайт, мои проекты и прочий трэш Ely-Art.ru - наша маленькая домашняя арт-студия mugcraft.ru - кружки на любой вкус |
||||||||||
|
|||||||||||
Frekenbok |
|
|||
Шустрый Профиль Группа: Участник Сообщений: 51 Регистрация: 29.10.2006 Репутация: нет Всего: 1 |
Со всеми этими обсуждениями вторая глава как-то осталась незамеченной. Или всем все понятно? Мне вот не все.
ManiaK, батенька, да вы философ! (я имею в виду чайники) Вот мои вопросы:
|
|||
|
||||
archimed7592 |
|
||||||||
Архимед Профиль Группа: Завсегдатай Сообщений: 2531 Регистрация: 12.6.2004 Где: Moscow Репутация: 6 Всего: 93 |
но это не массивы. массив - это N элементов одного заданного типа. структура же может иметь много полей и все могут быть разных типов, в том числе и массивами. структура - это как мусорный контейнер (совсем не в плохом слысле, просто в голову больше ниче не приходит), а массив - как корзина для яиц. кстати, помимо того, что структура может содержать массивы ещё не редко встречаются массивы из структур. вступление: передача параметров как правило производится через регистры (размер регистра 4 байта). если регистров не хватает, то передача параметров производится через стек. ещё одно вступление: указатель - это почти в любом случае 4 байта - просто номер первого (нулевого) байта в памяти (для int - это номер младшего байта). ответ: нет. ManiaK сказал, что с точки зрения ассемблера - это одинаковые ф-ции (лучше было сказать сигнатуры)...ф-ции то разные, но вот способ их вызова одинаковый. т. е. способ их вызова при хорошо сложившихся звёздах будет одинаковый. т. е., когда мы вызываем любую из этих ф-ций происходит следующее: первый аргумент кладётся в регистр1, второй - в регистр2. и в том и в другом случае второй аргумент - это указатель. в первом случае - на char, во втором - на Chaynik. но! с точки зрения ассемблера разницы между указателем на char и указателем на Chaynik нету. в первом случае всё понятно, а вот во втором: почему указатель, мы ж структуру передаем? вот собственно говоря та мысль, которую пытался донести ManiaK: при передаче малых объектов (до 4 байт) они передаются как есть т. е. их копия лежит в регистре, а для больших объектов делается следующее: создается копия на стеке и в регистре передается уже указатель, а не сам объект. во второй параметр передается не структура, но некий объект, размер которого превышает 4 байта (8 байт) и потому разницы между сигнатурами всех трех ф-ций не будет - в первой укзатель передается явно, в друих двух компилятор делает так, что ты не замечаешь как делается копия и неявно передается указатель на неё.
1. разыменовать 2. взять поле. т. е.
к методам тоже. к любым членам. -------------------- 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 |
||||||||
|
|||||||||
Frekenbok |
|
|||
Шустрый Профиль Группа: Участник Сообщений: 51 Регистрация: 29.10.2006 Репутация: нет Всего: 1 |
archimed7592, еще раз, для особо одаренных...
Насколько я понимаю, указатель должен указывать на уже существующий объект (переменную и т.п.) или на NULL. Возьмем для примера класс TButton (С++ Builder). Я получаю доступ к полям и методам кнопки через указатель? А "где" сам объект? Компилятор создает объект и указатель на него, а я работаю только с указателем? |
|||
|
||||
Daevaorn |
|
|||
Эксперт Профиль Группа: Комодератор Сообщений: 2155 Регистрация: 29.11.2004 Где: Москва Репутация: нет Всего: 70 |
Указаетль указывает на область памяти. Куда именно ему всё равно. Просто если там не будет объекта - будут проблемы Да Находится по адресу накоторой указывает указатель Создает поле в классе формы с типом "указатель на объект кнопка" Да |
|||
|
||||
ManiaK |
|
|||
Homo Sapience Профиль Группа: Комодератор Сообщений: 1145 Регистрация: 3.8.2004 Где: ИУ5-93 Репутация: 2 Всего: 29 |
Нифига подобного. В Си/Си++ компилятор вообще ничего сам не делает. ТЫ создаёшь указатель, ТЫ, если хочешь, создаёшь объект, ТЫ выбираешь с кем тебе работать - с указателем или с объектом. И, наконец, ТЫ выбираешь когда твой объект должен будет удалён из памяти. Ни один из этих пунктов не будет за тебя выполнен компилятором, если ты не создашь объект сам, а попытаешься обратиться к нему по указателю, то результат может быть самым неожиданным (в зависимости от того, что находится в данный момент времени в качестве значения указателя). |
|||
|
||||
archimed7592 |
|
|||
Архимед Профиль Группа: Завсегдатай Сообщений: 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 |
|||
|
||||
Frekenbok |
|
|||
Шустрый Профиль Группа: Участник Сообщений: 51 Регистрация: 29.10.2006 Репутация: нет Всего: 1 |
||||
|
||||
archimed7592 |
|
|||
Архимед Профиль Группа: Завсегдатай Сообщений: 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 |
|||
|
||||
Frekenbok |
|
|||
Шустрый Профиль Группа: Участник Сообщений: 51 Регистрация: 29.10.2006 Репутация: нет Всего: 1 |
как передать в функцию указатель на строку в качестве параметра-переменной (чтобы в функции он передвинулся и в main это было видно, но не глобальный указатель)? сама функция возвращает число (позиция указателя).
|
|||
|
||||
archimed7592 |
|
|||
Архимед Профиль Группа: Завсегдатай Сообщений: 2531 Регистрация: 12.6.2004 Где: Moscow Репутация: 6 Всего: 93 |
-------------------- 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 |
|||
|
||||
ManiaK |
|
||||||
Homo Sapience Профиль Группа: Комодератор Сообщений: 1145 Регистрация: 3.8.2004 Где: ИУ5-93 Репутация: 2 Всего: 29 |
C языке Си придётся пользоваться указателем на указатель.
Добавлено @ 09:56
Я вообще про формы ничего не говорил. Если ты кидаешь компонент на форму - то, что там произойдёт зависит от компилятора, на уровне языка у тебя ещё ничего не будет. Я имел ввиду вот что:
Компилятор за тебя ни то, ни другое, ни третье делать не будет.
Вот что я, собственно, хотел сказать. Если есть какие-то непонимания в понятиях объект/указатель в Си/Си++ - то это уже вопрос к первой статье, по которой, покуда все молчат, я посчитал, всё понятно. Да, конечно. Для статических нам нужно только изъявить желание и мы тут же получим объект и нам не нужно будет следить за тем, чтобы память им занимаемая где-нибудь не "улетела". Но это всё вроде было уже описано в первой статье. Если что-то непонятно - я поправлю. Тока скажите. |
||||||
|
|||||||
Frekenbok |
|
|||
Шустрый Профиль Группа: Участник Сообщений: 51 Регистрация: 29.10.2006 Репутация: нет Всего: 1 |
||||
|
||||
archimed7592 |
|
|||
Архимед Профиль Группа: Завсегдатай Сообщений: 2531 Регистрация: 12.6.2004 Где: Moscow Репутация: 6 Всего: 93 |
вот тебе и передача...оттуда же
-------------------- 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 |
|||
|
||||
drLans |
|
|||
Новичок Профиль Группа: Участник Сообщений: 22 Регистрация: 11.8.2006 Репутация: нет Всего: нет |
Люди поясните, плз, в чем отличие динамического выделения памяти от обычного задания того же массива в тексте программы?
|
|||
|
||||
Partizan |
|
|||
Let's do some .NET Профиль Группа: Модератор Сообщений: 2828 Регистрация: 19.12.2005 Где: Санкт-Петербург Репутация: 2 Всего: 67 |
разные области памяти задействуются... при объявлении массив в тексте программы в compile-time память под него выделяется в стэке... динамическая память выделяется из кучи... -------------------- СУВ, Partizan. |
|||
|
||||
nickless |
|
|||
Гентозавр Профиль Группа: Участник Клуба Сообщений: 2976 Регистрация: 29.8.2005 Где: Germany Репутация: 2 Всего: 181 |
А еще динамически выделенную память нужно не забывать освобождать Это сообщение отредактировал(а) nickless - 2.12.2006, 17:36 -------------------- 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 |
|||
|
||||
drLans |
|
|||
Новичок Профиль Группа: Участник Сообщений: 22 Регистрация: 11.8.2006 Репутация: нет Всего: нет |
Partizan,
так стек по идее быстрее. Daevaorn, не могу пока. Ибо не вдавался в особенности (и главное смысл) использования new/delete. Я не понимаю, что может быть лучше этого:
|
|||
|
||||
Daevaorn |
|
|||
Эксперт Профиль Группа: Комодератор Сообщений: 2155 Регистрация: 29.11.2004 Где: Москва Репутация: нет Всего: 70 |
||||
|
||||
maxim1000 |
|
|||
Эксперт Профиль Группа: Участник Сообщений: 3334 Регистрация: 11.1.2003 Где: Киев Репутация: 1 Всего: 110 |
в общем смысле динамическое использование памяти нужно для контроля времени жизни объекта
при использовании стека если объект1 создан раньше объекта2, то уничтожен он будет позже именно из этого ограничения исходит скорость работы - функции выделения освобождения памяти зачастую сводятся к одной команде процессора, а проблемы поиска и фрагментации не возникают в принципе (ну ещё на скорость влияет то, что чаще всего стек находится в кеше, а значит не нужно его ниоткуда доставать, но это уже зависит от платформы) если вернуться к C++, то ещё одно достоинство (которое уже упоминали) - автоматическое удаление выделенного объекта при выходе из блока, так что забыть это сделать невозможно лично я стараюсь по минимуму использовать динамическую память и другое выделение ресурсов, которые потом нужно явно освобождать, только когда без этого сложно обойтись (как уже привели пример с неизвестным количеством элементов) или это сильно повышает эффективность (когда она критична), но даже в этих случаях (особенно когда программа сложная) не помешает упаковать управление этими ресурсами в какой-нибудь класс, который будет следить за ними (для всяких массивов чаще всего подходит std::vector)... -------------------- qqq |
|||
|
||||
UnrealMan |
|
||||||||
Опытный Профиль Группа: Участник Сообщений: 722 Регистрация: 30.3.2006 Репутация: 5 Всего: 32 |
А под статически объявленный массив память тоже может выделяться динамически (если он объявлен как член класса и память под объект такого класса выделяется динамически). По поводу отличий ничего говорить не буду, ибо вопрос – суть боян. В И-нете полно литературы – читай и просвещайся. Всё равно по-нормальному научиться чему-то без книг, задавая подобные вопросы на форумах, вряд ли получится. Б. Страуструп: «Язык программирования С++ (2-е дополненное издание)» 3.2.6 Свободная память.
Необязательно. Из-за оптимизации это правило может быть нарушено, например:
вывод (MinGW-3.4.4):
– т.е. объект a2 функции func может быть создан прямо на месте объекта a в функции main и уничтожен только по завершении main. |
||||||||
|
|||||||||
ali01 |
|
|||
Новичок Профиль Группа: Участник Сообщений: 5 Регистрация: 29.7.2007 Репутация: нет Всего: нет |
А где же про классы? есть вопрос если в классе создаются динамические переменные, в деструкторе надо вызывать delete?
|
|||
|
||||
bsa |
|
|||
Эксперт Профиль Группа: Модератор Сообщений: 9185 Регистрация: 6.4.2006 Где: Москва, Россия Репутация: 85 Всего: 196 |
Если где-то сделан new, то обязательно должен быть выполнен соответствующий delete. Иначе никак. И не важно где, в классах, структурах или функциях. |
|||
|
||||
ali01 |
|
|||
Новичок Профиль Группа: Участник Сообщений: 5 Регистрация: 29.7.2007 Репутация: нет Всего: нет |
Деструктор сам этого не делает? А когда автоматически создаешь класс например в VC там создается пустой конструктор, я так понял надо самому там прописывать delete?
|
|||
|
||||
Daevaorn |
|
|||
Эксперт Профиль Группа: Комодератор Сообщений: 2155 Регистрация: 29.11.2004 Где: Москва Репутация: нет Всего: 70 |
||||
|
||||
ali01 |
|
|||
Новичок Профиль Группа: Участник Сообщений: 5 Регистрация: 29.7.2007 Репутация: нет Всего: нет |
Да, запутался. Я понял, спасибо. Раньше думал деструктор сам все сделает
|
|||
|
||||
ali01 |
|
||||||||
Новичок Профиль Группа: Участник Сообщений: 5 Регистрация: 29.7.2007 Репутация: нет Всего: нет |
Просматривал код wxFormBuilder, пример конструктора:
и деструктора:
Там вообще нет delete. Или может я что-то не понял? |
||||||||
|
|||||||||
archimed7592 |
|
|||
Архимед Профиль Группа: Завсегдатай Сообщений: 2531 Регистрация: 12.6.2004 Где: Moscow Репутация: 6 Всего: 93 |
ali01, посмотри деструкторы базовых классов. Там(чуть ли не на самом верху иерархии) должны быть примерно следующие строки:
Во всяком случае в известных мне тулкитах(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 |
|||
|
||||
archimed7592 |
|
|||
Архимед Профиль Группа: Завсегдатай Сообщений: 2531 Регистрация: 12.6.2004 Где: Moscow Репутация: 6 Всего: 93 |
Это сообщение отредактировал(а) 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 |
|||
|
||||
anatox91 |
|
|||
программист-самоучка Профиль Группа: Участник Сообщений: 699 Регистрация: 12.1.2008 Где: ++Украина.Крым++ Репутация: 1 Всего: 13 |
ManiaK, большое спасибо за интересную статью и своеобразный стиль изложения
Тема разделена: Возвращение указателя на локальный объект, warning C4172: returning address of local variable or temporary. Это сообщение отредактировал(а) archimed7592 - 3.2.2008, 00:52 -------------------- The code is the design © Sony VAIO VGN-FW480J |
|||
|
||||
Annuta |
|
|||
Бывалый Профиль Группа: Участник Сообщений: 174 Регистрация: 3.10.2006 Где: Dubna Репутация: нет Всего: 1 |
У меня такой вопрос - есть ли разница между этими двумя объявлениями. Какое из них более рационально ?
TMinuit *min = new TMinuit(6); TMinuit min(6); --------------------
Программист - это комбинация лени и логики ! |
|||
|
||||
bsa |
|
|||
Эксперт Профиль Группа: Модератор Сообщений: 9185 Регистрация: 6.4.2006 Где: Москва, Россия Репутация: 85 Всего: 196 |
Второе. Если есть желание обсудить этот вопрос, то лучше создать отдельную тему |
|||
|
||||
XeS |
|
||||||||
Новичок Профиль Группа: Участник Сообщений: 16 Регистрация: 6.4.2008 Репутация: нет Всего: нет |
Маленький вопросик по работе с указателями
это как я понимаю массив из 10 указателей, т.е я с ним работаю так
а конструкция такого вида
это указатель на массив из 10 int я с ним работаю так
я прав???? |
||||||||
|
|||||||||
J0ker |
|
||||
Опытный Профиль Группа: Участник Сообщений: 986 Регистрация: 17.9.2008 Репутация: 9 Всего: 14 |
нет можно так:
технически int* и int(*)[n] - разные типы, и данная запись несколько бессмысленна... ну разве что вам надо передавать p в функцию принимающую конкретно int(*)[n] но той-же функциональностью обладает следующий код:
|
||||
|
|||||
UnrealMan |
|
|||
Опытный Профиль Группа: Участник Сообщений: 722 Регистрация: 30.3.2006 Репутация: 5 Всего: 32 |
||||
|
||||
boombasta |
|
||||||||
Новичок Профиль Группа: Участник Сообщений: 12 Регистрация: 23.10.2008 Репутация: нет Всего: нет |
привет всем!
что-то немогу найти ответов на 2 моих вопроса: 1. че отличается
от
2. что здесь написано:
что за конструкция такая PluginInterface*(*)?? 3. и здесь
тут типа обьявление переменной getObject но не понятно что за (CoreInterface*)? З.Ы. PluginInterface и CoreInterface - это классы |
||||||||
|
|||||||||
bsa |
|
|||
Эксперт Профиль Группа: Модератор Сообщений: 9185 Регистрация: 6.4.2006 Где: Москва, Россия Репутация: 85 Всего: 196 |
1. ничем не отличается.
2 и 3. Это тип указателя на функцию, которая принимает указатель на CoreInterface и возвращает указатель на PluginInterface. Соответственно, PluginInterface* (*getObject)(CoreInterface*) - это указатель на функцию. Я, например, всегда делаю так, чтобы не вводить никого в замешательство:
|
|||
|
||||
boombasta |
|
|||
Новичок Профиль Группа: Участник Сообщений: 12 Регистрация: 23.10.2008 Репутация: нет Всего: нет |
со строкой 1 понятно (спсб bsa ;)) обьявляется указательна функцию, которая в качестве аргумента принимает указатель на CoreInterface, а возвращает указатель на PluginInterface. а что происходит в строке 2? насколько я понимаю lib->resolve("getObject") возвражает ссылку на функцию? а что такое вот это: (PluginInterface*(*)(CoreInterface*)) ? - это жжет мне мозг как-то это можно переписать по проще - пусть больше строк, но как-то понятнее (или хотябы просто по-другому, может так будет понятнее ) дольше по тексту getObject юзается как обычная функция: PluginInterface* getObject(CoreInterface*); |
|||
|
||||
bsa |
|
||||
Эксперт Профиль Группа: Модератор Сообщений: 9185 Регистрация: 6.4.2006 Где: Москва, Россия Репутация: 85 Всего: 196 |
Это преобразование к типу "указатель на функцию" - PluginInterface* (*)(CoreInterface*). Что делать для того, чтобы было понятно я показал:
Вообще-то обобщенно тип "указатель на функцию" выглядит так: <return_type> (*)(<arg1_type>, <arg2_type>, ...) Сам указатель (переменная типа "указатель на функцию") определяется так: <return_type> (*<var_name>)(<arg1_type>, <arg2_type>, ...) P.S.: почитай книжку для начинающих |
||||
|
|||||
Goliaf777 |
|
|||
Шустрый Профиль Группа: Участник Сообщений: 70 Регистрация: 19.11.2008 Репутация: нет Всего: нет |
Вопрос.Почему нужно using namespace std если в приводимых кодах на форуме пишется std::cout.Объясните ЧаЙнИгУ!!)))
|
|||
|
||||
bsa |
|
||||
Эксперт Профиль Группа: Модератор Сообщений: 9185 Регистрация: 6.4.2006 Где: Москва, Россия Репутация: 85 Всего: 196 |
по-хорошему, надо писать std::cout, std::cin, std::cerr, std::endl... Но если тебе лень и пишешь маленькую программу, которую не собираешься в дальнейшем улучшать, то можно написать после всех инклюдов using namespace std, и это избавит тебя от необходимости писать приставку std::. Если у тебя нет желания писать std, но не хочется глобально раскрывать стандартное пространство имен, то можешь использовать using namespace std внутри функции (точнее, внутри почти любых операторных скобок). Как вариант, можно раскрыть только необходимые элементы пространства имен:
|
||||
|
|||||
Goliaf777 |
|
|||
Шустрый Профиль Группа: Участник Сообщений: 70 Регистрация: 19.11.2008 Репутация: нет Всего: нет |
Огромное спасибо!!!
|
|||
|
||||
XeS |
|
||||
Новичок Профиль Группа: Участник Сообщений: 16 Регистрация: 6.4.2008 Репутация: нет Всего: нет |
Помогите разобраться, почему не меняеться адрес переменной, допустим есть такой код:
на экране будет 123, а не 321, а если я перепешу код вот так то все будет норм:
с чем это связано и почему? и второй вопрос: пусть у меня есть переменные такого типа: int*** a; int* b; что бы привести b к типу a, я должен сделать такое: int*** a; int** c; int* b; c = &b; a = &c; можно ли это сделать по другому, что-бы меньше писать? |
||||
|
|||||
bsa |
|
||||||||
Эксперт Профиль Группа: Модератор Сообщений: 9185 Регистрация: 6.4.2006 Где: Москва, Россия Репутация: 85 Всего: 196 |
XeS
Обычно, такие конструкции можно встретить в качестве параметров функций (когда функция может изменить значение аргумента) или для определения многомерных динамических массивов. Это сообщение отредактировал(а) bsa - 21.4.2009, 13:15 |
||||||||
|
|||||||||
master123 |
|
|||
Новичок Профиль Группа: Участник Сообщений: 5 Регистрация: 13.4.2009 Репутация: нет Всего: нет |
Спс... Мне помогло
________________________________ http://linecinema.ru/ Это сообщение отредактировал(а) master123 - 1.5.2009, 20:10 |
|||
|
||||
XAKERs89 |
|
|||
Новичок Профиль Группа: Участник Сообщений: 2 Регистрация: 24.8.2009 Репутация: нет Всего: нет |
можно ли создать массив структур с помощью указателей?
Это сообщение отредактировал(а) XAKERs89 - 26.8.2009, 21:13 |
|||
|
||||
andrew_121 |
|
|||
Кодофей Профиль Группа: Завсегдатай Сообщений: 3448 Регистрация: 3.1.2008 Репутация: 10 Всего: 33 |
XAKERs89, Ссылок? Или указателей?
Уточните вопрос. -------------------- Удалил аккаунт. Прощайте! |
|||
|
||||
XAKERs89 |
|
|||
Новичок Профиль Группа: Участник Сообщений: 2 Регистрация: 24.8.2009 Репутация: нет Всего: нет |
оЙ..сори запутался ((( указателей
|
|||
|
||||
andrew_121 |
|
|||
Кодофей Профиль Группа: Завсегдатай Сообщений: 3448 Регистрация: 3.1.2008 Репутация: 10 Всего: 33 |
-------------------- Удалил аккаунт. Прощайте! |
|||
|
||||
ПанкПрогаммист |
|
|||
Новичок Профиль Группа: Участник Сообщений: 1 Регистрация: 25.9.2009 Репутация: нет Всего: нет |
Нефига не понял)) сушствует книга с++ для чаиников??
|
|||
|
||||
bsa |
|
|||
Эксперт Профиль Группа: Модератор Сообщений: 9185 Регистрация: 6.4.2006 Где: Москва, Россия Репутация: 85 Всего: 196 |
ПанкПрогаммист, попробуй тут посмотреть: Часто задаваемые вопросы
|
|||
|
||||
Axsandr |
|
|||
Новичок Профиль Группа: Участник Сообщений: 2 Регистрация: 23.3.2010 Репутация: нет Всего: нет |
язык программирования С++ Бьерн Страуструп В принципе очень доступно все написано.... _____________________ Для тех, кто интересуется forex есть выгодные условия торговли. Это сообщение отредактировал(а) Axsandr - 29.3.2010, 13:28 |
|||
|
||||
EgoBrain |
|
|||
Опытный Профиль Группа: Участник Сообщений: 537 Регистрация: 23.3.2008 Где: Комната Репутация: нет Всего: 2 |
А что происходит при использовании ссылки на сложные типы данных, например структуры? Компилятор меняют ссылку на указатель, и вместо оператора "." вставляет оператор "->"? |
|||
|
||||
baldina |
|
|||
Эксперт Профиль Группа: Завсегдатай Сообщений: 3433 Регистрация: 5.12.2007 Где: Москва Репутация: 15 Всего: 101 |
ничего он не меняет. maxim1000 имел в виду семантику, связанную с реализацией, но не саму реализацию.
впрочем, если считать, что в ассемблерном виде может (и скорее всего будет) использоваться косвенная адресация, то можно и так сказать |
|||
|
||||
kolesnle |
|
|||
Упертый сишник Профиль Группа: Участник Сообщений: 93 Регистрация: 22.3.2013 Репутация: нет Всего: нет |
||||
|
||||
rvpxbnkx |
|
|||
Новичок Профиль Группа: Участник Сообщений: 1 Регистрация: 18.7.2013 Репутация: нет Всего: нет |
Поясните пожалуйста. Программа на си. Я так понимаю массив это указатель на первый элемент + число элементов. В примере char *dic[][40] получается трехмерный массив. Потом он приобразуется с p = (char **)dic; Вот я и не понимаю почему трехмерный массив станет 2-х мерным. И еще Не совсем понятно чем такая конструкция char (*p)[10]; отличается от char *s[]
|
|||
|
||||
bsa |
|
|||
Эксперт Профиль Группа: Модератор Сообщений: 9185 Регистрация: 6.4.2006 Где: Москва, Россия Репутация: 85 Всего: 196 |
char *dic[][40] массив массивов по 40 указателей на char. char ** - это указатель на указатель на char. Т.е. когда идет это преобразование ты отказываешься от одного измерения.
Обычно, подобные вещи делают так: const char *dic[] = { ... }; А вообще, почитай про указатели и ссылки еще тут. |
|||
|
||||
eses |
|
|||
Новичок Профиль Группа: Участник Сообщений: 1 Регистрация: 8.10.2012 Репутация: нет Всего: нет |
Модератор: Сообщение скрыто. |
|||
|
||||
gendalf7771 |
|
|||
Новичок Профиль Группа: Участник Сообщений: 31 Регистрация: 19.7.2012 Где: Архангельск Репутация: нет Всего: нет |
Привет всем.
Есть незамысловатый код:
Как бы так покомпактнее сделать, чтобы в этих if'ах не копипастить освобождение памяти для всех указателей? Я пытался с запретным goto прыгать в конец программы сразу к освобождению, да не заладилось чегой-то... И если в этих условиях вообще о таком не заикаться, то будет ли утечка памяти? |
|||
|
||||
baldina |
|
|||
Эксперт Профиль Группа: Завсегдатай Сообщений: 3433 Регистрация: 5.12.2007 Где: Москва Репутация: 15 Всего: 101 |
в main() уже ничего не надо освобождать, ОС освободит.
ну а вообще - сделайте локальный объект, освобождайте в деструкторе. при возврате из функции деструктор вызовется автоматически. |
|||
|
||||
gendalf7771 |
|
|||
Новичок Профиль Группа: Участник Сообщений: 31 Регистрация: 19.7.2012 Где: Архангельск Репутация: нет Всего: нет |
baldina, то есть если, скажем, у меня в программе большущие динамические массивы char наклёпаны, то в рамках main() можно в принципе забить на delete?
И вопрос вдогонку. Код:
Вот бывает иногда параметров у функции итак навалом, ну не хочу я szTemp передавать среди прочих, а на выходе функции массив получить надо. Есть ли адекватный способ получить желаемый массив с нормальный освобождением памяти? |
|||
|
||||
bsa |
|
|||
Эксперт Профиль Группа: Модератор Сообщений: 9185 Регистрация: 6.4.2006 Где: Москва, Россия Репутация: 85 Всего: 196 |
gendalf7771, если ты можешь использовать возможности C++11, то рекомендую использовать std::unique_ptr:
|
|||
|
||||
baldina |
|
|||
Эксперт Профиль Группа: Завсегдатай Сообщений: 3433 Регистрация: 5.12.2007 Где: Москва Репутация: 15 Всего: 101 |
delete в main() дань хорошему стилю, если не сделать, отладчик будет показывать утечки памяти.
но т.к. приложение завершается, и ОС освободит память, занятую приложением, с этой точки зрения этот delete ничего не дает.
возвращать объект. например, unique_ptr |
|||
|
||||
gendalf7771 |
|
|||
Новичок Профиль Группа: Участник Сообщений: 31 Регистрация: 19.7.2012 Где: Архангельск Репутация: нет Всего: нет |
Большое спасибо за ответы!
|
|||
|
||||
TarasProger |
|
|||
Шустрый Профиль Группа: Участник Сообщений: 104 Регистрация: 5.8.2015 Репутация: нет Всего: нет |
Количество символов можно хранить в двух первых байтах, и в четырёх, и в восьми, и даже в sizeof(char*) первых байт, чтоб уж точно хватило на любую возможную строку. Проблема здесь другая: 1. Если на одной платформе будет количество символов в 4-х первых байтах, а на другой - в восьми, и такая строка будет как есть сохранена в бинарный файл, или передана по сети, то код первого символа точно прочитается не правильно, а может быть не правильно интерпретируется и количество символов. 2. Если на одной платформе один порядкок байт, а на другой - совсем другой, то не правильно интерпретируется количество символов. Терминальный же ноль в этом плане универсален. И что неудобного в структуре
Добавлено через 7 минут и 40 секунд Вот только вызов функции, возможно на языке ассемблера написанной, - не показатель. А попробуйте помимо функций и асм-вставок вызхвать программное прерываение, или присвоить значение регистру процессора. Или соверешить переход в зависимости не от результата сравнения, а от флага результата арифметической операции, например, в случае переполнения. На c нельзя делать вообще ничего, что можно делать на языке ассемблера. А так то свой memcpy можно написать и для паскаля, и для бейсика, и для любого другого языка, допускающего побочные эффекты подпрограмм, даже стекового. Добавлено через 14 минут и 50 секунд И это на фоне утверждения о том, что на c якобы можно делать всё тоже самое. Эффектичность чистого c кода, кстати, обязана быть максимально возможной, это было главное требование ТЗ на язык. Максимальная эффктивность кода + возможность делать всё тоже самое, что и на языке ассемблера = на ассемблере нельзя ничего делать эффективнее, в лучшем случае столько же эффективно, но не эффективнее. Но почему то c нуждается в поддержке вставок на языке ассемблера. |
|||
|
||||
TarasProger |
|
|||
Шустрый Профиль Группа: Участник Сообщений: 104 Регистрация: 5.8.2015 Репутация: нет Всего: нет |
Я первые месяца полтора не то что изменить что либо в чужой программе, а даже понять в них хоть что то не мог, а мог как раз только писать сам. Потом со знанием алгоритмов и их классов пришло и умение читать чужие программы. Умение же что то менять в чужих программах пришло только лет через 7 с умением абстрагироваться от конркретного стиля, а раньше я мог только прочитать, полностью переписать и тогда уже дополнять. |
|||
|
||||
TarasProger |
|
|||
Шустрый Профиль Группа: Участник Сообщений: 104 Регистрация: 5.8.2015 Репутация: нет Всего: нет |
1. Константные, в этом случае и количество элементов массива - константа, и сами элементы - константы. 2. Статические, в этом случае количество элементов массива - константа, но сами элементы - переменные. Весь массив при этом может лежать на стеке, если это не противорчит ни стандарту языка, ни спецификации конкретного диалекта, тогда при слишком большой глубине рекурсии рекурсивной функции с локальным массивом может происходить переполнение. В программах на Object Pascal я данное явление наблюдал дважды. 3. Динамические, тогда и количество элементов - переменная, и сами элементы - переменные. 4. Разреженные, тогда количество фактически хранимых элементов может быть меньше диапазона индексов. 5. Логические, каждый такой массив есть массив, номинально состоящий из всех элементов разреженного массива, количество элементов логического массива соответствует диапазону элементов разреженного массива. В принципе можно считать, что логический массив есть представление интерфейса разреженного массива. 6. Физические, это вообще не обязательно массивы, физический массив есть контейнер, содержащий все фактичеки хранимые элементы разреженного массива. Можно считать, что физический массив есть контейнер, на котором реализован разреженный массив, но без собственно реализации операций, отвечающих за разрежение. Физический массив может быть стаческим, или динамическим массивом, линейным или кольцевым списком (но не очередью, или стеком), массивом линейных или кольцевых списков (но не очередей, или стеков), деревом массивом деревьев, линейным или кольцевым списком (но не очередей, или стеков) деревьев деревом массивов, деревом линейных или кольцевых списков (но не очередей, или стеков) и много чем ещё, сколь угодно экзотическим. Но это обязательно контейнер и он обязательно допускает неразрушающее произвольное чтение элемента. Очевидно, что разреженные и логические массивы явяются динамическими. Только константный массив целиком - константа, любой другой массив целиком - переменная. Но хотя массив - тоже переменная, но особая. В оличие от скалярных переменных, имеющих одно значение каждая, и струкутр, содержащих каэжая несколько переменных, чьи значения складываются в комплексное, но всёж одно значение всей структуры, массив вообще не имеет единого значения, весь массив имеет как раз только множество значений всех своих элементов и эти значения не складываются в нечто цельное. Это не сложная переменная, а перменная-контейнер. И термин "статический" в случае массива имеет иное значение, относящееся не к памяти, в которой он хранится, а к количеству элементов. Это сообщение отредактировал(а) TarasProger - 14.8.2015, 09:48 |
|||
|
||||
TarasProger |
|
|||
Шустрый Профиль Группа: Участник Сообщений: 104 Регистрация: 5.8.2015 Репутация: нет Всего: нет |
Computer от английского to compute - вычислять. Значит вычислитель. А реагирование на кнопки - это ещё не вычисление, первый копьютер - это вообще арифмометр, второй - калькулятор. |
|||
|
||||
TarasProger |
|
||||
Шустрый Профиль Группа: Участник Сообщений: 104 Регистрация: 5.8.2015 Репутация: нет Всего: нет |
Процелура и функция различаются способом возврата значения. Если через имя самой подпрограммы - это функция, иначе - процедура.
|
||||
|
|||||
TarasProger |
|
||||
Шустрый Профиль Группа: Участник Сообщений: 104 Регистрация: 5.8.2015 Репутация: нет Всего: нет |
1. Переменная, хранящая адрес другой переменной. Возможно другого указателя. 2. Константа, хранящая адрес переменной. Возможно другого указателя. 3. Переменная, хранящая адрес константы. Возможно другого указателя. 4. Константа, хранящая адерес другой константы. Возможно другого указателя. 5. Переменная, хранящая адрес функции. 6. Константа, хранящая адрес функции. Указатель, хранящий адрес другого указателя, называется кратным указателем. Указатель, хранящий адерс другого указателя, в котором уже харнится адрес не указателья, называется двойным указателем. Указатель, хранящий адерс другого указателя, в котором опять хрантся адрес другого указателя, в котором уже харнится адрес не указателья, называется тройным указателем. Кратный - собирательное название. Кратность указателя - это количество указателей, включая данный, адреса которых нужны, чтоб по цепочке добраться до не указателя. Здесь:
Добавлено через 11 минут и 12 секунд Вот как раз параметры ссылочных типов - это на самом деле такие константные указатели, которые не надо разыменовывать. Ни сайзоф, ни взятие адреса этого не покажут, так как настоящая, но временная и безымянная ссылка получается при разыменовании указателья, а параметр ссылочного типа при каждом использовании разыменуется по умолчанию. Но информацию о том, где переменная лежит, надо ведь как то в функцию передать и другого способа кроме указателя для этого на низком уровне нет. Добавлено через 14 минут и 47 секунд Эта ссылка ближе как раз к переменной, такая ссылка - это просто дублирующая запись с тем же адресом в таблице переменных компилятора. Ничего общего с указателем, хранящим адрес на этапе исполнения, она не имеет. |
||||
|
|||||
TarasProger |
|
||||||||
Шустрый Профиль Группа: Участник Сообщений: 104 Регистрация: 5.8.2015 Репутация: нет Всего: нет |
Есть, причём, огромная. Декларация указателя и приведение к указательному типу.
Добавлено через 14 минут и 29 секунд
1. По указателю. 2. Вычислив адрес элемента по адерсу всего массива и индексу элемента. Но если
|
||||||||
|
|||||||||
Правила форума "C/C++: Для новичков" | |
|
Запрещается! 1. Публиковать ссылки на вскрытые компоненты 2. Обсуждать взлом компонентов и делиться вскрытыми компонентами
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, bsa. |
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | C/C++: Для новичков | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |