Public void c что это
Смысл типа void и его предназначение тесно связаны с основной, самой мощной (и при неумелом применении деструктивной) особенностью языка C — использование указателей. Поэтому сначала нужно разобраться, что такое указатели и для чего они нужны (кто не знает, что такое указатели, см. врезку ниже).
Для работы со встраиваемыми системами критически важно хорошо программировать на языке C и иметь четкое представление о смысле указателей (здесь приведен перевод статьи [1]). Указатель так важен потому, что он позволяет программисту получить доступ к памяти системы самым быстрым и эффективным способом (что для встраиваемых систем критически важно). Память системы организована как последовательность байт (1 байт состоит из 8 битов). Если, к примеру, общая память в системе имеет размер 128 байт, то здесь будет 128 доступных ячеек, каждая из которых будет байтом, который можно прочитать и записать. Все 128 ячеек памяти будут пронумерованы числами от 0 до 127 специальным способом, примерно так: 0000, 0001, 0002, . и т. д. Это число, связанное с каждым байтом, называется адресом ячейки памяти.
Ниже на рисунке для иллюстрации всей идеи показана организация памяти некогда очень популярной архитектуры 8051.

Указатель это переменная, которая содержит адрес ячейки памяти. Если, к примеру, адрес ячейки 2050H, то указатель используется для того, чтобы хранить в себе это значение адреса.
Примечание: адрес ячейки памяти это всегда положительное целое число. Диапазон адресов простирается от 0 (адрес первой ячейки памяти; часто этот адрес имеет специальное назначение, об этом позже) до положительной целочисленной константы (которая является адресом последней ячейки памяти).
[Переменные указателей]
Мы можем использовать переменные для хранения адресов памяти, и такие переменные известны как переменные указателей. Обычные переменные используются для хранения в себе значений каких-то данных определенного типа (char, int, float и т. д.). Перед использованием переменной в программе мы сначала её декларируем. Специальным образом нам нужно также декларировать переменные и для указателей – чтобы компилятор знал, что мы декларируем переменную как указатель (это не обычная переменная). Делается такая декларация с помощью оператора *, это так называемый оператор косвенного обращения на языке C (indirection operator), иногда его называют оператором разыменования.
Общий синтаксис декларации указателя следующий:
Здесь мы декларировали переменную указателя с именем ptr, и этот указатель предназначен для указания на первую ячейку памяти, где находится значение типа int.
Почему для указателей нужно указывать тип данных? По некоторому адресу в памяти могут содержаться какие-то данные, и это понятно. И это может быть данные любого типа char, int, float, даже структура, и т. д. Разница между типами в том, что они могут занимать для себя разное количество памяти. Char требует 1 байт, int может требовать 2 байта (хотя это не всегда так), и float занимает 4 байта. Память, выделенная для всех этих типов это последовательность из нескольких непрерывно следующих друг за другом байт.
Давайте рассмотрим сценарий наподобие следующего, в программе определены 3 переменные:
Предположим, что память системы начинается с адреса 2000H. Теперь символьная переменная ‘a’ будет находиться по адресу 2000H (и займет в памяти 1 байт), тогда как int-переменная ‘b’ займет 2 байта и будет находиться по адресам 2001H и 2002H. И наконец, последняя float-переменная ‘c’ займет 4 байта, и они будут находится в расположенных друг за другом байтах памяти с адресами 2003H, 2004H, 2005H, 2006H. Теперь Вы можете догадаться, зачем надо указывать типы данных, чтобы объявить переменную указателя. Причина в том, что области памяти под переменные выделяются в последовательных, находящихся друг за другом байтах памяти, и количество выделенных байт зависит от типа переменной, которая в них содержится.
Примечание: показанное в этом примере распределение памяти типично для 8-разрядных систем, таких как MSC-51 и AVR. Для 16-битных и 32-разрядных систем реальное распределение памяти для переменных может быть другим, что связано с выравниванием данных в памяти с целью более эффективного использования особенностей процессора.
Таким образом, когда мы декларируем переменную указателя как float *ptr, и затем присваиваем ей адрес обычной float-переменной c, то для компилятора устанавливается привязка указателя ptr ко всей области памяти от 2003H до 2006H. Но сама переменная ptr будет хранить в себе только начальный адрес блока памяти переменной (т. е. в нашем примере это 2003H), а тип указателя будет указывать для компилятора размер этого блока.
Следовательно, чтобы компилятор мог корректно интерпретировать содержимое памяти, соответствующее указателю, для указателя должен быть при декларации указан тип данных. И этот тип данных должен совпадать с типом данных, которые находятся по адресу переменной – тому адресу, который присваивается переменной указателя. Например, если адрес 2000H будет присвоен указателю ptr, то указателю будет соответствовать память, в которой находится символьная переменная ‘a’. В этом случае переменная указателя ptr должна быть декларирована с типом char, как показано ниже:
Примечание: фактически мы могли бы декларировать переменную указателя без какого либо типа данных, используя ключевое слово void. Тогда получится так называемый пустой указатель.
[Присваивание адреса переменной указателя]
Чтобы можно было использовать указатель и его возможности, указателю должен быть присвоен адрес переменной. Указателю можно присвоить адрес как одиночной переменной, так и адрес массива, так и адрес структуры, и адрес переменной класса, и даже адрес переменной указателя. Это делает указатели особенно мощным (но и достаточно опасным при неумелом использовании) инструментом в программировании на языке C. Мы можем играючи обращаться с памятью системы, используя указатели.
Чтобы присвоить адрес переменной указателя мы используем оператор & (оператор взятия адреса переменной). Оператор & возвратит начало места в памяти, где расположена переменная. Пример:
[Обращение к содержимому памяти по указателю]
Теперь мы знаем, как присваивать адрес переменной указателя. Но как можно в программе обратиться к содержимому переменной, адрес которой присвоен указателю? Для этого мы используем тот же оператор косвенного обращения *, который мы использовали для декларации переменной указателя. Операция взятия значения по указателю также называется разыменованием указателя.
Запуск этого кода выведет следующую строку:
[Арифметические операции над указателями]
С типизованным указателями указателями (т. е. с такими указателями, для которых для декларации явно указан тип) можно использовать многие операции, которые доступны с целыми числами без знака: декремент —, инкремент ++, прибавление и вычитание константы. При этом будет меняться адрес, сохраненный в указателе на величину, кратную размеру типа указателя. Например, если прибавить к указателю на float константу 1, то сохраненный в указателе адрес увеличится на 4, потому что размер типа float равен 4 байтам.
[Указатели void на языке C]
Обычно переменная указателя декларируется с указанием типа данных содержимого, которое хранится в том месте памяти, на которое ссылается указатель (перевод статьи [2]). Примеры:
Переменная указателя, декларированная на определенный тип, не может содержать в себе адрес переменной другого типа. Это неправильно, и приведет к сообщению об ошибке при компиляции. Пример:
На языке C есть возможность создать указатель на неопределенный тип, так называемый «пустой указатель» (void pointer). Указатель на void это просто переменная указателя, которая декларирована с зарезервированным на языке C ключевым словом void. Пример:
Когда указатель декларируется с ключевым словом void, он становится универсальным. Это значит, что ему может быть присвоен адрес переменной любого типа (char, int, float и т. д.), и это не будет ошибкой.
[Разыменование void-указателя]
Как делается разыменование типизованных указателей с помощью оператора *, Вы уже знаете (если нет, то см. врезку «Что такое указатель»). Но в случае указателя на void нужно использовать приведение типа (typecast) переменной указателя, чтобы выполнить её разыменование (выполнить обращение к содержимому памяти, на которую ссылается void-указатель). Причина в том, что с void-указателем не связан никакой тип, и для компилятора нет никакого способа автоматически узнать, как обращаться к содержимому памяти, связанному с void-указателем. Таким образом, чтобы получить данные, на который ссылается void-указатель, мы делаем приведение указателя к корректному типу данных, которые находятся по адресу, содержащемуся в void-указателе.
Указатели void полезны для программиста, когда заранее неизвестно о типе данных, которые поступают на вход программы. Типичным примером могут служить библиотечные функции манипулирования блоками памяти memcpy, memset и т. п. С помощью void-указателя программист может сослаться на место размещения данных произвольного, заранее неизвестного типа данных. Программа, к примеру, может быть написана таким образом, чтобы запросить у пользователя, какое приведение типа нужно использовать, чтобы правильно обработать входные данные. Ниже приведен в качестве примера кусок подобного кода.
При использовании void-указателей следует помнить, что для них недопустимы арифметические операции, как для типизованных указателей (см. врезку «Что такое указатель»). Пример:
Методы в языке C#
В языке С, дедушке языка C#, все было очень просто: данные это числа, строки, массивы; программный код – операторы языка и функции. Функции в C, говоря языком структурного программирования, это подпрограммы.
С переходом от императивного языка C к объектно-ориентированному языку C++ понятие функции (как подпрограммы) сохранилось, однако каждая функция стала принадлежать какому-либо классу. Отметим что в С++ были добавлены специальные функции – конструкторы и деструкторы.
В язык C#, являющийся прямым наследником языка C++, многое перенесено без особых изменений, однако введенная терминология поначалу может ввести вас в заблуждение.
Разберемся с терминами и понятиями более подробно.
Оказывается, что к данным (данные-члены класса) теперь относятся не только поля, но и константы и события, а все, что содержит программный код, теперь называется функциями-членами класса, среди которых традиционная функция теперь называется методом класса.
Авторы языка определили, что функции-члены — это члены, которые обеспечивают некоторую функциональность для манипулирования данными класса. Они включают методы, свойства, конструкторы, финализаторы (деструкторы в С++), операции и индексаторы.
Для начинающего программиста важно освоить, прежде всего, работу с методами класса, а по сути (в прежнем понимании) – с функциями как подпрограммами. К этому добавим работу с конструкторами классов, а все остальное будет рассмотрено позже.
Шаблон объявления метода
[модификаторы] тип_возвращаемого_значения ИмяМетода([параметры])
<
// Тело метода
>
В C# определение метода состоит из указания модификаторов (static, public и т.п., модификаторы не обязательны), типа возвращаемого значения, имени метода и списка параметров в круглых скобках. Далее в фигурных скобках записывается тело метода.
Каждый параметр состоит из имени типа параметра и имени, по которому к нему можно обратиться в теле метода.
Возврат из метода и возврат значения
Если метод возвращает значение, то для указания точки выхода должен использоваться оператор возврата return вместе с возвращаемым значением. Если метод не возвращает ничего, то в качестве типа возврата указывается void (опустить тип возврата невозможно).
Если метод не принимает параметров, то все равно после имени метода должны присутствовать пустые круглые скобки (. ). При этом включать в тело метода оператор возврата не обязательно — метод возвращает управление автоматически по достижении закрывающей фигурной скобки.
В целом, возврат из метода может произойти при двух условиях. Во-первых, когда встречается фигурная скобка, закрывающая тело метода. И во-вторых, когда выполняется оператор return. Имеются две формы оператора return: одна — для методов типа void (возврат из метода), т.е. тех методов, которые не возвращают значения, а другая — для методов, возвращающих конкретные значения.
Использование параметров
При вызове метода ему можно передать одно или несколько значений. Значение, передаваемое методу, называется аргументом. А переменная, получающая аргумент, называется формальным параметром, или просто параметром.
Параметры объявляются в скобках после имени метода. Синтаксис объявления параметров такой же, как и у переменных. А областью действия параметров является тело метода. За исключением особых случаев передачи аргументов методу, параметры действуют так же, как и любые другие переменные.
Два способа передачи параметров. В общем случае параметры могут передаваться методу либо по значению, либо по ссылке в зависимости от типа данных.
Если переменная, указанная в списке параметров, относится к типу значений (int, double, bool и т.п.), то вызываемый метод получает копию этой переменной, а это значит, что все изменения в ней, выполненные внутри метода будут утеряны.
Если переменная из списка параметров относится к ссылочному типу (например, массив), то параметр передается по ссылке, вызываемый метод получает саму переменную, поэтому любые изменения, которым она подвергнется внутри метода, останутся в силе после его завершения.
Поведение строк также отличается. Дело в том, что строки являются неизменными (изменение значения строки приводит к созданию совершенно новой строки), поэтому строки не демонстрируют поведение, характерное для ссылочных типов. Любые изменения, проведенные в строке внутри метода, не влияют на исходную строку.
Передача структуры (struct) как параметра через ее имя также произойдет через копирование (помните, структура относится к типу значений).
Если же для каких-либо типов данных необходима передача по ссылке (она более эффективна при большом объеме данных, который приходится копировать), то перед указанием типа параметра достаточно указать модификатор ref (reference – ссылка, англ.).
Рассмотрим три примера.
ПРИМЕР 1. В статье Первая программа на языке Си шарп мы разбирали программу:
В классе Program объявляется метод Main() (главная функция приложения), при выполнении которого вызываются два метода класса Console: WriteLine() (вывод строки текста) и ReadKey() (чтение символа с клавиатуры).
Ключевое слово static означает, что данный метод принадлежит классу Program.
Модификатор void указывает, что метод не возвращает никаких параметров.
Метод Main() в качестве списка параметров имеет массив строк. Отметим, что список параметров args в Main() пропустить. Метод WriteLine() имеет один параметр – строку, метод ReadKey() параметров не имеет.
ПРИМЕР 2. Объявление метода в примере Вычисление функции sin(x) выглядит так:
Параметром (аргументом) функции объявляется переменная x типа double, метод возвращает результат также типа double (последний оператор метода return s;). Метод является статическим. Для вызова метода достаточно написать оператор y=Sin2(x);
ПРИМЕР 3. Передача параметров по значению, ссылке и через статическую переменную
Объявим в классе Program структуру West с двумя полями, статическую переменную f и метод vp( ):
static void vp(int[] b, West u, string t, double d, ref decimal y)
где b – массив, u – структура, t – строка, d – вещественное число, y – десятичный тип, передается по ссылке (модификатор ref).
В методе изменим эти переменные, а также и f для того, чтобы проверить способы передачи параметров (копированием или по ссылке).
Напечатаем эти значения до и после вызова метода vp().

