Для чего нужен union
Продолжаю открывать для себя С++...
Объединение служит для, как бы это ни казалось странным, объединения.
Это позволяет, записывая данные в одно поле union'а, одновременно получать эти же данные в другом поле. Причем поля могут быть разные по содержимому.
Пример:
typedef struct
{
unsigned short fbNVRIncorrectCRC :1;
unsigned short fbConfigIncorrectCRC :1;
unsigned short fbSPIInterfaceError :1;
unsigned short fbMFIncorrectCRC :1;
unsigned short fbMFWriteError :1;
unsigned short fbMFNotInstalled :1;
unsigned short fbEEJFatalError :1;
unsigned short fbUnused1 :1;
unsigned short fbNotInitialized :1;
unsigned short fbNonFiscalMode :1;
unsigned short fbShiftOpened :1;
unsigned short fb24HourOverflow :1;
unsigned short fbEEJArchieveClosed :1;
unsigned short fbEEJNotActivated :1;
unsigned short fbMFNoMemoryForShift :1;
unsigned short fbMFWrongPassword :1;
unsigned char dbDocumentType;
} ST_FRSTATUS;
typedef union
{
ST_FRSTATUS stStatus;
unsigned char dbStatus[3];
} UN_FRSTATUS;
И теперь можно записать данные в массив unsigned char dbStatus[3]; и потом получить их из структуры уже разобранными.
Т.е. можно обратиться к dbStatus[2] и к stStatus.dbDocumentType и получить один и тот же результат.
Единственное - нельзя обращаться по разным именам в одном блоке программы..
[Android] Поворот экрана
Варианта управления экраном три:
1. Запретить поворот экрана
В раздел activity вписать
android:screenOrientation="portrait"
или
android:screenOrientation="landscape"
2. Обрабатываем поворот вручную
Т.к. метод onCreate() вызывается не только при создании Activity, но и при повороте экрана, можно отловить данные события.
Подробно можно почитать тут - http://developer.android.com/guide/topics/resources/runtime-changes.html (Кстати, там, возможно, опечатка)
Коротко:
Добавляем в класс Activity метод:
@Override
public Object onRetainNonConfigurationInstance() {
final MyDataObject data = collectMyLoadedData();
return data;
}
В котором производим сборку всех данных, которые нужно будет снова показать после поворота.
И в метод onCreate добавляем конструкцию:
final MyDataObject data = (MyDataObject) getLastNonConfigurationInstance();
if (data != null) {
loadMyData();
}
В которой данные распаковываем обратно.
3. Запрет автоматического пересоздания Activity
В AndroidManifest.xml дописать в раздел activity
android:configChanges="orientation|keyboardHidden"
Метод не рекомендуемый, т.к. в сложных приложениях придется самостоятельно обновлять данные внутри программы.
Подробнее можно тут: http://stackoverflow.com/questions/7818717/why-not-use-always-androidconfigchanges-keyboardhiddenorientation
Ну или в официальной доке.
Еще немного о классах
Класс - это упрощенное описание каких-нибудь реальных (чаще, правда, выдуманных) вещей.
Вот, например, точка. Единственное, что есть у точки - это её координаты в пространстве. Это её параметры.
Как задать точку на плоскости? Надо нарисовать точку и подписать её: M(1,2), т.е. точка с именем M и координатами х = 1, у = 2.
Это её "конструктор". Т.е. мы "сконструировали" конкретную точку М с конкретными координатами.
На языке Java это будет выглядеть так:
class Point { //общее описание точек
private double x, y; //параметры точки
Point(double ax, double ay) { //конструктор
x = ax;
y = ay;
}
}
Сконструируем точку
Point M = new Point(1, 2);
В данном классе задан один конкретный конструктор. Из-за этого каждая создаваемая точка будет ожидать, что при создании ей назначат координаты.
Т.е. данная строчка работать не будет:
Point M = new Point(); //пустые параметры //или Point M = new Point(1); //неполные параметры
Дальше. Класс включает в себя не только параметры, но и методы.
Что такое метод класса? Это вопрос, который мы можем задать или указание, что-либо сделать. Причем и вопрос и указания нужно адресовать уже конкретной сконструированной точке.
Научим точку сообщать свои координаты и перемещаться по одной из координатных осей:
class Point { //общее описание точек
private double x, y; //параметры точки
Point(double ax, double ay) { //конструктор
x = ax;
y = ay;
}
//запрос координаты Х
double getX() {
return x;
}
//запрос координаты Y
double getY() {
return y;
}
//указание сдвинуться по оси X на расстояние dx
void moveX(double dx) {
x = x + dx;
}
//указание сдвинуться по оси Y на расстояние dy
void moveY(double dy) {
y = y + dy;
}
}
//использование
public class Main {
public static void main(String[] args) {
//создаём точку
Point M = new Point(1, 2);
//спрашиваем у созданной точки - скажи нам, точка М свои координаты X и Y
System.out.println("Текущие координаты точки М: x = " + M.getX() + ", у = " + M.getY());
System.out.println("Сдвинем точку по оси Х на 5 и по оси У на -1");
//просим точку М - точка М, сдвинься на 5 по оси X
M.moveX(5);
//просим точку М - точка М, сдвинься на -1 по оси Y
M.moveY(-1);
//спрашиваем у точки М - скажи нам, точка М свои координаты X и Y
System.out.println("Новые координаты точки М: x = " + M.getX() + ", у = " + M.getY());
}
}
Еще раз повторю - методы вызываются у конкретной точки - сконструированного объекта.
Класс - это просто заготовка, шаблон, описание.
Т.е. пока точка не создана - мы как бы задумываем её нарисовать.
А вот когда сказали new, это значит, что мы нарисовали конкретную точку в конкретном месте координатной плоскости.
Записи вида
Point.getX(); //или Point.moveX(5);
Не имеют смысла, потому что описание ничего не знает.
Это как если спросить у человека - "скажи мне координаты точки?"
Логично будет подумать, что он ответит - "какой именно?"
Также делает и компилятор.
[Modbus] Функции 16-17
- 16 (10 Hex) Preset Multiple Regs
- 17 (11 Hex) Чтение идентификатора подчиненного
[Modbus] Функции 20-24
- 20 (14 Hex) Read General Reference
- 21 (15 Hex) Write general reference
- 22 (16 Hex) Mask Write 4X Register
- 23 (17 Hex) Read/Write 4X Registers
- 24 (18 Hex) Read FIFO Queue
[Modbus] Функции 07, 11, 12, 15
- 07 Read Exception Status
- 11 (0B Hex) Fetch Comm Event Ctr
- 12 (0C Hex) Fetch Comm Event Log
- 15 (0F Hex) Force Multiple Coils
[Java] Структуры данных. Список
Список, для начала, возьмем простой линейный односвязный.

