Видимо, понял задумку программиста и сам разделил по блокам из 5-ти байтWiseLord писал(а):Кстати, странно, что на саму инициализацию массива не ругается.
Вопросы по С/С++ (СИ)
- Аlex
- Модератор
- Сообщения: 4614
- Зарегистрирован: Чт мар 18, 2010 23:09:57
- Откуда: Планета Земля
- Контактная информация:
Re: Вопросы по С/С++ (СИ)
- Реклама
- Zhuk72
- Сверлит текстолит когтями
- Сообщения: 1231
- Зарегистрирован: Ср янв 29, 2014 08:41:31
- Откуда: Баку
- Контактная информация:
Re: Вопросы по С/С++ (СИ)
[uquote="Аlex",url="/forum/viewtopic.php?p=3126024#p3126024"]ptr должен быть указателем на такой же тип, с такими же квалификаторами, как и lcd_font.[/uquote]
Так и есть. Выше я просто для примера int писал. У себя я написал
Массив тоже unsigned char, как и указатель, но тем не менее компилятор ругается: a value of type "const unsigned char (*)[5]" cannot be assigned to an entity of type "unsigned char *"
Так и есть. Выше я просто для примера int писал. У себя я написал
Код: Выделить всё
const unsigned char *ptr;
void lcd_out (unsigned char row, unsigned char col, unsigned char *text)
{
ptr = lcd_font;
......
}
В источнике этих символов массив представлялся как {{5 байт},{5 байт},{5 байт}, и т.д.}, я по какой-то причине (это было несколько месяцев назад) внутренние {} убрал. Получился одномерный массив, но адресуемый как двухмерный. Я, кстати, такое и раньше делал, ошибок в работе это не вызывало.WiseLord писал(а):Массив-то двумерный, а указатель - на одномерный.
Кстати, странно, что на саму инициализацию массива не ругается.
Каждый имеет право на свое личное ошибочное мнение.
У меня было тяжелое детство - я до 14 лет смотрел черно-белый телевизор.
У меня было тяжелое детство - я до 14 лет смотрел черно-белый телевизор.
- Аlex
- Модератор
- Сообщения: 4614
- Зарегистрирован: Чт мар 18, 2010 23:09:57
- Откуда: Планета Земля
- Контактная информация:
Re: Вопросы по С/С++ (СИ)
А, ну так правильно. Массив то двумерный.Zhuk72 писал(а):Массив тоже unsigned char, как и указатель, но тем не менее компилятор ругается: a value of type "const unsigned char (*)[5]" cannot be assigned to an entity of type "unsigned char *"
Вот так надо :
Код: Выделить всё
ptr = lcd_font[0];
Или так :
Код: Выделить всё
ptr = &lcd_font[0][0];
Добавлено after 6 minutes 42 seconds:
Т.е., в данном случае, мы можем сделать так :
Код: Выделить всё
ptr = lcd_font[ch];
for (i = 0; i < 5 ; i++)
{
lcd_send(*(ptr + i), DTA); // или lcd_send(ptr[i]), DTA);
}
Код: Выделить всё
for (i = 0; i < 5 ; i++)
{
ptr = &lcd_font[ch][i];
lcd_send(*ptr,DTA);
}
Код: Выделить всё
lcd_send(lcd_font[ch][i],DTA);- Zhuk72
- Сверлит текстолит когтями
- Сообщения: 1231
- Зарегистрирован: Ср янв 29, 2014 08:41:31
- Откуда: Баку
- Контактная информация:
Re: Вопросы по С/С++ (СИ)
Спасибо большое, ptr = lcd_font[0]; подошло.
Я так пробовал и раньше, но тогда, видать, мешала ошибка, связанная с константой.
Завтра с отсылкой еще буду разбираться, а пока у меня что-то функция дилей не работает. Собрал все на макетке, прогнал дебаггером, оказалось,что дисплей инициализацию не проходит из-за зацикленной задержки. Что-то я там с SysTick'ом не то сварганил
Я так пробовал и раньше, но тогда, видать, мешала ошибка, связанная с константой.
Завтра с отсылкой еще буду разбираться, а пока у меня что-то функция дилей не работает. Собрал все на макетке, прогнал дебаггером, оказалось,что дисплей инициализацию не проходит из-за зацикленной задержки. Что-то я там с SysTick'ом не то сварганил
Каждый имеет право на свое личное ошибочное мнение.
У меня было тяжелое детство - я до 14 лет смотрел черно-белый телевизор.
У меня было тяжелое детство - я до 14 лет смотрел черно-белый телевизор.
Re: Вопросы по С/С++ (СИ)
[uquote="Zhuk72",url="/forum/viewtopic.php?p=3126084#p3126084"]ов массив представлялся как {{5 байт},{5 байт},{5 байт}, и т.д.}, ... внутренние {} убрал. Получился одномерный массив, но адресуемый как двухмерный. Я, кстати, такое и раньше делал, ошибок в работе это не вызывало.[/uquote]
В принципе, размерность массива явно указывается при его объявлении количеством скобок []. Внутри него вы можете получать доступ к элементам как угодно, но его размерность от этого не изменится. В регистрах памяти массив, независимо от его размерности, будет располагаться линейно, элемент за элементом, строка за строкой.
В двухмерном массиве array[][] при его инициализации строки можно разделять внутренними скобками {{ , , },{ , , },{ , , },{ , , }}. В одномерном массиве такая запись приведет (может привести) к предупреждению "скобки вокруг скалярной инициализации".
[uquote="Zhuk72",url="/forum/viewtopic.php?p=3125816#p3125816"]Наверное люди делятся на две категории: одни понимают указатели, другие нет
[/uquote]
Эт веееерно
Вообще-то, тут по большей части теоретический вопрос. В умных книшшках много раз описывались указатели, но мало прочесть, надо опробовать самому на деле. И автору (Zhuk72) желательно было бы потренироваться "на кошках", т.е. на простом абстрактном примере, а не влезать сразу к шрифтам и дисплею.
Указатель подобен курсору в текстовом редакторе, или прямоугольной рамке размером в один символ (равносильно char *ptr), и эта рамка может перемещаться по строке текста. Позиция указателя будет отсчитываться от начала строки (т.е. значение переменной ptr, хранящей некоторый адрес из адресного пространства). Рамку можем перемещать в любую позицию. Например, перемещаем на первую букву в выбранном слове ( char *ptr = word1 ) и в рамке видим эту букву. Рамку можем двигать на какое-то число позиций вправо или влево по слову ( ptr += 5; ptr--) и наблюдать в рамке буквы ( a = *ptr ). Номер позиции рамки от начала всей строки мы записываем в свободное место той же строки (переменная ptr сама находится в том же адресном пространтве), а так же в свободном месте строки можем записывать буквы, которые видим в рамке ( переменная, в которую читается по указателю, тоже находится в том же адресном пространстве). Причем, мы можем перескочить за границы интересующего нас слова или вообще попасть туда, где нет текста, но есть место под буквы (перемещаясь по массиву, мы можем запросто выйти за его границы и ошибок компиляции не будет). Причем, буквы могут быть разной ширины, и чтобы увидеть в рамке именно то, что интересует, нужно выбирать рамку строго по ширине конкретной буквы (т.е. указатель и переменная должны быть одного типа (кол-ва байт)). К тому же, буквы могут не быть в составе осмысленных слов (указатели могут указывать на отдельные переменные).