Результат:
Обратите внимание, что второй элемент массива a[1], десятичное число x (передаваемое через ссылку — ref) и статическая переменная f изменились, а структура u, строка t и вещественное число d не изменились.
Отметим, что статическое поле f, объявленное в классе, доступно методам этого класса без механизма передачи параметров.
Некоторые итоги:
1) переменные, объявленные внутри метода или в списке его параметров, являются локальными переменными;
2) при вызове метода параметры из списка в заголовке метода либо копируются в локальные переменные метода, либо передаются по ссылке;
3) копируются все встроенные типы значений, структуры и (по сути) строки, хотя и они относятся к ссылочным типам;
4) по ссылке передаются массивы и другие объекты. Если их поля будут изменены внутри вызываемого метода, то эти изменения сохранятся при возврате в вызывающий метод;
5) передачу параметров по ссылке (без копирования в локальные переменные метода) можно обеспечить, используя модификатор ref. Однако это не имеет смысла для переменных ссылочного типа («масло масленое»);
6) нет необходимости передавать статические переменные класса через список параметров, в рамках одного класса они имеют смысл глобальных переменных в императивных языках программирования (хотя термин «глобальная переменная» в C# не употребляют);
7) для доступа к данным-членам и функциям-членам других классов необходимо указать имя класса, поставить точку, указать имя члена класса, например: Math.PI — константа, число пи=3,14158… , Math.Tan(x) — тангенс числа x, заданного в радианах;
8) доступ к членам других классов зависит от уровня его приватности (public, но не private или protected).
Как и во многих языках программирования для реализации рекурсивных алгоритмов в C# можно использовать рекурсивные методы.
Завершим раздел рассмотрением двух из трех ключевых принципов ООП — наследования и полиморфизма, считаю принцип инкапсуляции (объединение данных и и методов в класс) уже достаточно ясным.
NEW: Наш Чат, в котором вы можете обсудить любые вопросы, идеи, поделиться опытом или связаться с администраторами.
Что означает void в C, С++ и С#?
Глядя на то, чтобы получить основы, на которых исходит термин VOID, и почему его называют недействительным. Цель вопроса состоит в том, чтобы помочь кому-то, у кого нет опыта работы в C, и вдруг смотрит на кодовую базу на основе C.
ОТВЕТЫ
Ответ 1
В основном это означает «ничего» или «нет типа»
Существует 3 основных способа использования void:
Аргумент функции: int myFunc(void) — функция ничего не принимает.
Возвращаемое значение функции: void myFunc(int) — функция ничего не возвращает
Общий указатель данных: void* data — «данные» — это указатель на данные неизвестного типа и не может быть разыменован
Примечание: параметр void в аргументе функции является необязательным в С++, поэтому int myFunc() точно такой же, как int myFunc(void) , и он полностью не учитывается в С#. Это всегда необходимо для возвращаемого значения.
Ответ 2
Я всегда считал это отсутствующим. Вот четыре случая на языке C, которые соответствуют этому использованию отсутствующих
- R f(void) — отсутствуют функциональные параметры
- void f(P) — Возвращаемое значение отсутствует
- void *p — Тип того, на что указывает, отсутствует
- (void) p — Использование значения отсутствует
Другие потомки C используют его для других вещей. Язык программирования D использует его для случаев, когда инициализатор отсутствует
- T t = void; — значение инициализации отсутствует
Ответ 3
Это означает «нет значения». Вы используете void , чтобы указать, что функция не возвращает значение или что у него нет параметров или обоих. В значительной степени соответствует типичному использованию слова void на английском языке.
Ответ 4
Существует два способа использования void:
Первое указывает, что никакой аргумент не передается или что аргумент не возвращается.
Второй сообщает компилятору, что нет никакого типа, связанного с данными, что означает, что вы не можете использовать данные, на которые указывает, пока он не будет передан известному типу.
Например, вы увидите, что void* используется много, когда у вас есть интерфейс, который вызывает функцию, параметры которой не могут быть известны заранее.
Например, в ядре Linux при отсрочке работы вы настроите функцию, которая будет запущена в последний раз, указав ей указатель на функцию, которая будет запущена, и указатель на данные, которые будут переданы функции:
Затем поток ядра просматривает список отложенной работы, и когда он достигает этого node, он эффективно выполняет:
Затем в баре вы:
Ответ 5
Указывает на отсутствие возвращаемого значения в функции.
Некоторые языки имеют два вида подпрограмм: процедуры и функции. Процедуры — это всего лишь последовательность операций, тогда как функция представляет собой последовательность операций, возвращающих результат.
В C и его производных разница между ними не является явной. Все в принципе является функцией. ключевое слово void указывает, что это не «фактическая» функция, так как она не возвращает значение.
Ответ 6
Подумайте о пустоте как о «пустой структуре». Позвольте мне объяснить.
Каждая функция принимает последовательность параметров, где каждый параметр имеет тип. Фактически, мы могли бы упаковать параметры в структуру, а слоты структуры соответствовали параметрам. Это означает, что каждая функция имеет ровно один аргумент. Аналогично, функции производят результат, который имеет тип. Это может быть логическое значение, или оно может быть плавающим, или это может быть структура, содержащая произвольный набор других типизированных значений. Если мы хотим, чтобы язык имел несколько возвращаемых значений, легко просто настаивать на том, чтобы они были упакованы в структуру. На самом деле мы всегда могли утверждать, что функция вернула структуру. Теперь каждая функция принимает ровно один аргумент и производит ровно одно значение.
Теперь, что происходит, когда мне нужна функция, которая производит «нет» значение? Ну, рассмотрим, что я получаю, когда я формирую структуру с тремя слотами: это имеет 3 значения. Когда у меня есть 2 слота, он имеет два значения. Когда это имеет один слот, одно значение. И когда он имеет нулевые слоты, он держится. мм, нулевые значения или «no» значение. Итак, я могу думать о функции, возвращающей void как возврат структуры, не содержащей значений. Вы даже можете решить, что «пустота» является просто синонимом типа, представленного пустой структурой, а не ключевое слово в языке (возможно, это только предопределенный тип:)
Аналогично, я могу думать о функции, не требующей значений, принимающих пустую структуру, например, «void».
Я даже могу реализовать свой язык программирования таким образом. Передача значения пустоты занимает нулевые байты, поэтому передача значений void является лишь частным случаем прохождения другие значения произвольного размера. Это упрощает компилятор для обработки «void» результат или аргумент. Вероятно, вам нужна функция langauge который может отбросить функцию; в C, если вы вызываете непустой результат function foo в следующем выражении: Foo (. ); компилятор знает, что foo производит результат и просто игнорирует его. Если void является значением, это отлично работает и теперь «процедуры» (которые просто прилагательное для функции с результатом void) просто тривиальны случаи общих функций.
Void * немного смешнее. Я не думаю, что дизайнеры C думали о пустоте в выше; они просто создали ключевое слово. Это ключевое слово было доступно, когда кто-то нужна точка произвольному типу, поэтому void * как идиома в C. Это действительно работает очень хорошо, если вы интерпретируете пустоту как пустую структуру. Указатель void * — это адрес места, где эта пустая структура имеет был поставлен.
Отбрасывает от void * до T * для других типов T, также работает с этой перспективой. Присвоения указателей — это полный чит, который работает на большинстве распространенных архитектур, чтобы воспользоваться тем фактом, что если у составного типа T есть элемент с подтипом S, физически расположенный в начале T в его макете хранения, тогда литье S * в T * и наоборот, использование одного и того же физического адреса устройства имеет тенденцию к разработке, поскольку большинство указателей машин имеют одно представление. Замена типа S по типу void дает точно такой же эффект, и, таким образом, отбрасывание в/из void * выполняется.
Язык программирования PARLANSE очень хорошо реализует приведенные выше идеи. Мы придумали его дизайн и не обращали пристального внимания на «пустоту» как на возвращение тип и, следовательно, имеют langauge ключевые слова для процедуры. Его в основном просто изменение синтаксиса, но это одна из вещей, с которыми вы не обходитесь, когда вы получаете рабочий код большого тела на языке.
Ответ 7
В С# вы должны использовать ключевое слово void, чтобы указать, что метод не возвращает значение:
Ответ 8
Три случая использования для void:
Подписи функций. void foo(int bar) не возвращает значение. int bar(void) не принимает никаких параметров, но обычно это выражается пустым списком аргументов: int bar() . Использование ключевого слова void здесь соответствует его значению на английском языке.
Общий указатель верхнего уровня void * , который указывает на неуказанные данные и не может быть разыменован. Здесь значение void отличается от других значений void: universal type vs. no type.
В выражениях, таких как (void) new Foo(this) , означает, что возвращаемое значение намеренно выбрасывается. Здесь использование ключевого слова также соответствует его значению на английском языке.
Случаи 1 и 2 уже были охвачены компанией @Gerald, но случай 3 еще не был рассмотрен.
Ответ 9
Если вы объясняете концепцию новичку, может быть полезно использовать аналогию. Использование void во всех этих случаях аналогично по значению странице в книге, которая имеет следующие слова: «Эта страница оставлена умышленно пустой». Он должен отличать компилятор от того, что должно быть помечено как ошибка, по сравнению с типом, который намеренно остается пустым, потому что это поведение, которое вы хотите.
Он всегда появляется в коде, где обычно вы ожидаете увидеть тип, например, тип возврата или тип указателя. Вот почему в С#, void отображает фактический тип CLR, System.Void, потому что это сам тип.
Некоторые языки программирования никогда не разрабатывали концепцию пустоты, точно так же, как некоторые человеческие культуры никогда не изобретали концепцию нулевого числа. Void представляет такое же продвижение на языке программирования, как понятие нуля представляет собой человеческий язык.
Ответ 10
Это означает «нет значения». Вы используете void, чтобы указать, что функция не возвращает значение или что у него нет параметров или обоих. Это очень похоже на типичное использование слова void на английском языке.
Нельзя путать пустоту с нулем. Null означает для переменной, адрес которой находится в стеке, значение в куче для этого адреса пусто.
Ответ 11
Пустота используется только в сигнатурах метода. Для типов возвращаемых значений это означает, что метод ничего не возвращает вызывающему коду. Для параметров это означает, что никакие параметры не передаются методу
В С# мы можем опустить void для параметров и записать код выше:
Void не следует путать с нулем. Нуль означает переменную, адрес которой находится в стеке, значение в куче для этого адреса пуст.
Ответ 12
Пустота — это неполный тип, который дефинированием не может быть lvalue. Что означает, что ему не может быть присвоено значение.
Таким образом, он также не может иметь никакого значения.
Ответ 13
void означает, что вы не будете возвращать какое-либо значение из функции или метода
Ответ 14
Void означает, что в возвращаемом типе значение не требуется для функции во всех трех языках.