Что такое дескрипторы файлов, объясненные простыми словами

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

Логотип

Итак, первое - логотип. Это больше статусная вещь. Если он у вас есть, хорошо, если нет – не страшно. Не нужно тратить недели на придумывание логотипа. Для начала подойдет любой, который вы можете заказать у фрилансера за 500 руб. Логотип можно сделать в какой-нибудь программе или просто написать название фирмы красивым шрифтом.

Дескриптор

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

Примеры дескрипторов для лендинга:

«Интернет-магазин автозапчастей в Москве. Доставка по России»

«Оформление виз и мультивиз в Европу за 15 минут»

На этом примере дескрипт «обучение плаванию»:

Телефон

Не нужно ставить на лендинге мобильный номер, это не вызывает доверия. У номера телефона должен быть соответствующий код города, чтобы человек сразу понимал, где вы находитесь и куда он звонит. Если вы работаете по всей России, то нужен номер 8-800, который даёт возможность клиентам звонить бесплатно. В этом случае напишите под номером: “Звонок по России бесплатно”.

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

Кнопка «Заказать звонок»

Под номером телефона можно поставить кнопку “Заказать обратный звонок”, но практика показывает, что звонок заказывают очень редко. Кроме того, нужно учитывать, будет ли кнопка вписываться в дизайн и не будет ли отвлекать внимание. Если, к примеру, у вас на странице есть форма заявки со своей кнопкой и цель вашего лендинга в получении заявки, то лишняя кнопка в шапке будет отвлекать посетителя от совершения целевого действия.

Желательно текст кнопки писать в повелительном наклонении: “Закажите”. Вообще все призывы на лендинге должны быть в повелительном наклонении: закажите, узнайте, посмотрите, позвоните.

Адрес компании

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

Теперь переходим к основному содержимому страницы.

Заголовок и подзаголовок

Здесь должно быть сформулировано УТП . Помимо УТП должна быть показана конкретная деятельность. Если в дескрипте вы говорите обобщенно, то в заголовке и подзаголовке нужно описать всё конкретно.

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

Помните: то, что вы помещаете в заголовке, даёт 50% успеха вашего лендинга. Вторые 50% дают дизайн и структура.

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

Примеры заголовков на лендинге:

Как придумать хороший заголовок?

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

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

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

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

Как составить продающих дескриптор?

Первый пример

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

В данном случае дескриптор будет звучать так: «Интернет-магазин качественной сантехники и аксессуаров для личных и общественных санузлов с доставкой по МО и Москве».

Есть конкретные вопросы?
Оставьте заявку и расскажите о проблеме!

Если вам требуется улучшение продающих качеств, создание или продвижение сайта, контент-маркетинг, аудит или оптимизация, свяжитесь с нами через форму он-лайн заявки,
по телефону - (495) 661-4026 или по е-мейл - to(at)gonka(dot)ru .

Второй пример

Вы продаете, скажем, скважинные насосы. Об этом нам посетителю говорит тайтл (служебный заголовок страницы). Но каждый ли посетитель смотрит на ваш тайтл? Конечно, нет. Его увидит, пожалуй, только опытный сеошник, да и то по привычке. Плюс во многих современных браузерах тайтл вовсе незаметен.

Воспользоваться навигацией – выход, но не очень удобный, особенно для неопытных пользователей ПК. Если в навигационной системе вашего сайта много пунктов, заблудиться будет не так сложно. Да и вряд ли навигация расскажет посетителю, будет ли доставлен тот или иной товар по вашему адресу, а также можно ли его купить в розницу или только оптом.

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

Третий пример

Рассмотрим сайт, предлагающий услуги, например, по оптимизации налогов. Естественно, сама формулировка «оптимизация налогов» будет недостаточна. Непонятно, где проводится оптимизация, каких налогов, как и для кого. В данном случае дополнительные данные обязательны. Не забудьте подчеркнуть ссылки.

И напоследок, что важно знать о дескрипторе?