Просто так взяв рамку и бросив ее на текст, мы можем попасть на любую букву. Это к слову об использовании неинициализованных указателей, которые могут указывать на любое случайное место. Поэтому, взяв рамку в руки, обязательно определим, какое место в строке нас интересует. То есть, объявив указатель, скажите ему, на что он указывает. В крайнем случае, напишите char *pa = (void*)0, сделав его "нулевым указателем". (чисто технически он указывает на адрес 0 в адресном пространстве). Вместо 0 вы можете записать любой другой адрес, либо получить адрес какой-то переменно с помощью символа &.
Встречается ситуация, когда указатель может указывать на переменную не своего типа. Тогда выдается предупреждение о несовместимости типов переменной и указателя на нее. Образно выражаясь, ширина рамки не совпадает с шириной символа. И если это сделано сознательно, а не в результате ошибки, то можно выполнить приведение типа (подогнать ширину рамки вручную по месту). Вот в таком примере мы сможем четырехбайтную переменную var разложить на отдельные байты в отдельных переменных byte3 - byte0. (обратная последовательность связана с тем, что младший байт имеет больший адрес, чем старший)
Используя возможности приведения типа, можно создать указатель на void, то есть указатель на неопределенный тип переменной, и использовать этот указатель как универсальный, для указания на любой тип. Объясняется это просто - тип void имеет размер в 1 байт (по соглашению в новых редакциях языка)
без приведения типа указателя компилятор будет выдавать предупреждение о несовместимости типов. А void* - это в большей мере условность, наглядно показывающая универсальный указатель. Этот механизм используется для передачи через параметры функции данных произвольного размера, в том числе и структуры. Параметр функции используется как "универсальный передатчик". Метод используется во FreeRTOS при передаче произвольных параметров в создаваемую задачу.
Вот вкратце и есть суть указателей. Как видим, они используются не только для работы в массивах, но и для создания универсальных взаимосвязей между переменными в разных частях кода. Поэтому освоить указатели - очень даже важно для продвинутого написания кода. "уровень - эдвансд юзер", так сказать
------
Что касается доступа к элементам двухмерного массива через индексацию и через указатели, тут вот какая штука.
Допустим, у нас есть двухмерный массив
array[3][5] = {{0,1,2,3,4},{5,6,7,8,9},{10,11,12,13,14}}; с одинаковой длиной строк:
Доступ к произвольному элементу при помощи индексации осуществляется по типу X-Y (не забываем, что индексация происходит с 0, как по горизонтали, так и по вертикали!)
таким способом удобно получать доступ к произвольному элементу по его координатам.
А вот доступ по порядковому номеру (по смещению от начала) удобнее выполняется через указатель.
однако, как видим, произвольный доступ по координатам X-Y через указатель записывается гораздо хуже, потому что нужно знать именно ширину строки, т.е. количество элементов в строке.
Таким образом, указатели хорошо подходят для последовательного считывания в одномерном или двухмерном массиве. Но указатели усложняют жизнь, когда нужно прочитать некоторую ограниченную область в двухмерном массиве, либо при произвольном доступе в координатах X-Y.
С таким простым шрифтом, как у Zhuk72, можно работать хоть как - по указателю или по индексации, разницы большой нет.
В принципе, размерность массива явно указывается при его объявлении количеством скобок []. Внутри него вы можете получать доступ к элементам как угодно, но его размерность от этого не изменится. В регистрах памяти массив, независимо от его размерности, будет располагаться линейно, элемент за элементом, строка за строкой.
В двухмерном массиве array[][] при его инициализации строки можно разделять внутренними скобками {{ , , },{ , , },{ , , },{ , , }}. В одномерном массиве такая запись приведет (может привести) к предупреждению "скобки вокруг скалярной инициализации".
[uquote="Zhuk72",url="/forum/viewtopic.php?p=3125816#p3125816"]Наверное люди делятся на две категории: одни понимают указатели, другие нет
Эт веееерно
Указатель подобен курсору в текстовом редакторе, или прямоугольной рамке размером в один символ (равносильно char *ptr), и эта рамка может перемещаться по строке текста. Позиция указателя будет отсчитываться от начала строки (т.е. значение переменной ptr, хранящей некоторый адрес из адресного пространства). Рамку можем перемещать в любую позицию. Например, перемещаем на первую букву в выбранном слове ( char *ptr = word1 ) и в рамке видим эту букву. Рамку можем двигать на какое-то число позиций вправо или влево по слову ( ptr += 5; ptr--) и наблюдать в рамке буквы ( a = *ptr ). Номер позиции рамки от начала всей строки мы записываем в свободное место той же строки (переменная ptr сама находится в том же адресном пространтве), а так же в свободном месте строки можем записывать буквы, которые видим в рамке ( переменная, в которую читается по указателю, тоже находится в том же адресном пространстве). Причем, мы можем перескочить за границы интересующего нас слова или вообще попасть туда, где нет текста, но есть место под буквы (перемещаясь по массиву, мы можем запросто выйти за его границы и ошибок компиляции не будет). Причем, буквы могут быть разной ширины, и чтобы увидеть в рамке именно то, что интересует, нужно выбирать рамку строго по ширине конкретной буквы (т.е. указатель и переменная должны быть одного типа (кол-ва байт)). К тому же, буквы могут не быть в составе осмысленных слов (указатели могут указывать на отдельные переменные).

