Спасибо большое! Хотел сказать что про указатели есть очень хорошее видео Александра Писанца (пусть простит меня если не верно написал его имя или фамилию) так там он более понятно расказавает (не обижайтесь, я не хочу сказать что ви плохо расказиваете) потому что он еще и показивает практические примери. Теория с практикой лучше самой теории! В видео Александра Писанца сказано что указатели виполняются бистрей и контроллер при работе с указателями делает меньше операций чем даже с переменними - ето так? Видео 4:45 можно поподробнее? Как етого избежать? Что нужно учитивать?
Действительно, указатели работают быстрее так как идет обращение напрямую к ОЗУ, а точнее к РОН. Наезд на стек это когда мы например записываем данные по указателю, а при этом скачем по адресам не следя за ним. В конечном итоге мы залезаем в адреса стека и затираем его.
А как ми можем себя от етого обезопасить? Ето нужно в програме делать какое то ограждение что типа указателям дальше такого то адреса идти нельзя! Или как?
Нет. Просто в качестве второго аргумента функции передавать точную длину массива. Если компилятор выделил место для массива, то оно точно не залезло на стек.
А можете написать на Си как ето виглядит (практически) а то как би понял что надо сделать но не понял как! :) Ви уж извините что я такой не понятливий.
как компилятор интерпретирует запись *p++ ? допустим есть функция которая принимает байт в качестве аргумента void func(char); далее я её вызываю в цикле в котором скармливаю ей массив данных и пушу вот так p = arr; while((*p) != 0x00) { func(*p++); // с одной стороны функция берет именно данные, но инкремент производит над указателем } корректно ли вообще так писать?
Компилятор выдаст ошибку. Дело в том что p=arr; при условии что arr это массив, полная ерунда. Имя массива это указатель на первый элемент массива. Поэтому перед p нужно поставить звездочку сказав компилятору что это указатель. Так же нужно перед указателем поставить тип такой же как и у массива, чтобы компилятор знал сколько байт в ячейке. Выглядеть будет так: char arr[3] = {0}; массив на 3 ячейки типа char. char *p = arr; вот теперь все будет правильно. Создается указатель *p с типом char и передается ему адрес первого элемента массива. А вот теперь как использовать указатели. *p++ просто увеличит значение в ячейке памяти на 1 по текущему адресу записанному в указателе p. То есть тоже самое что arr[0]++. p++ (без звездочке) увеличит адрес ячейки на величину байт в зависимости от типа. Если был char, то увеличит на 1, если int, то на 2. Так как архитектура 8-ми битная, а int 16 бит, а адресация идет по байтно. char *p = &arr[0] такая запись эквивалентно предыдущий. Только здесь явно указателю передается адрес ячейки массива с индексом 0.
*p++ просто увеличит значение в ячейке памяти на 1 по текущему адресу записанному в указателе p. Вот и я думаю что должно так работать, а работает в итоге так, что в функцию передаются именно данные массива, на которые указывает указатель, а ++ инкрементирует адрес. Получается неоднозначность, и я не могу понять почему компилятор такое принимает. Вот конкретно та функция которая проходит компиляцию и работает в реальном контроллере, хотя с точки зрения синтаксиса работать не должна. Передает строку в дисплей: void _LCD_string_ram(char addr, char *str) { _LCD_cmd(0x80 + addr); while(*str != 0) { _LCD_data(*str++); }
А с точки зрения синтаксиса, здесь ошибки нет))) Смотри. В качестве аргументов передаем адрес и указатель на массив void _LCD_string_ram(char addr, char *str) { Здесь в качестве аргумента передали адрес плюс смещение в памяти ЖК _LCD_cmd(0x80 + addr); Просто цикл с ожидание нуля while(*str != 0) { А вот тут прикол. В качестве аргумента передается значение первого элемента массива. Например там символ 'A'. Данный символ в ASCII это число 0x41. То есть функция приняла в качестве аргумента указатель на ячейку в памяти с адресом 0x41. Далее функция возьмет из этой ячейки значение и выведет его на дисплей. Затем в цикле увеличится значение первого элемента массива string на 1 и станет равным 0x42. И так до 0xFF. Следующее увеличение даст переполнение ячейки и станет 0x00. Ура цикл закончился. _LCD_data(*str++); } То есть на дисплей будет выведен мусор из памяти находящейся по адресам с 0x41 до 0xFF.
А как тогда правильно переписать эту строчку: _LCD_data(*str++); чтобы функция _LCD_data принимала код символа, но инкремент шел именно значения указателя. Нам же надо код символа выводить на дисплей, а проверку делать на равенства символа 0х00, как признака оканчания строки?
Спасибо!
За видео спасибо! Ждем продолжения :)
Спасибо большое!
Хотел сказать что про указатели есть очень хорошее видео Александра Писанца (пусть простит меня если не верно написал его имя или фамилию) так там он более понятно расказавает (не обижайтесь, я не хочу сказать что ви плохо расказиваете) потому что он еще и показивает практические примери.
Теория с практикой лучше самой теории!
В видео Александра Писанца сказано что указатели виполняются бистрей и контроллер при работе с указателями делает меньше операций чем даже с переменними - ето так?
Видео 4:45 можно поподробнее? Как етого избежать? Что нужно учитивать?
Действительно, указатели работают быстрее так как идет обращение напрямую к ОЗУ, а точнее к РОН.
Наезд на стек это когда мы например записываем данные по указателю, а при этом скачем по адресам не следя за ним. В конечном итоге мы залезаем в адреса стека и затираем его.
А как ми можем себя от етого обезопасить?
Ето нужно в програме делать какое то ограждение что типа указателям дальше такого то адреса идти нельзя! Или как?
Нет. Просто в качестве второго аргумента функции передавать точную длину массива. Если компилятор выделил место для массива, то оно точно не залезло на стек.
А можете написать на Си как ето виглядит (практически) а то как би понял что надо сделать но не понял как!
:) Ви уж извините что я такой не понятливий.
Передовать функции длину массива. Массив никогда не залезет на стек.
как компилятор интерпретирует запись *p++ ? допустим есть функция которая принимает байт в качестве аргумента void func(char); далее я её вызываю в цикле в котором скармливаю ей массив данных и пушу вот так
p = arr;
while((*p) != 0x00)
{
func(*p++); // с одной стороны функция берет именно данные, но инкремент производит над указателем
}
корректно ли вообще так писать?
Компилятор выдаст ошибку. Дело в том что p=arr; при условии что arr это массив, полная ерунда. Имя массива это указатель на первый элемент массива. Поэтому перед p нужно поставить звездочку сказав компилятору что это указатель. Так же нужно перед указателем поставить тип такой же как и у массива, чтобы компилятор знал сколько байт в ячейке. Выглядеть будет так: char arr[3] = {0}; массив на 3 ячейки типа char. char *p = arr; вот теперь все будет правильно. Создается указатель *p с типом char и передается ему адрес первого элемента массива. А вот теперь как использовать указатели. *p++ просто увеличит значение в ячейке памяти на 1 по текущему адресу записанному в указателе p. То есть тоже самое что arr[0]++. p++ (без звездочке) увеличит адрес ячейки на величину байт в зависимости от типа. Если был char, то увеличит на 1, если int, то на 2. Так как архитектура 8-ми битная, а int 16 бит, а адресация идет по байтно. char *p = &arr[0] такая запись эквивалентно предыдущий. Только здесь явно указателю передается адрес ячейки массива с индексом 0.
*p++ просто увеличит значение в ячейке памяти на 1 по текущему адресу записанному в указателе p.
Вот и я думаю что должно так работать, а работает в итоге так, что в функцию передаются именно данные массива, на которые указывает указатель, а ++ инкрементирует адрес. Получается неоднозначность, и я не могу понять почему компилятор такое принимает. Вот конкретно та функция которая проходит компиляцию и работает в реальном контроллере, хотя с точки зрения синтаксиса работать не должна. Передает строку в дисплей:
void _LCD_string_ram(char addr, char *str)
{
_LCD_cmd(0x80 + addr);
while(*str != 0)
{
_LCD_data(*str++);
}
А с точки зрения синтаксиса, здесь ошибки нет)))
Смотри.
В качестве аргументов передаем адрес и указатель на массив
void _LCD_string_ram(char addr, char *str)
{
Здесь в качестве аргумента передали адрес плюс смещение в памяти ЖК
_LCD_cmd(0x80 + addr);
Просто цикл с ожидание нуля
while(*str != 0)
{
А вот тут прикол. В качестве аргумента передается значение первого элемента массива. Например там символ 'A'. Данный символ в ASCII это число 0x41. То есть функция приняла в качестве аргумента указатель на ячейку в памяти с адресом 0x41. Далее функция возьмет из этой ячейки значение и выведет его на дисплей. Затем в цикле увеличится значение первого элемента массива string на 1 и станет равным 0x42. И так до 0xFF. Следующее увеличение даст переполнение ячейки и станет 0x00. Ура цикл закончился.
_LCD_data(*str++);
}
То есть на дисплей будет выведен мусор из памяти находящейся по адресам с 0x41 до 0xFF.
А как тогда правильно переписать эту строчку: _LCD_data(*str++); чтобы функция _LCD_data принимала код символа, но инкремент шел именно значения указателя. Нам же надо код символа выводить на дисплей, а проверку делать на равенства символа 0х00, как признака оканчания строки?
_LCD_data(*str); // Передали код символа
str++; // Сдвинулись на следующей символ.