Дескриптор – это обобщенное описание компании, ее товаров и услуг.
Дескриптор должен быть:

  • Четким, лаконичным, но не слишком коротким.
  • Отвечающим на такие вопросы, как «для кого», «где», «чем занимаетесь» и пр.
  • Открывающимся на каждой странице ресурса.
  • Включающим мощное и громкое конкурентное преимущество, чтобы купили/заказали именно у вас.

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

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

Дескрипторы, написанные нашими специалистыми, улучшают продающие качества сайтов.

6 ответов

Простыми словами при открытии файла операционная система создает запись для представления этого файла и сохраняет информацию об этом открывшемся файле. Итак, если в вашей ОС открыто 100 файлов, то в ОС будет находиться 100 записей (где-то в ядре). Эти записи представлены целыми числами типа (... 100, 101, 102....). Этот номер записи является файловым дескриптором. Таким образом, это просто целое число, которое уникально представляет собой открытый файл в операционной системе. Если ваш процесс откроет 10 файлов, ваша таблица процессов будет содержать 10 записей для дескрипторов файлов.

Аналогично, когда вы открываете сетевой сокет, он также представляется целым числом и называется дескриптором Socket. Надеюсь, вы понимаете.

Дескриптор файла - это непрозрачный дескриптор, который используется в интерфейсе между пространством пользователя и ядра для идентификации ресурсов файла/сокета. Поэтому, когда вы используете open() или socket() (системные вызовы для интерфейса с ядром), вам предоставляется дескриптор файла, который является целым числом (это фактически индекс в структуре процессов u, но это не важно). Поэтому, если вы хотите напрямую взаимодействовать с ядром, используя системные вызовы read() , write() , close() и т.д. Дескриптор, который вы используете, является файловым дескриптором.

На системных вызовах имеется слой абстракции, который является интерфейсом stdio . Это обеспечивает больше функциональности/функций, чем это делают основные системные вызовы. Для этого интерфейса непрозрачным дескриптором вы получаете FILE* , который возвращается вызовом fopen() . Существует много функций, которые используют интерфейс stdio fprintf() , fscanf() , fclose() , которые помогут вам сделать вашу жизнь проще. В C, stdin , stdout и stderr находятся FILE* , которые в UNIX соответственно сопоставляются файловым дескрипторам 0 , 1 и 2 .

Услышьте его от Лошадиного устья: APUE (Ричард Стивенс).
К ядру все открытые файлы упоминаются файловыми дескрипторами. Дескриптор файла является неотрицательным числом.
Когда мы открываем существующий файл или создаем новый файл, ядро ​​возвращает дескриптор файла в процесс. Ядро поддерживает таблицу всех открытых дескрипторов файлов, которые используются. Выделение файловых дескрипторов, как правило, является последовательным, и они привязаны к файлу как следующий свободный дескриптор файла из пула свободных файловых дескрипторов. Когда мы закрываем файл, дескриптор файла освобождается и доступен для дальнейшего выделения.
См. Это изображение для получения дополнительной информации:

Когда мы хотим прочитать или записать файл, мы идентифицируем файл с файловым дескриптором, который был вызван вызовом функции open() или create() , и используйте его как аргумент для read() или write() .
По общему мнению, оболочки UNIX связывают файловый дескриптор 0 с стандартным входом процесса, дескриптор файла 1 с стандартным выходом и дескриптор файла 2 с Стандартная ошибка .
Дескриптор файла варьируется от 0 до OPEN_MAX.
Для получения дополнительной информации перейдите к третьей главе книги APUE.