Просто так взяв рамку и бросив ее на текст, мы можем попасть на любую букву. Это к слову об использовании неинициализованных указателей, которые могут указывать на любое случайное место. Поэтому, взяв рамку в руки, обязательно определим, какое место в строке нас интересует. То есть, объявив указатель, скажите ему, на что он указывает. В крайнем случае, напишите char *pa = (void*)0, сделав его "нулевым указателем". (чисто технически он указывает на адрес 0 в адресном пространстве). Вместо 0 вы можете записать любой другой адрес, либо получить адрес какой-то переменно с помощью символа &.
Встречается ситуация, когда указатель может указывать на переменную не своего типа. Тогда выдается предупреждение о несовместимости типов переменной и указателя на нее. Образно выражаясь, ширина рамки не совпадает с шириной символа. И если это сделано сознательно, а не в результате ошибки, то можно выполнить приведение типа (подогнать ширину рамки вручную по месту). Вот в таком примере мы сможем четырехбайтную переменную var разложить на отдельные байты в отдельных переменных byte3 - byte0. (обратная последовательность связана с тем, что младший байт имеет больший адрес, чем старший)
Код: Выделить всё
int var = 0x12345678;
char *ptr = (char*)&var; // указатель будет указывать только на 1 байт переменной var
byte3 = *ptr++; // чтение переменной по указателю и последующий инкремент указателя, передвигая его на следующий байт
byte2 = *ptr++;
byte1 = *ptr++;
byte0 = *ptr;Код: Выделить всё
void *pv = (void*)0; // "нулевой" указатель на void
int a = 0x12345, x;
char b = 'A', y;
pv = &a; // получает адрес переменной любого типа
x = *((int*)pv); // чтение по указателю с приведением указателя к типу читаемой переменной. x = 0x12345
pv = &b;
y = *((char*)pv); // y = 'A'
/* можно пойти еще дальше и объявить указатель непосредственно на char, который будет выражаться через указатель на void с приведением типа */
char *pc;
pc = (char*)pv;
/* и теперь использовать уже его */
char symb;
symb = *pc; // фактически, symb = b непосредственно
*pc = 'B'; // фактически, b = 'B' непосредственноВот вкратце и есть суть указателей. Как видим, они используются не только для работы в массивах, но и для создания универсальных взаимосвязей между переменными в разных частях кода. Поэтому освоить указатели - очень даже важно для продвинутого написания кода. "уровень - эдвансд юзер", так сказать
------
Что касается доступа к элементам двухмерного массива через индексацию и через указатели, тут вот какая штука.
Допустим, у нас есть двухмерный массив
array[3][5] = {{0,1,2,3,4},{5,6,7,8,9},{10,11,12,13,14}}; с одинаковой длиной строк:
Код: Выделить всё
0 1 2 3 4
5 6 7 8 9
10 11 12 13 14
Код: Выделить всё
char a;
int x, y;
x = 2;
y = 1;
a = array[y][x];
a = array[y+1][x-2]; А вот доступ по порядковому номеру (по смещению от начала) удобнее выполняется через указатель.
Код: Выделить всё
char *ptr = &array[0][0]; // создается указатель на элементы массива и ему сразу присваивается адрес начала массива
ptr += 8; // смещение указателя на 8 позиций от начала массива
a = *ptr; // получение элемента 8-го массива
ptr++; // указатель перемещается на следующий элемент массива
a = *ptr; // получение следующего элемента массива
ptr -= 3; // указатель перемещается на 3 позиции назад от текущей
a = *ptr;
a = *ptr++; // получение текущего элемента с последующим смещением указателя на след.элемент
ptr += (1 * 5); // перемещение указателя на следующую строку ниже. 1 - число строк вниз, 5 - ширина массива (X)
a = *ptr; // получение элемента строкой ниже (12-й)
Таким образом, указатели хорошо подходят для последовательного считывания в одномерном или двухмерном массиве. Но указатели усложняют жизнь, когда нужно прочитать некоторую ограниченную область в двухмерном массиве, либо при произвольном доступе в координатах X-Y.
С таким простым шрифтом, как у Zhuk72, можно работать хоть как - по указателю или по индексации, разницы большой нет.
Ёшкин кот обормот
- Реклама
- Zhuk72
- Сверлит текстолит когтями
- Сообщения: 1231
- Зарегистрирован: Ср янв 29, 2014 08:41:31
- Откуда: Баку
- Контактная информация:
Re: Вопросы по С/С++ (СИ)
Спасибо большое за такую серьезную работу, это действительно впечатляет!
По указателям я несколько раз читал КиР, смотрел одно обширное видео на ютубе, потому смысл сего чуда представляю.
Но вот когда доходит до практической реализации, у меня в особо сложных случаях шарики за ролики заходят.
Почитаю ваше объяснение, может просветлею в этот раз
По указателям я несколько раз читал КиР, смотрел одно обширное видео на ютубе, потому смысл сего чуда представляю.
Но вот когда доходит до практической реализации, у меня в особо сложных случаях шарики за ролики заходят.
Почитаю ваше объяснение, может просветлею в этот раз
Каждый имеет право на свое личное ошибочное мнение.
У меня было тяжелое детство - я до 14 лет смотрел черно-белый телевизор.
У меня было тяжелое детство - я до 14 лет смотрел черно-белый телевизор.
Re: Вопросы по С/С++ (СИ)
Да нет, тут не в практической реализации дело. Судя по вашему первому вопросу, уж извините, дело в теоретических пробелах.
Если в описании функции в параметрах запрашивается указатель, то при вызове функции должен быть передан адрес какой-либо переменной. Почему-то никто явно этого не заметил, а написали, что "ошибка не в указателях" и дальше пошли в какие-то далекие дебри про двухмерные указатели, только лишь мельком зацепившись за несоответствие параметра функции.
А раз так, то разберем детально:
Для той функции, которая была изначально задана, будет работать именно приведенный вариант вызова.
А вот попытка сделать вызов вот так (как поначалу начали советовать):
lcd_out('A');
приведет к предупреждению (если оно включено в настройках) о несовместимости типов. Можно плюнуть на это, а можно прикинуться "типа умным" и вручную привести к запрашиваемому функцией типу указателя, используя наработки из ранее написанного мною.
lcd_out((unsigned char*)'A');
Предупреждений нету, но функция делает совсем не то, что ожидалось. А теперь вспомним, что запрашивает параметр? Он запрашивает какой-то адрес. А что есть 'A' ? Это ANSI-представление числа 0х41. То есть, мы явным образом передаем адрес памяти 0х41. Для функции это будет указатель (ссылка) на адрес 0х41. При дальнейшей работе функции идет чтение по принятому в параметре адресу 0х41. Что там физически находится - хрен его знает. Да, работает. Но результат не тот, что ожидался.
Но чтобы раз и навсегда разобраться с вариантами, теперь перепишем немного нашу функцию и попробуем вызвать ее так, как было в вашем первом вопросе:
И у нас уже замечательнейше работает и выдает то, что и ожидали (однако, предыдущий вариант с адресом массива уже работать не будет).
Именно на этом построены всякие printf, в том числе и самописные.
Так вот видите, где собака зарыта то? А вы говорите "не в указателях дело", да "кавычки не те". В них дело, в них самых.
Кстати, можно не извращаться, а для такого случая использовать готовую встроенную sprintf или ее облегченный вариант siprintf из набора tiny_printf. Но об этом - позже.
Если в описании функции в параметрах запрашивается указатель, то при вызове функции должен быть передан адрес какой-либо переменной. Почему-то никто явно этого не заметил, а написали, что "ошибка не в указателях" и дальше пошли в какие-то далекие дебри про двухмерные указатели, только лишь мельком зацепившись за несоответствие параметра функции.
А раз так, то разберем детально:
Код: Выделить всё
void lcd_out (unsigned char *text) // параметр функции в виде указателя
{
unsigned char ch; // переменная, в которую будет читаться то, что принято по указателю
ch = *text; // собственно, это и есть чтение в переменную по принятому указателю
}
void main(void)
{
unsigned char symbol = 'A'; // переменная с одиночным символом
unsigned char message[] = "whattafaka"; // массив переменных в виде строки, инициализированной последовательностю символов
lcd_out(&symbol); // вызов функции и передача в нее параметра в виде адреса одиночной переменной
lcd_out(message); // вызов функции и передача в нее параметра в виде адреса массива символов.
// Без знака &, поскольку имя одномерного массива - это и есть его адрес,
//адрес первого его элемента
}
А вот попытка сделать вызов вот так (как поначалу начали советовать):
lcd_out('A');
приведет к предупреждению (если оно включено в настройках) о несовместимости типов. Можно плюнуть на это, а можно прикинуться "типа умным" и вручную привести к запрашиваемому функцией типу указателя, используя наработки из ранее написанного мною.
lcd_out((unsigned char*)'A');
Предупреждений нету, но функция делает совсем не то, что ожидалось. А теперь вспомним, что запрашивает параметр? Он запрашивает какой-то адрес. А что есть 'A' ? Это ANSI-представление числа 0х41. То есть, мы явным образом передаем адрес памяти 0х41. Для функции это будет указатель (ссылка) на адрес 0х41. При дальнейшей работе функции идет чтение по принятому в параметре адресу 0х41. Что там физически находится - хрен его знает. Да, работает. Но результат не тот, что ожидался.
Но чтобы раз и навсегда разобраться с вариантами, теперь перепишем немного нашу функцию и попробуем вызвать ее так, как было в вашем первом вопросе:
Код: Выделить всё
void lcd_out (const char *text)
{
char ch;
ch = *text;
}
/---
void main(void)
{
lcd_out("Whattafaka"); // строка символов в качестве параметра
}
Именно на этом построены всякие printf, в том числе и самописные.
Так вот видите, где собака зарыта то? А вы говорите "не в указателях дело", да "кавычки не те". В них дело, в них самых.
Кстати, можно не извращаться, а для такого случая использовать готовую встроенную sprintf или ее облегченный вариант siprintf из набора tiny_printf. Но об этом - позже.
Ёшкин кот обормот
- ARV
- Ум, честь и совесть. И скромность.
- Сообщения: 18544
- Зарегистрирован: Чт дек 28, 2006 08:19:56
- Откуда: Новочеркасск
- Контактная информация:
Re: Вопросы по С/С++ (СИ)
Самая большая проблема с указателями, с моей точки зрения, это процесс разыменования и приращения.
При разыменовании надо всегда помнить приоритет этой операции, что частенько порождает кучу скобок, вносящих путаницу.
А при приращении - надо держать в голове размер данных разыменованного указателя, что тоже портит настроение, если в программе много указателей разных типов.
При разыменовании надо всегда помнить приоритет этой операции, что частенько порождает кучу скобок, вносящих путаницу.
А при приращении - надо держать в голове размер данных разыменованного указателя, что тоже портит настроение, если в программе много указателей разных типов.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...
Мой уютный бложик... заходите!
при взгляде на многих сверху ничего не меняется...
Мой уютный бложик... заходите!
- Zhuk72
- Сверлит текстолит когтями
- Сообщения: 1231
- Зарегистрирован: Ср янв 29, 2014 08:41:31
- Откуда: Баку
- Контактная информация:
Re: Вопросы по С/С++ (СИ)
[uquote="Зурбаган",url="/forum/viewtopic.php?p=3129745#p3129745"]Да нет, тут не в практической реализации дело. Судя по вашему первому вопросу, уж извините, дело в теоретических пробелах.[/uquote]
Для меня реализация в программе - это именно практика. Смысл указателя, получение содержимого по указателю (*) и получение адреса переменной, на который он указывает (&), мне понятны.
Я же писал выше, что у меня получилось избавиться от ошибок.
После исправления у меня получилось так:Я изначально и вообще всегда работал с массивами через индексы, так мне понятнее, но вот литература говорит, что обращаться к массиву через указатель правильнее. Отсюда и пошло вот это: lcd_send(*(lcd_font[0]+ch * 5 + i),DTA).
Массив:
Вызов остался прежним:
Вот на на эту строку я и получаю предупреждение в виде восклицательного знака с подсказкой passing 'char [5]' to parameter of type 'unsigned char *' converts between pointers to integer types with different sign, которое тем не менее отсутствует в окне Build output.
Добавлено after 5 minutes 44 seconds:
Забыл добавить, что вышеуказанное на практике пока не проверил, т.к. дисплей сразу не завелся, пока не понял причину.
Для меня реализация в программе - это именно практика. Смысл указателя, получение содержимого по указателю (*) и получение адреса переменной, на который он указывает (&), мне понятны.
Я же писал выше, что у меня получилось избавиться от ошибок.
После исправления у меня получилось так:
Код: Выделить всё
void lcd_out (unsigned char row, unsigned char col, unsigned char *text)
{
unsigned char ch, i;
lcd_setxy(row,col*6);
while (*text)
{
ch = *text;
if ((ch >= 32) && (ch <= '~')) ch -= 32;
else if ((ch >= 192) && (ch <= 255)) ch -= 97; // 'А' <= ch <= 'я'
else ch = 255;
for (i = 0; i < 5 ; i++)
{
lcd_send(*(lcd_font[0]+ch * 5 + i),DTA);
}
lcd_send(0,DTA); // Additional space
text++;
}
}
Массив:
Код: Выделить всё
const unsigned char lcd_font[159][5] =
{
0x00, 0x00, 0x00, 0x00, 0x00, // sp - 0
0x00, 0x00, 0x2f, 0x00, 0x00, // ! - 1
0x00, 0x07, 0x00, 0x07, 0x00, // " - 2
...................
}
Код: Выделить всё
lcd_out(1,3,"test");
Добавлено after 5 minutes 44 seconds:
Забыл добавить, что вышеуказанное на практике пока не проверил, т.к. дисплей сразу не завелся, пока не понял причину.
Каждый имеет право на свое личное ошибочное мнение.
У меня было тяжелое детство - я до 14 лет смотрел черно-белый телевизор.
У меня было тяжелое детство - я до 14 лет смотрел черно-белый телевизор.
- WiseLord
- Друг Кота
- Сообщения: 4905
- Зарегистрирован: Чт апр 11, 2013 11:19:59
- Откуда: Минск
- Контактная информация:
Re: Вопросы по С/С++ (СИ)
Ну так в фунции параметр типа "unsigned char *", а посылается в неё "char *". Вот и говорит оно о неявном преобразовании указателей на разные типы - знаковый и беззнаковый.
- Zhuk72
- Сверлит текстолит когтями
- Сообщения: 1231
- Зарегистрирован: Ср янв 29, 2014 08:41:31
- Откуда: Баку
- Контактная информация:
Re: Вопросы по С/С++ (СИ)
Текст (массив символов) по умолчанию считается signed?
Каждый имеет право на свое личное ошибочное мнение.
У меня было тяжелое детство - я до 14 лет смотрел черно-белый телевизор.
У меня было тяжелое детство - я до 14 лет смотрел черно-белый телевизор.
Re: Вопросы по С/С++ (СИ)
[uquote="Zhuk72",url="/forum/viewtopic.php?p=3129810#p3129810"]Я изначально и вообще всегда работал с массивами через индексы, так мне понятнее, но вот литература говорит, что обращаться к массиву через указатель правильнее.[/uquote]
Пишите как вам понятнее. Компилятор соптимизирует, а глаза меньше болеть будут от расшифровки. Особенно когда вернётесь править этот код через несколько недель.
[uquote="Zhuk72",url="/forum/viewtopic.php?p=3129847#p3129847"]Текст (массив символов) по умолчанию считается signed?[/uquote]
"Знаковость" типа char в случае отсутствия явного указания signed/unsigned трактуется как правило в зависимости от настроек компилятора/проекта. Прошерстите в этих свойствах.
Пишите как вам понятнее. Компилятор соптимизирует, а глаза меньше болеть будут от расшифровки. Особенно когда вернётесь править этот код через несколько недель.
[uquote="Zhuk72",url="/forum/viewtopic.php?p=3129847#p3129847"]Текст (массив символов) по умолчанию считается signed?[/uquote]
"Знаковость" типа char в случае отсутствия явного указания signed/unsigned трактуется как правило в зависимости от настроек компилятора/проекта. Прошерстите в этих свойствах.
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! 
- ARV
- Ум, честь и совесть. И скромность.
- Сообщения: 18544
- Зарегистрирован: Чт дек 28, 2006 08:19:56
- Откуда: Новочеркасск
- Контактная информация:
Re: Вопросы по С/С++ (СИ)
Язык Си - фантастическая солянка противоречий. Например, типы данных в Си - это просто чудо! Единственный тип, о котором хоть что-то наперед КОНКРЕТНО известно, это int - но известно о нем только то, что это тип со знаком и его размер определяется платформой. А все прочие типы опираются на int и так или иначе (снова по-разному) кратны его размеру.
казалось бы, что может быть проще char-а? это ведь просто байт! но нет: char на самом деле платформозависимый тип и единственное, что про него известно гарантированно, что его размера должно хватать для обозначения кода символа (по-моему, английского алфавита). про знак char-а не сказано ничего конкретного. поэтому формально вполне может быть что char != unsigned char или (зависит от компилятора) char != signed char. соответственно указатель на char может быть как указатель на число со знаком, так и без. существуют платформы, где char занимает больше 8 бит.
именно поэтому в стандарте С99 появились типы гарантированной разрядности int8_t, int16_t, uint16_t и т.д. - но опять-таки формально они не эквивалентны соответствующим стандартным типам аналогичной разрядности. правда, степень этой формальности разная, например, в AVR-GCC все эти intXX_t являются алиасами стандартных... что еще больше запутывает
казалось бы, что может быть проще char-а? это ведь просто байт! но нет: char на самом деле платформозависимый тип и единственное, что про него известно гарантированно, что его размера должно хватать для обозначения кода символа (по-моему, английского алфавита). про знак char-а не сказано ничего конкретного. поэтому формально вполне может быть что char != unsigned char или (зависит от компилятора) char != signed char. соответственно указатель на char может быть как указатель на число со знаком, так и без. существуют платформы, где char занимает больше 8 бит.
именно поэтому в стандарте С99 появились типы гарантированной разрядности int8_t, int16_t, uint16_t и т.д. - но опять-таки формально они не эквивалентны соответствующим стандартным типам аналогичной разрядности. правда, степень этой формальности разная, например, в AVR-GCC все эти intXX_t являются алиасами стандартных... что еще больше запутывает
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...
Мой уютный бложик... заходите!
при взгляде на многих сверху ничего не меняется...
Мой уютный бложик... заходите!
- Zhuk72
- Сверлит текстолит когтями
- Сообщения: 1231
- Зарегистрирован: Ср янв 29, 2014 08:41:31
- Откуда: Баку
- Контактная информация:
Re: Вопросы по С/С++ (СИ)
Нашел картинку (просто пример, на выделение не обращайте внимания):
Опция Plain char as signed в любом виде на предупреждение никак не влияет, С99 тоже.
Ради интереса тикнул вариант Strict ANSI C; в результате при попытке скомпилировать получил 150 ошибок и 10 предупреждений
Спойлер

