Страницы

Поиск по вопросам

четверг, 13 декабря 2018 г.

Ассемблерная команда LEA

Не знаю почему, но эта ассемблерная команда не дает мне покоя LEA
C++
int f(int t) { return t+1; }
int f(int*t) { return *t+1; }
int f(int& t) { return t+1; }
Ассемблер
f(int): # @f(int) lea eax, [rdi + 1] ret
f(int*): # @f(int*) mov eax, dword ptr [rdi] inc eax ret
f(int&): # @f(int&) mov eax, dword ptr [rdi] inc eax ret
Если команда MOV ясна как день, то команда LEA не ясна!
Я знаю что команда LEA выполняет вычисление адреса второго операнда и записывание его в первый операнд (это все что мне известно)
В том примере что по ссылке, а именно: lea eax,[rdi + 1] это явно не вычисление адреса и не запись в первый операнд, нет запись то будет но скорее всего что-то другое. Или я что-то не правильно понял? Объясните пожалуйста в соответствии с С++ кодом.
P.S Искал, но исчерпывающего ответа на мой вопрос не нашел, искал даже в книжке Калашникова, а там даже этой команды не нашел...эхх.


Ответ

lea eax, [rdi+1]
Эта команда загружает в eax адрес значения, лежащего под адресу rdi + 1. Т.е. она загружает в eax просто rdi+1
Выглядит странно, и чтобы понять зачем именно нужна lea, и чем она лучше просто аналогичного вызова mov или ручного вычисления адреса, нужно понять как команды записываются в памяти и выполняются процессором.
Например, у вас есть команда чтения значения:
mov eax, [rdi+1]; взять значение по адресу "rdi + 1"
Она компилируется в что-то вроде
[опкод mov][флаг что складываем в eax][флаг что берем по адресу rdi][+1]

Т.е. в 66 67 8B 47 01
Предположим что вам нужно получить сам адрес rdi+1 в eax
Вы можете сделать одно из двух:
Высчитать его руками:
mov eax, rdi + 1; не работает, move не умеет плюс!
и вам придется написать:
mov eax, rdi inc eax; 66 05 01 00 00 00
т.е. выполнить две инструкции. Возможно, хороший вариант, но только для простых +1. А для адресов вида [bp+si+4]?
mov eax, bp add eax, si add eax, 4; да, некрасиво!
или выполнить lea
lea eax, [rdi+1]

Сравните с mov

Байткод: 66 67 8D 47 01
Отличается только opcode, 8B -> 8D.
В процессоре есть готовый, очень эффективный механизм для базовых операций с адресами. И он уже реализован для операции mov - ведь mov умеет доставать значение по адресу!.
При использовании lea процессор делает все, что делает при mov, но пропускает последний шаг - извлечение значения по адресу. Вместо этого он складывает в eax сам адрес. Это гораздо удобнее и быстрее чем считать вещи вроде rdi + 1 отдельными командами.

Какое это отношение имеет к вашему примеру?
В вашем примере параметр лежит в rdi, а результат вы должны вернуть в eax. По-честному, компилятор должнен был написать
mov eax, rdi; 66 A1 add eax, 1; 66 05 01 00 00 00
Ну ок, для 1 можно использовать inc
mov eax, rdi; 66 A1 inc eax; 66 40
Но это все еще две команды. Процессор будет выполнять их по очереди.
Компилятор умный. Он знает, что процессор умеет складывать значения регистров с небольшими константами при обработке команды lea. И он вставляет одну команду, которая выдаст тот же результат.
lea eax, [rdi + 1]
Неважно, что никакой адрес на самом деле никуда не загружается - главное что работаеть будет точно так же, и чуть быстрее - т.к. процессор вычисляет адреса в памяти быстрее, чем складывает числа :)

Передача параметра по ссылке с++

Как передать функции параметр по ссылке со значением по умолчанию?
void func(int &i)
void func2(set &s)
и при этом иметь возможность вызывать эти функции без аргументов, чтобы переменной i присваивалось значение по умолчанию, а для s вызывался конструктор, чтобы использовать множество set в теле функции.


Ответ