В дополнение к другим ответам, unix рассматривает все как файловую систему. Ваша клавиатура - это файл, который читается только с точки зрения ядра. Экран - это файл только для записи. Аналогичным образом, папки, устройства ввода-вывода и т.д. Также считаются файлами. Всякий раз, когда файл открывается, скажите, когда драйверы устройства [для файлов устройства] запрашивают open() или процесс открывает файл пользователя, ядро ​​выделяет файловый дескриптор, целое число, которое указывает доступ к этому файлу, такое, что оно доступно только для чтения, писать только и т.д. [для справки: https://en.wikipedia.org/wiki/Everything_is_a_file ]

Больше о дескрипторах файлов

    Дескрипторы файлов (FD) - неотрицательные целые числа (0,1,2,...), которые связаны с открытым файлом.

    0,1,2 являются стандартными FD, которые соответствуют STDIN_FILENO, STDOUT_FILENO и STDERR_FILENO. (определяется в unistd.h) открывается по умолчанию для имени оболочки при запуске программы.

    FDs выделены в последовательном порядке, означает наименьшее возможное нераспределенное целочисленное значение.

    FD для конкретного процесса можно увидеть в /proc/ $pid/fd (в системах на базе Unix)

    В любой операционной системе работают процессы (p), например p1, p2, p3 и т.д. Каждый процесс обычно делает постоянное использование файлов.

    Каждый процесс состоит из дерева процессов (или таблицы процессов, в другой фразировке).

    Обычно операционные системы представляют каждый файл в каждом процессе по номеру (то есть в каждом дереве/таблице процессов).

    Первым файлом, используемым в этом процессе, является файл0, второй - файл1, третий - файл2 и т.д.

    Любое такое число является файловым дескриптором.

    Дескрипторы файлов обычно являются целыми числами (0, 1, 2, а не 0,5, 1,5, 2,5).

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

    Аналогично, когда вы открываете сетевой сокет, он имеет дескриптор сокета.

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

    Дескрипторы файлов могут быть глобальными (процесс A начинается с 0, и заканчивается в 1, процесс B начинается с 2, а заканчивается на 3) и т.д., но, насколько я знаю, обычно в современных операционных системы, дескрипторы файлов не являются глобальными и на самом деле зависят от процесса (процесс A начинается с 0, а заканчивается на 5, а процесс B начинается с 0, а заканчивается на 10).

    • Перевод

    Краткий обзор

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

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

    Детали выше описывают, что алгоритм вызова дескрипторов реализуется с помощью метода __getattribute__() для object , type и super . Классы наследуют этот алгоритм, когда они наследуют от object или если у них есть метакласс, реализующий подобную функциональность. Таким образом, классы могут отключить вызов дескрипторов, если переопределят __getattribute__() .

    Пример дескриптора

    Следующий код создаёт класс, чьи объекты являются дескрипторам данных и всё, что они делают - это печатают сообщение на каждый вызов get или set . Переопределение __getattribute__ - это альтернативный подход, с помощью которого мы могли бы сделать это для каждого атрибута. Но если мы хотим наблюдать только за отдельными атрибутами, то это проще сделать с помощью дескриптора.
    class RevealAccess(object): """Дескриптор данных, который устанавливает и возвращает значения, и печатает сообщение о том, что к атрибуту был доступ. """ def __init__(self, initval=None, name="var"): self.val = initval self.name = name def __get__(self, obj, objtype): print "Получаю", self.name return self.val def __set__(self, obj, val): print "Обновляю" , self.name self.val = val >>> class MyClass(object): x = RevealAccess(10, "var "x"") y = 5 >>> m = MyClass() >>> m.x Получаю var "x" 10 >>> m.x = 20 Обновляю var "x" >>> m.x Получаю var "x" 20 >>> m.y 5
    Этот простой протокол предоставляет просто увлекательные возможности. Некоторые из них настолько часто используются, что были объединены в отдельные функции. Свойства, связанные и несвязанные методы, статические методы и методы класса - все они основаны на этом протоколе.

    Свойства

    Вызова property() достаточно, чтобы создать дескриптор данных, который вызывает нужные функции во время доступа к атрибуту. Вот его сигнатура:
    property(fget=None, fset=None, fdel=None, doc=None) --> атрибут, реализующий свойства
    В документации показано типичное использование property() для создания управляемого атрибута x:
    class C(object): def getx(self): return self.__x def setx(self, value): self.__x = value def delx(self): del self.__x x = property(getx, setx, delx, "Я свойство "x".")
    Вот эквивалент property на чистом питоне, чтобы было понятно как реализовано property() с помощью протокола дескрипторов:
    class Property(object): "Эмуляция PyProperty_Type() в Objects/descrobject.c" def __init__(self, fget=None, fset=None, fdel=None, doc=None): self.fget = fget self.fset = fset self.fdel = fdel self.__doc__ = doc def __get__(self, obj, objtype=None): if obj is None: return self if self.fget is None: raise AttributeError, "нечитаемый атрибут" return self.fget(obj) def __set__(self, obj, value): if self.fset is None: raise AttributeError, "не могу установить атрибут" self.fset(obj, value) def __delete__(self, obj): if self.fdel is None: raise AttributeError, "не могу удалить атрибут" self.fdel(obj)
    Встроенная реализация property() может помочь, когда существовал интерфейс доступа к атрибуту и произошли какие-то изменения, в результате которых понадобилось вмешательство метода.

    Например, класс электронной таблицы может давать доступ к значению ячейки через Cell("b10").value . В результате последующих изменений в программе, понадобилось сделать так, чтобы это значение пересчитывалось при каждом доступе к ячейке, однако программист не хочет менять клиентский код, который обращается к атрибуту напрямую. Эту проблему можно решить, если обернуть атрибут value с помощью дескриптора данных, который будет создан с помощью property() :
    class Cell(object): . . . def getvalue(self, obj): "Пересчитываем ячейку прежде чем вернуть значение" self.recalc() return obj._value value = property(getvalue)

    Функции и методы

    В питоне все объектно-ориентированные возможности реализованы с помощью функционального подхода. Это сделано совсем незаметно с помощью дескрипторов не данных.

    Словари классов хранят методы в виде функций. При определении класса, методы записываются с помощью def и lambda - стандартных инструментов для создания функций. Единственное отличие этих функций от обычных в том, что первый аргумент зарезервирован под экземпляр объекта. Этот аргумент обычно называется self , но может называться this или любым другим словом, которым можно называть переменные.

    Для того, чтобы поддерживать вызов методов, функции включают в себя метод __get__ , который автоматически делает их дескрипторами не данных при поиске атрибутов. Функции возвращают связанные или не связанные методы, в зависимости от того, через что был вызван этот дескриптор.
    class Function(object): . . . def __get__(self, obj, objtype=None): "Симуляция func_descr_get() в Objects/funcobject.c" return types.MethodType(self, obj, objtype)
    С помощью интерпретатора мы можем увидеть как на самом деле работает дескриптор функции:
    >>> class D(object): def f(self, x): return x >>> d = D() >>> D.__dict__["f"] # Внутренне хранится как функция >>> D.f # Доступ через класс возвращает несвязанный метод >>> d.f # Доступ через экземпляр объекта возвращает связанный метод >
    Вывод интерпретатора подсказывает нам, что связанные и несвязанные методы - это два разных типа. Даже если они могли бы быть реализованы таким образом, на самом деле, реализация PyMethod_Type в файле Objects/classobject.c содержит единственный объект с двумя различными отображениями, которые зависят только от того, есть ли в поле im_self значение или там содержится NULL (C эквивалент значения None).

    Таким образом, эффект вызова метода зависит от поля im_self . Если оно установлено (т.е. метод связан), то оригинальная функция (хранится в поле im_func) вызывается, как мы и ожидаем, с первым аргументом, установленным в значение экземпляра объекта. Если же она не связана, то все аргументы передаются без изменения оригинальной функции. Настоящая C реализация instancemethod_call() чуть более сложная, потому что включает в себя некоторые проверки типов и тому подобное.

    Статические методы и методы класса

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

    Повторим ещё раз. Функции имеют метод __get__ , с помощью которых они становятся методами, во время поиска атрибутов и автоматического вызова дескрипторов. Дескрипторы не данных преобразуют вызов obj.f(*args) в вызов f(obj, *args) , а вызов klass.f(*args) становится f(*args) .

    В этой таблице показано связывание и два наиболее популярных варианта:

    Статические методы возвращают функцию без изменений. Вызовы c.f или C.f эквиваленты вызовам object.__getattribute__(c, "f") или object.__getattribute__(C, "f") . Как результат, функция одинаково доступна как из объекта, так и из класса.

    Хорошими кандидатами для статических методов являются методы, которым не нужна ссылка на переменную self .

    Например, пакет для статистики может включать класс для экспериментальных данных. Класс предоставляет обычные методы для расчёта среднего, ожидания, медианы и другой статистики, которая зависит от данных. Однако, там могут быть и другие функции, которые концептуально связаны, но не зависят от данных. Например, erf(x) это простая функция для преобразования, которая нужна в статистике, но не зависит от конкретного набора данных в этом классе. Она может быть вызвана и из объекта, и из класса: s.erf(1.5) --> 0.9332 или Sample.erf(1.5) --> 0.9332 .

    Так как staticmethod() возвращает функцию без изменений, то этот пример не удивляет:
    >>> class E(object): def f(x): print x f = staticmethod(f) >>> print E.f(3) 3 >>> print E().f(3) 3
    Если использовать протокол дескриптора не данных, то на чистом питоне staticmethod() выглядел бы так:
    class StaticMethod(object): "Эмуляция PyStaticMethod_Type() в Objects/funcobject.c" def __init__(self, f): self.f = f def __get__(self, obj, objtype=None): return self.f
    В отличие от статических методов, методы класса подставляют в начало вызова функции ссылку на класс. Формат вызова всегда один и тот же, и не зависит от того, вызываем мы метод через объект или через класс.
    >>> class E(object): def f(klass, x): return klass.__name__, x f = classmethod(f) >>> print E.f(3) ("E", 3) >>> print E().f(3) ("E", 3)
    Это поведение удобно, когда нашей функции всегда нужна ссылка на класс и ей не нужны данные. Один из способов использования classmethod() - это создание альтернативных конструкторов класса. В питоне 2.3, метод класса dict.fromkeys() создаёт новый словарь из списка ключей. Эквивалент на чистом питоне будет таким:
    class Dict: . . . def fromkeys(klass, iterable, value=None): "Эмуляция dict_fromkeys() в Objects/dictobject.c" d = klass() for key in iterable: d = value return d fromkeys = classmethod(fromkeys)
    Теперь новый словарь уникальных ключей можно создать таким образом:
    >>> Dict.fromkeys("abracadabra") {"a": None, "r": None, "b": None, "c": None, "d": None}
    Если использовать протокол дескриптора не данных, то на чистом питоне classmethod() выглядел бы так:
    class ClassMethod(object): "Эмуляция PyClassMethod_Type() в Objects/funcobject.c" def __init__(self, f): self.f = f def __get__(self, obj, klass=None): if klass is None: klass = type(obj) def newfunc(*args): return self.f(klass, *args) return newfunc

    8 ответов

    Это абстрактное опорное значение к ресурсу, часто памяти или открытого файла, или трубы.

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

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

    Я должен добавить, что в любой современной операционной системе даже так называемые "настоящие указатели" по-прежнему непрозрачны в пространстве виртуальной памяти процесса, что позволяет O/S управлять и переупорядочивать память без аннулирования указателей в процессе.

    A HANDLE - уникальный для контекста идентификатор. В контексте контекста я имею в виду, что дескриптор, полученный из одного контекста, не обязательно может использоваться в любом другом произвольном контексте, который также работает на HANDLE s.

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

    Сама HANDLE является всего лишь интегральным типом. Обычно, но необязательно, это указатель на некоторый базовый тип или расположение памяти. Например, HANDLE , возвращаемый GetModuleHandle , фактически является указателем на адрес базовой виртуальной памяти модуля. Но нет правила, указывающего, что ручки должны быть указателями. Ручка также может быть просто простым целым числом (которое может быть использовано некоторыми Win32 API в качестве индекса в массиве).

    HANDLE представляют собой преднамеренно непрозрачные представления, которые обеспечивают инкапсуляцию и абстрагирование от внутренних ресурсов Win32. Таким образом, API Win32 может потенциально изменить базовый тип за ручкой, без какого-либо влияния на код пользователя (по крайней мере, на эту идею).

    Рассмотрим эти три различные внутренние реализации API Win32, которые я только что составил, и предположим, что Widget является struct .

    Widget * GetWidget (std::string name) { Widget *w; w = findWidget(name); return w; } void * GetWidget (std::string name) { Widget *w; w = findWidget(name); return reinterpret_cast(w); } typedef void * HANDLE; HANDLE GetWidget (std::string name) { Widget *w; w = findWidget(name); return reinterpret_cast(w); }

    В первом примере представлены внутренние сведения об API: он позволяет коду пользователя знать, что GetWidget возвращает указатель на struct Widget . Это имеет несколько следствий:

    • код пользователя должен иметь доступ к файлу заголовка, который определяет структуру Widget struct
    • код пользователя может потенциально изменять внутренние части возвращенной структуры Widget

    Оба эти последствия могут быть нежелательными.

    Второй пример скрывает эту внутреннюю деталь от кода пользователя, возвращая только void * . Пользовательскому коду не нужен доступ к заголовку, который определяет структуру Widget .

    Третий пример точно такой же, как второй, но мы просто вызываем void * a HANDLE . Возможно, это препятствует тому, чтобы пользовательский код пытался выяснить, что именно указывает void * .

    Зачем переживать эту проблему? Рассмотрим этот четвертый пример более новой версии этого же API:

    Typedef void * HANDLE; HANDLE GetWidget (std::string name) { NewImprovedWidget *w; w = findImprovedWidget(name); return reinterpret_cast(w); }

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

    Ручки в этом примере - это просто новое, предположительно более дружелюбное имя для void * , что именно соответствует HANDLE в Win32 API (посмотрите в MSDN). Он обеспечивает непрозрачную стену между кодом пользователя и внутренними представлениями библиотеки Win32, что повышает переносимость между версиями Windows кода, использующего Win32 API.

    A HANDLE в программировании Win32 - это токен, представляющий ресурс, управляемый ядром Windows. Ручкой может быть окно, файл и т.д.

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

    Так, например, если вы хотите создать окно и показать его на экране, вы можете сделать следующее:

    // Create the window HWND hwnd = CreateWindow(...); if (!hwnd) return; // hwnd not created // Show the window. ShowWindow(hwnd, SW_SHOW);

    В приведенном выше примере HWND означает "дескриптор окна".

    Если вы привыкли к объектно-ориентированному языку, вы можете придумать РУЧКУ как экземпляр класса без каких-либо методов, которые заявляют, что они могут быть изменены только другими функциями. В этом случае функция ShowWindow изменяет состояние окна HANDLE.

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

    Таким образом, на самом базовом уровне РУЧКА любого типа является указателем на указатель или

    #define HANDLE void **

    Теперь о том, почему вы хотели бы использовать его

    Позволяет выполнить настройку:

    Class Object{ int Value; } class LargeObj{ char * val; LargeObj() { val = malloc(2048 * 1000); } } void foo(Object bar){ LargeObj lo = new LargeObj(); bar.Value++; } void main() { Object obj = new Object(); obj.val = 1; foo(obj); printf("%d", obj.val); }

    Итак, поскольку obj передается по значению (сделайте копию и передайте его функции) в foo, printf напечатает исходное значение 1.

    Теперь, если мы обновим foo до:

    Void foo(Object * bar) { LargeObj lo = new LargeObj(); bar->val++; }

    Есть вероятность, что printf напечатает обновленное значение 2. Но есть также вероятность того, что foo приведет к некоей форме повреждения или исключения памяти.

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

    Последнее обновление для foo:

    Void foo(Object **bar){ LargeObj lo = LargeObj(); Object * b = &bar; b->val++; }

    Это всегда будет печатать обновленное значение.

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

    Любые конкретные типы HANDLE (hWnd, FILE и т.д.) являются специфичными для домена и указывают на определенный тип структуры для защиты от повреждения памяти.