Ради интереса тикнул вариант Strict ANSI C; в результате при попытке скомпилировать получил 150 ошибок и 10 предупреждений
Каждый имеет право на свое личное ошибочное мнение.
У меня было тяжелое детство - я до 14 лет смотрел черно-белый телевизор.
У меня было тяжелое детство - я до 14 лет смотрел черно-белый телевизор.
Re: Вопросы по С/С++ (СИ)
[uquote="ARV",url="/forum/viewtopic.php?p=3129749#p3129749"]А при приращении - надо держать в голове размер данных разыменованного указателя[/uquote]
sizeof? вообще приращение типизированных указателей должно и так само работать с его учетом
sizeof? вообще приращение типизированных указателей должно и так само работать с его учетом
Последний раз редактировалось arkhnchul Вт июн 20, 2017 13:56:29, всего редактировалось 1 раз.
- ARV
- Ум, честь и совесть. И скромность.
- Сообщения: 18544
- Зарегистрирован: Чт дек 28, 2006 08:19:56
- Откуда: Новочеркасск
- Контактная информация:
Re: Вопросы по С/С++ (СИ)
что вы хотели этим сказать? например, как sizeof поможет мне разобраться, на сколько изменится значение указателя по записи *ptr++? sizeof тут не липнет - только просмотр исходника до места описания указателя, чтобы вспомнить, что он long int *ptr и поэтому увеличится на 4.arkhnchul писал(а):sizeof?
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...
Мой уютный бложик... заходите!
при взгляде на многих сверху ничего не меняется...
Мой уютный бложик... заходите!
Re: Вопросы по С/С++ (СИ)
[uquote="ARV",url="/forum/viewtopic.php?p=3129926#p3129926"]
это к "держать в голове"
честно говоря, могу предположить не так много вариантов, когда из данных, на которые у нас есть указатель, нужно получать значения другого типа и знать для этого их размер. Разве что разгребать какой-нибудь сырой кусок данных в виде uint8_t*, содержащий некие известные структуры.
что вы хотели этим сказать?[/uquote]arkhnchul писал(а):sizeof?
это к "держать в голове"
Re: Вопросы по С/С++ (СИ)
[uquote="ARV",url="/forum/viewtopic.php?p=3129749#p3129749"]А при приращении - надо держать в голове размер данных разыменованного указателя, что тоже портит настроение, если в программе много указателей разных типов.[/uquote]
Я бы сказал - немножко иначе, нужно быть аккуратным, чтобы тип указателя при приращении был правильным - а компилятор сам нужное количество байтов отсчитает. Ведь операция проводится над структурированными данными, а количество байтов мало кого из шерифов волнует, как правило. Ну а для именования переменных - проклятая венгерская нотация не совсем уж и бесполезна.
Я бы сказал - немножко иначе, нужно быть аккуратным, чтобы тип указателя при приращении был правильным - а компилятор сам нужное количество байтов отсчитает. Ведь операция проводится над структурированными данными, а количество байтов мало кого из шерифов волнует, как правило. Ну а для именования переменных - проклятая венгерская нотация не совсем уж и бесполезна.
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! 
- ARV
- Ум, честь и совесть. И скромность.
- Сообщения: 18544
- Зарегистрирован: Чт дек 28, 2006 08:19:56
- Откуда: Новочеркасск
- Контактная информация:
Re: Вопросы по С/С++ (СИ)
использование указателей, с моей точки зрения, примерно в половине случаев как раз и требуется дя ковыряния в сырых данных
для "логичного" доступа квадратные скобки с индексом понятнее гораздо, да и классическое разыменование тут уместно. выручают так же соглашения об именах переменных. Microsoft, например, страшно любит "префиксы" к переменным, аж скулы сводит от всяких lsptrData
во: пока писал, сообщили про венгерскую нотацию - она самая, в яблочко! 
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...
Мой уютный бложик... заходите!
при взгляде на многих сверху ничего не меняется...
Мой уютный бложик... заходите!
Re: Вопросы по С/С++ (СИ)
[uquote="Zhuk72",url="/forum/viewtopic.php?p=3129810#p3129810"]void lcd_out (unsigned char row, unsigned char col, unsigned char *text)
{
.....
Вызов остался прежним:
Вот на на эту строку я и получаю предупреждение .[/uquote]
Вообще-то, если подходить с правильного направления, то та рассматриваемая функция изначально предназначена для работы совместно с одной из "печатающих" функций типа форматной sprint или неформатной "копирующей" strcpy . Это стандартные встроенные функции, поставляемые с компилятором. Правда, встроенная sprintf довольно тяжела по объему и скорости, и потому у нее есть облегченный вариант siprintf с урезанным функционалом, входящий в комплект tiny printf.
Суть этого - получение универсального способа вывода, причем не только на физический дисплей, но и куда угодно, хоть в UART-терминалку, хоть в азбуку Морзе.
Выглядеть это может вот так, на примере вывода двух строк в классический двухстрочник 1602 (на самом деле, эти функции не привязаны к конкретному дисплею)
а сама функция просмотра строки для вывода на дисплей будет вот такой:
благодаря такой конструкции мы можем выводить в дисплей любые данные - текстовые сообщения, текст с переменной, просто переменную в нужной нам позиции на дисплее
Таким образом, мы видим, что мы простым способом можем вывести в дисплей любую инфу в любом месте дисплея и нам не требуется изобретать ничего в функции вывода на дисплей. Выбор способа идет на уровне подготовки массива текстового буфера.
{
.....
Вызов остался прежним:
Код: Выделить всё
lcd_out(1,3,"test");
Вообще-то, если подходить с правильного направления, то та рассматриваемая функция изначально предназначена для работы совместно с одной из "печатающих" функций типа форматной sprint или неформатной "копирующей" strcpy . Это стандартные встроенные функции, поставляемые с компилятором. Правда, встроенная sprintf довольно тяжела по объему и скорости, и потому у нее есть облегченный вариант siprintf с урезанным функционалом, входящий в комплект tiny printf.
Суть этого - получение универсального способа вывода, причем не только на физический дисплей, но и куда угодно, хоть в UART-терминалку, хоть в азбуку Морзе.
Выглядеть это может вот так, на примере вывода двух строк в классический двухстрочник 1602 (на самом деле, эти функции не привязаны к конкретному дисплею)
Код: Выделить всё
#define LINE_LENGTH (16+1) // длина строки, плюс символ конца строки
char txtbuf[LINE_LENGTH * 2] = {0}; // текстовый буфер длиной в 2 строки по (16+1) элементов
int length; // длина строки
int voltage = 0, current = 0; // переменные для значений напряжения и тока
voltage = 7;
current = 210;
length = sprintf(txtbuf, "Voltage: %2i V", voltage); // первая строка (возвращается число скопированных символов)
length = sprintf(txtbuf + LINE_LENGTH, "Current: %4i mA", current); // вторая строка
/* либо немного иначе, но функция вывода должна распознавать символ переноса строки \n */
length = sprintf(txtbuf, "Voltage: %2i V\nCurrent: %4i mA", voltage, current);
write_to_LCD(txtbuf, LINE_LENGTH * 2 ); // выводятся в дисплей две строки сразу
Код: Выделить всё
int write_to_LCD(char *buf, int lehgth)
{
/* это заглушка функции */
}
Код: Выделить всё
sprintf(txtbuf + 5, "%i", voltage); // вывод только значения переменной в опред.позиции
sprintf(txtbuf + LINE_LENGTH + 3, "Hello world"); // вывод текста в определенной позиции
strcpy(txtbuf, " "); // затирается строка
const char message[] = {"Main messaga"}; // есть некий массив, инициализованный как строка
strcpy(txtbuf+1, message); // копируем ее в текстовый буфер для вывода
strcpy(txtbuf + LINE_LENGTH, "Waka-Waka-shet"); // или пишем напрямую на второй строчке
Ёшкин кот обормот