Если вы хотите использовать аргумент по умолчанию по ссылке, то этот аргумент должен быть не временным объектом и уже где-то быть определенным на момент вызова функции.
Передача аргумента по неконстантной ссылке предполагает, что этот исходный аргумент будет в функции изменен. Вы не можете передавать по неконстантной ссылке временные объекты.
Поэтому, исходя из условий, указанных в вашем вопросе, скорей всего у вас должны быть две перегруженные функции. Одна функция принимает аргумент по lvalue-ссылке с конкретно заданным объектом. Вторая функция оибл принимает временный объект по rvalue-сслке, оибо вместо него использует значение по умолчанию.
Например,
#include
void func( int &i ) { ++i; std::cout << "Inside func( int & ) i = " << i << std::endl; }
void func( int &&i = int() ) { ++i; std::cout << "Insde func( int && ) i = " << i << std::endl; }
int main() { int i = 0;
std::cout << "Before func( i ) i = " << i << std::endl;
func( i );
std::cout << "After func( i ) i = " << i << std::endl;
std::cout << std::endl;
i = 0;
std::cout << "Before func() i = " << i << std::endl;
func();
std::cout << "After func() i = " << i << std::endl;
return 0; }
Вывод программы на консоль:
Before func( i ) i = 0 Inside func( int & ) i = 1 After func( i ) i = 1
Before func() i = 0 Insde func( int && ) i = 1 After func() i = 0
В этой программе, когда в качестве аргумента передается lvalue, то используется первая функция
void func( int &i ) { ++i; std::cout << "Inside func( int & ) i = " << i << std::endl; }
и она меняет в своем теле исходный аргумент.
Когда же аргумент не указывается, или когда указывается rvalue, то есть некоторый временный объект, как, например,
func(); // используется аргумент по умолчанию int(), который равен 0
или
func( 10 ); // используется временный объект - целочисленный литерал - 10
то используется вторая функция
void func( int &&i = int() ) { ++i; std::cout << "Insde func( int && ) i = " << i << std::endl; }
Если же вы не собираетесь изменять исходный аргумент, то достаточно объявить одну функцию с параметром в виду константной ссылки. Например,
void func( const int &i = int() );

Как удалить элемент из master ветки репозитория?

Добрый день уважаемые, подскажите, пожалуйста, какие команды нужно выполнить чтобы удалить файл из главной ветки. Допустим, я случайно в git добавил файл style/mywork.scss . Что мне необходимо выполнить в окне gitBash чтобы удалить его, без последующего отображения на github'e ? И ещё вопрос по данной же теме. В .gitignore я добавил запись для файла .style/.scss Но он всё равно добавился в git, в чём ошибка ?


Ответ

Чтобы удалить файл из репозитория необходимо сделать следующее:
Во первых, убедиться что Вы на ветке master
git branch
Она будет выделена зелёным и звёздочкой. Если Вы не на нужной ветке, то переключиться на неё:
git checkout master Затем необходимо удалить файл. Для этого есть два варианта, можно воспользоваться любым:
можно удалить Git-командой:
git rm <путь_к_удалённому_файлу> либо просто удалить файл с диска (можете просто через проводник) и добавить изменение в индекс следующей командой:
git add <путь_к_удалённому_файлу>
После выполнения одного из двух вариантов файл будет удалён, а изменения в индексе. Фиксируем изменения:
git commit -m "удалил файл <название_файла>"
Сообщение можно другое, это как пример. Далее необходимо отправить Ваши изменения на удалённую ветку (на github). Это можно сделать так:
git push
либо так:
git push origin master
.gitignore
Файл .gitignore необходимо добавлять в репозиторий в самом начале. Если вдруг Вы закомитили какие-то файлы, а потом добавили их в .gitignore, то файлы, которые уже под присмотром Git-а не будут удалены, но новые файлы, подподающие под правила из .gitignore, добавляться не будут.

“preventDefault()” или “return false”?

Не раз замечал, что в различных кодах используют то preventDefault(), то return false. И это касается, как маленьких, так и больших проектов.
По старинке, я привык использовать во всём return false, но недавно задумался: может preventDefault() лучше чем return? Почему его так часто используют?
Так всё же что лучше использовать: preventDefault() или return false?
var type = null; for(var i = 0; i <= 1; i++){ document.querySelectorAll('[name="type"]')[i].onclick = function() { type = this.value; }; }; document.querySelector('a').onclick = function(e) { if(type == 'pd'){ e.preventDefault(); alert('Прервано функцией preventDefault()!'); }else if(type == 'rf'){ alert('Прервано функцией return false!'); return false; }; }; body {font-family: arial;}

- preventDefault; - return false;

Ссылка


Ответ

return false из обработчика события jQuery так же, как вызов обоих e.preventDefault и e.stopPropagation у переданного jQuery.Event события.
e.preventDefault() остановит обработку события по умолчанию, e.stopPropagation() остановит всплытие события по DOM return false остановит их оба. e.stopImmediatePropagation предотвращает всплытие всех подобных событий (полезно, когда на элементе есть несколько обработчиков одного события)
В обычных (не-jQuery) обработчиках событий return false не запрещает всплывание(bubbling) события.
Также return false; в ванильном js не запрещает обработку события по умолчанию из addEventListener обработчиков. Он работает только из el.onclick хендлеров.
Source: John Resig
Any benefit to using event.preventDefault() over "return false" to cancel out an href click?

Вольный перевод ответа с enSO
Вывод:
В jquery - полезнее return false В vanillaJS - лучше его не использовать, он работает только с onclick-like обработчиками.

В IE8 и ниже необходимо заменить stopPropagation на cancelBubble, а preventDefault на return false;

Демонстрация
Третья ссылка совершает переход несмотря на return false
document.getElementById('a1').addEventListener('click', function(e){ console.log('preventDefault - clicked'); e.preventDefault(); }); document.getElementById('a2').onclick = function(){ console.log('return false from onclick'); return false; }; document.getElementById('a3').addEventListener('click', function(){ console.log('return false - clicked'); return false; }) preventDefault
return false from onclick
return false