Список есть динамическая структура данных.
Каждый элемент списка содержит в себе данные и указатель на следующий элемент.
Возьмем простой вариант данных в списке - просто целые числа.
Элемент списка:
class ListElement {
ListElement next; // указатель на следующий элемент
int data; // данные
}
Сам список также будет классом.
Будет содержать в себе указатель на первый элемент списка и на последний - head и tail.
Также реализуем методы:
1. Добавление элемента в конец списка
2. Добавление элемента в начало списка
3. Удаление элемента по значению
4. Печать всего списка
class List {
private ListElement head; // указатель на первый элемент
private ListElement tail; // указатель последний элемент
void addFront(int data) //добавить спереди
{
ListElement a = new ListElement(); //создаём новый элемент
a.data = data; //инициализируем данные.
// указатель на следующий элемент автоматически инициализируется как null
if(head == null) //если список пуст
{ //то указываем ссылки начала и конца на новый элемент
head = a; //т.е. список теперь состоит из одного элемента
tail = a;
}
else {
a.next = head; //иначе новый элемент теперь ссылается на "бывший" первый
head = a; //а указатель на первый элемент теперь ссылается на новый элемент
}
}
void addBack(int data) { //добавление в конец списка
ListElement a = new ListElement(); //создаём новый элемент
a.data = data;
if (tail == null) //если список пуст
{ //то указываем ссылки начала и конца на новый элемент
head = a; //т.е. список теперь состоит из одного элемента
tail = a;
} else {
tail.next = a; //иначе "старый" последний элемент теперь ссылается на новый
tail = a; //а в указатель на последний элемент записываем адрес нового элемента
}
}
void printList() //печать списка
{
ListElement t = head; //получаем ссылку на первый элемент
while (t != null) //пока элемент существуе
{
System.out.print(t.data + " "); //печатаем его данные
t = t.next; //и переключаемся на следующий
}
}
void delEl(int data) //удаление элемента
{
if(head == null) //если список пуст -
return; //ничего не делаем
if (head == tail) { //если список состоит из одного элемента
head = null; //очищаем указатели начала и конца
tail = null;
return; //и выходим
}
if (head.data == data) { //если первый элемент - тот, что нам нужен
head = head.next; //переключаем указатель начала на второй элемент
return; //и выходим
}
ListElement t = head; //иначе начинаем искать
while (t.next != null) { //пока следующий элемент существует
if (t.next.data == data) { //проверяем следующий элемент
if(tail == t.next) //если он последний
{
tail = t; //то переключаем указатель на последний элемент на текущий
}
t.next = t.next.next; //найденный элемент выкидываем
return; //и выходим
}
t = t.next; //иначе ищем дальше
}
}
}
Тонкости Java-реализации:
Освобождать память не надо - достаточно чтобы на выделенные по new участок не было указателей и тогда сборщик мусора сам её почистит.
Все переменные являются ссылками на объекты, так что нет заморочек с разыменование указателей и передачи адресов.
Испоьзование:
public class Main {
public static void main(String[] args) {
List ml = new List();
ml.addBack(1);
ml.addBack(2);
ml.addBack(3);
ml.addFront(6);
ml.printList();
System.out.println();
ml.delEl(6);
ml.delEl(5);
ml.delEl(12);
ml.delEl(2);
ml.printList();
System.out.println();
}
}
Распечатает:
6 1 2 3 1 3
А теперь немного магии :)
С целочисленным списком скучно. Сделаем элемент списка универсальным!
Это можно сделать при помощи "шаблонизации"
Причем какой именно тип - будет автоматически определяться при сборке!
class ListElement<E> {
ListElement next;
E data;
}
class List<E> {
private ListElement head; // указатель на первый элемент
private ListElement tail; // указатель последний элемент
void addFront(E data) //добавить спереди
{
ListElement a = new ListElement(); //создаём новый элемент
a.data = data; //инициализируем данные.
// указатель на следующий элемент автоматически инициализируется как null
if(head == null) //если список пуст
{ //то указываем ссылки начала и конца на новый элемент
head = a; //т.е. список теперь состоит из одного элемента
tail = a;
}
else {
a.next = head; //иначе новый элемент теперь ссылается на "бывший" первый
head = a; //а указатель на первый элемент теперь ссылается на новый элемент
}
}
void addBack(E data) { //добавление в конец списка
ListElement a = new ListElement(); //создаём новый элемент
a.data = data;
if (tail == null) //если список пуст
{ //то указываем ссылки начала и конца на новый элемент
head = a; //т.е. список теперь состоит из одного элемента
tail = a;
} else {
tail.next = a; //иначе "старый" последний элемент теперь ссылается на новый
tail = a; //а в указатель на последний элемент записываем адрес нового элемента
}
}
void printList() //печать списка
{
ListElement t = head; //получаем ссылку на первый элемент
while (t != null) //пока элемент существуе
{
System.out.print(t.data + " "); //печатаем его данные
t = t.next; //и переключаемся на следующий
}
}
void delEl(E data) //удаление элемента
{
if(head == null) //если список пуст -
return; //ничего не делаем
if (head == tail) { //если список состоит из одного элемента
head = null; //очищаем указатели начала и конца
tail = null;
return; //и выходим
}
if (head.data == data) { //если первый элемент - тот, что нам нужен
head = head.next; //переключаем указатель начала на второй элемент
return; //и выходим
}
ListElement t = head; //иначе начинаем искать
while (t.next != null) { //пока следующий элемент существует
if (t.next.data == data) { //проверяем следующий элемент
if(tail == t.next) //если он последний
{
tail = t; //то переключаем указатель на последний элемент на текущий
}
t.next = t.next.next; //найденный элемент выкидываем
return; //и выходим
}
t = t.next; //иначе ищем дальше
}
}
}
Изменения минимальны.
Вместо int пишем E. И к имени класса добавляем
<E>
Зато теперь можно сделать вот так:
public class Main {
public static void main(String[] args) {
List ml = new List();
ml.addBack(1.0);
ml.addBack(2);
ml.addBack("brrr");
ml.addFront(6);
ml.printList();
System.out.println();
}
}
В выводе будет:
6 1.0 2 brrr
[Java] Библиотеки и пакеты
Библиотека Java — это сборник классов. Если программе нужен какой-то класс, то нужно подключить библиотеку, в которой этот класс находится.
Пакеты — это механизм, который служит как для работы с пространством имен, так и для ограничения видимости. У каждого файла .java есть 4 внутренних части.
Общая форма исходного файла Java:
- одиночный оператор package (необязателен)
- любое количество операторов import (необязательны)
- одиночное объявление открытого (public) класса
- любое количество закрытых (private) классов пакета (необязательны)
Оператор package
Сообщает транслятору, в каком пакете должны определяться содержащиеся в данном файле классы. Пакеты задают набор раздельных пространств имен, в которых хранятся имена классов. Если оператор package не указан, классы попадают в безымянное пространство имен, используемое по умолчанию. Если объявляется класс, принадлежащий определенному пакету, например,
package java.awt.image;
то и исходный код этого класса должен храниться в каталоге java/awt/image
Трансляция классов в пакетах
При попытке поместить класс в пакет, вы сразу натолкнетесь на жесткое требование точного совпадения иерархии каталогов с иерархией пакетов. Нельзя переименовать пакет, не переименовав каталог, в котором хранятся его классы. Эта трудность видна сразу, но есть и менее очевидная проблема.
Оператор import
После оператора package, но до любого определения классов в исходном Java-файле, может присутствовать список операторов import. Общая форма оператора import такова:
import пакет1 [.пакет2].(имякласса|*);
Здесь пакет1 — имя пакета верхнего уровня, пакет2 — это необязательное имя пакета, вложенного в первый пакет и отделенное точкой. И, наконец, после указания пути в иерархии пакетов, указывается либо имя класса, либо метасимвол звездочка. Звездочка означает, что, если Java-транслятору потребуется какой-либо класс, для которого пакет не указан явно, он должен просмотреть все содержимое пакета со звездочкой вместо имени класса.
import java.util.Date import java.io.*;
Замечание: Использование * может существенно увеличить время трансляции программы. На скорость работы и размер программы не влияет.
Если в двух пакетах, подключаемых с помощью формы оператора import со звездочкой, есть классы с одинаковыми именами, однако вы их не используете, транслятор не отреагирует. А вот при попытке использовать такой класс, вы сразу получите сообщение об ошибке, и вам придется переписать операторы import, чтобы явно указать, класс какого пакета вы имеете ввиду.
class MyDate extends Java.util.Date { }
Уровни доступа
В языке Java имеется три уровня доступа, определяемых ключевыми словами: private (закрытый), public (открытый) и protected (защищенный).
| private | модификатор отсутствует | private protected | protected | public | |
| тот же класс | да | да | да | да | да |
| подкласс в том же пакете | нет | да | да | да | да |
| независимый класс в том же пакете | нет | да | нет | да | да |
| подкласс в другом пакете | нет | нет | да | да | да |
| независимый класс в другом пакете | нет | нет | нет | нет | да |
- Элемент, объявленный public, доступен из любого места
- Все, что объявлено private, доступно только внутри класса, и нигде больше
- Если у элемента вообще не указан модификатор уровня доступа, то такой элемент будет виден из подклассов и классов того же пакета. (по умолчанию)
- Чтобы элемент был доступен извне пакета, но только подклассам того класса, которому он принадлежит, нужно объявить такой элемент protected
- Чтобы элемент был доступен только подклассам, причем независимо от того, находятся ли они в данном пакете или нет — используйте комбинацию private protected
Небольшой пример
Создаём новый проект и добавляем в папку src новый Пакет "alg" (New -> Package)
В папке пакета создаем класс sort (New -> Java Class)
В новом классе пишем наши уже знакомые функции сортировки с обязательным модификатором доступа public.
В итоге должно получиться следующее:
package alg;
public class sort {
public void gnom(int[] mas) {
int p = 2;
int i = 1;
for (; i < mas.length; ) {
if (mas[i - 1] <= mas[i]) {
p++;
i = p;
} else {
int a = mas[i];
mas[i] = mas[i - 1];
mas[i - 1] = a;
i--;
if (i == 0) {
p++;
i = p;
}
}
}
}
public void gnom(double[] mas) {
int p = 2;
int i = 1;
for (; i < mas.length; ) {
if (mas[i - 1] <= mas[i]) {
p++;
i = p;
} else {
double a = mas[i];
mas[i] = mas[i - 1];
mas[i - 1] = a;
i--;
if (i == 0) {
p++;
i = p;
}
}
}
}
}
Сохраняем. Создаём еще один проект. Или открываем старый.
Добавляем созданый модуль alg также как описывалось в предыдущей статье
Делаем новый класс и пишем туда первой строчкой:
import alg.sort;
Всё. Мы подключили наш пакет с функциями сортировки!
Дописываем функцию main
В итоге:
import alg.sort;
class Hello {
public static void main(String args[]) {
int masI[] = {5, 5, 9, 8, 0, -5, 9, 10};
double masD[] = {5.9, 5.7, 9.6, 8.9, 0.7, -5.6, 9.9, 10.0};
sort s1 = new sort();
s1.gnom(masI);
s1.gnom(masD);
}
[Java] Подключаем чужие исходники в IntelliJ IDEA
По следам статьи
В статье предлагается попробовать себя в роли разработчика Рогалика (Rogue-like) - игрушки, в которых вместо графики используется ASCII символы.
Разберем, как же подключить правильно сторонние библиотеки, чтобы всё работало.
Необходимо скачать две (а не одну, как сказано в статье) библиотеки: jcurses и libjcsi
Для скачивания исходников для libjcsi понадобится SVN клиент (надо будет написать про них немного)
Или можно взять тут: http://axis.bplaced.net/wp-content/uploads/2012/10/libjcsi.zip
Качаем исходники (Source) и распаковываем их куда-нибудь. У меня это C:\JavaLib\jcurses и C:\JavaLib\libjcsi
Создаём новый модуль (или открываем старый) и добавляем туда новый класс Rogalic.
Заходим в File -> Project Structure
Раздел Module, вкладка Dependencies.

Выбираем Create module from existing sources и жмем "..."

Жмем Next до упора и потом Finish (всё по умолчанию, все окна практически пустые)
Повторяем тоже самое для libjcsi

Теперь добавляем зависимости.
Жмем на модуль Net и затем справа на "+" -> Module dependency

Теперь выбираем наш родной модуль и повторяем операцию. Только теперь выбираем Net


Закрываем всё. Должно получиться как-то вот так

Теперь пишем в классе Rogalic следующее:
import java.util.ArrayList;
import java.util.Properties;
import net.slashie.libjcsi.CSIColor;
import net.slashie.libjcsi.CharKey;
import net.slashie.libjcsi.ConsoleSystemInterface;
import net.slashie.libjcsi.wswing.WSwingConsoleInterface;
public class Rogalic {
private static Rogalic instance;
private static ConsoleSystemInterface csi;
private boolean stop;
private int x,y;
public static void main(String[] args)
{
Properties text = new Properties();
text.setProperty("fontSize", "15");
text.setProperty("font", "roguelike.ttf");
csi = new WSwingConsoleInterface("RogueLike", text);
instance = new Rogalic();
instance.run();
}
public void run()
{
stop = false;
x = 0; y = 0;
while (!stop)
{
csi.cls();
csi.print(x,y,'@', CSIColor.WHITE); // отрисовка игрока
csi.refresh();
handleKeys(); // обработка клавиатуры
}
System.exit(0);
}
private void handleKeys(){
CharKey dir = csi.inkey();
if(dir.isUpArrow()&& (y-1 >= 0)){
y--;
}
if(dir.isDownArrow() && (y+1 < 25)){
y++;
}
if(dir.isLeftArrow() && (x-1 >= 0)){
x--;
}
if(dir.isRightArrow() && (x+1 < 80)){
x++;
}
if(dir.code == CharKey.Q){
stop = true;
}
}
}
Запускаем!
Если всё сделано правильно, то должно появиться окно с довольной "собакой (@) - главным героем всех Рогаликов. Ну, почти всех :)
Им можно шаволить курсорными стрелками.

[Java] Пример перегрузки методов. Тонкость в передаче параметров
С помощью перегрузки функций можно получить метод с одним именем, но разными реализациями.
Например, есть функция сортировки, которая принимает в качестве параметра целочисленный массив. Как научить её воспринимать и вещественные массивы?
Надо перегрузить функцию:
class Sort {
void gnom_sort(int[] mas) {
int p = 2;
int i = 1;
for (; i < mas.length; ) {
if (mas[i - 1] <= mas[i]) {
p++;
i = p;
} else {
int a = mas[i];
mas[i] = mas[i - 1];
mas[i - 1] = a;
i--;
if (i == 0) {
p++;
i = p;
}
}
}
}
void gnom_sort(double[] mas) { //перегруженная функция
int p = 2;
int i = 1;
for (; i < mas.length; ) {
if (mas[i - 1] <= mas[i]) {
p++;
i = p;
} else {
double a = mas[i];
mas[i] = mas[i - 1];
mas[i - 1] = a;
i--;
if (i == 0) {
p++;
i = p;
}
}
}
}
}
class Hello {
public static void main(String args[]) {
int masI[] = {5, 5, 9, 8, 0, -5, 9, 10};
double masD[] = {5.9, 5.7, 9.6, 8.9, 0.7, -5.6, 9.9, 10.0};
Sort s1 = new Sort();
s1.gnom_sort(masI);
s1.gnom_sort(masD); //для нас ничего не изменилось
}
А теперь обещанная тонкость
В Java в качестве параметров в функции передаются ссылки, а не значения. Кроме примитивных типов (int, long, float и т.п.).
Массив уже не является примитивным.
Что это значит. Это значит, что, передав объект (например тот же массив) в функцию, этот самый объект может быть в этой функции изменён безвозвратно.
Как пример. Создаём один массив и хотим проверить на нём несколько функций сортировки. Но после первой же функции массив станет отсортированный и дальнейшая его сортировка бессмысленна.
А вот как с этим жить дальше я пока не понимаю :(