this при объявлении конструктора Java

Есть два варианта объявления конструктора:
public class MyClass() { int a, b, c; MyClass (int a, int b, int c) { this.a=a; this.b=b; this.c=c; } }
или
public class MyClass() { int a, b, c; MyClass (int x, int y, int z) { a=x; b=y; c=z; } }
Мне, как новичку, вариант с this кажется более наглядным, но какая форма записи предпочтительнее и более профессиональна? И есть ли какие-либо подводные камни при использовании this таким способом?


Ответ

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

Как сделать селектор с привязкой к файлу PHP?

Здравствуйте, как сделать селектор. Чтобы сначала был выбор стран, к стране были прикреплены определенные города. Далее при выборе города открывалось содержимое файла php привязанное к данному городу. Нужен полный цикл. Я могу не правильно объяснить, но вот для наглядности вопроса сделал видео, что я имею ввиду в вопросе https://www.youtube.com/watch?v=fSxKDltA9-E может оно поможет раскрыть вопрос. Спасибо заранее за вашу помощь, с уважением.
Нужно типо такого http://codepen.io/ajaxray/pen/oBPbQe Но после последнего действия чтобы высвечивалось содержимое файла привязанного к городу.


Ответ

Подгружайте данные через AJAX. Если Вы имеете ввиду что-то, подобное поиску городов в VK, но там реализовано через AJAX запрос в БД.
Если же хотите брать из файла, то вешаете обработчик на изменение селектора страны. При его срабатывании делаете запрос в AJAX к PHP-файлу со списками городов (там выбираете нужную страну и отправляете список городов этой страны в ответе). В клиенте при получении ответа от сервера вставляете этот список в селектор с городами.
По брать из файла не самая лучшая идея, т.к. при хорошей детализации городов будет много. Вносите все геоданные в базу.
Шаг 1
Например, делаем AJAX-запрос через JQuery, где call.php - скрипт, который будет выдавать нам список городов, а data - это то, что мы передаем в PHP-скрипт (в данном случае требуется передать id страны, берем из селектора country_id). Пишем соответствующую функцию:
function getCities() { var data = { country_id: document.getElementById('country_id').value }; $.post('/call.php', { data: data }, function(result) { ... }); }
Шаг 2
В PHP-файле выбираем нужный нам массив со списком городов (на основе данных data):
$country_id = isset($_POST['data']['country_id']) ? $_POST['data']['country_id'] : NULL; ... if ($country_id == 1) $result = array(array('city_id'=>'1', 'city_name'=>'Москва'), array('city_id'=>'2', 'city_name'=>'Санкт-Петербург')); else if ($country_id == 2) $result = array(array('city_id'=>'1', 'city_name'=>'Нью-Йорк'), array('city_id'=>'2', 'city_name'=>'Сан-Франциско')); ... echo json_encode($result, true); exit();
Шаг 3
Далее возвращаемся в наш клиент и после получения ответа от сервера парсим полученный JSON-массив, собираем html-код селектора и обновляем DOM.
$.post('/call.php', { data: data }, function(result) { result = JSON.parse(result); ... var html = ''; result.forEach(function(data, index) { html += ''; }); document.getElementById('city_id').innerHTML = html; });
Где document.getElementById('city_id') - наш селектор со списком городов, city_id - передаваемый в массиве айдишник города, а city_name - соответственно название города.
Шаг 4
В самом HTML уже должен быть код селекторов стран и городов. Селектор country_id статичный, селектор city_id собирается динамически в зависимости от выбора страны. Не забываем повесить на селектор country_id обработчик, который будет вызывать нашу JS-функцию с AJAX-запросом. А на селектор с выбором города (city_id) вешаем обработчик, который будет вызывать функцию открытия нужного файла.


Шаг 5
В функции openFile() прописываем пути к файлам, которые будут открываться в зависимости от выбора города.
function openFile() { // vars var country_id = document.getElementById('country_id').value; var city_id = document.getElementById('city_id').value; // locations if (country_id == 1 && city_id == 1) window.location = 'https://aviareysi.ru/moscow.php'; else if (country_id == 1 && city_id == 2) window.location = 'https://aviareysi.ru/peterburg.php'; }
Если городов достаточно много, то можно засунуть это в AJAX, но в самом простом варианте можно брать напрямую из JS-файла.

Стереть содержимое всех текстовых файлов в папке

не могу стереть содержимое всех текстовых файлов в папке. испробовал разные способы, не получается Пытался использовать cp /dev/null * писало ошибку


Ответ

что бы "стереть содержимое файлов", можно просто обрезать их размер до нуля. Для этого есть команда truncate --size 0 <имяфайла>. Найти все файлы к каталоге (рекурсивно) можно такой командой find . -type f (где точка - текущий каталог).
Соединяем. Вначале запускаем
find . -type f
и смотрим на список файлов, что он соответствует требуемому. Если все ок, запускаем такое
find . -type f -exec truncate --size 0 {} \;