Страница 215 из 386
Re: Вопросы по С/С++ (СИ)
Добавлено: Чт фев 26, 2015 08:16:06
baron_P
Спасибо.
Т.е. lcd_write(*str++) означает, что *str (символ-константа) отправится на обрабоку в функцию, а str (переменная-адрес) инкрементируется и идет дальше на проверку условия перед след. итераций цикла while. Тогда еще глупые вопросы: почему в функцию отправляется не конечный результат выражения *str++, а промежуточный (ведь все выражение стоит в качестве аргумента)?
const char *str - переменная-указатель на символ-константу
const *char str - указатель-константа на символ переменную
Логику в этих выражениях не уловил, постараюсь просто запомнить.
О состоянии str сложно написано, не понял в чем возможны проблемы с этим выражением. Я передаю *str в качестве аргумента функции lcd_write. Что она там с ним делает не важно - после выхода из функции эта переменная перестает существовать, ведь локальная. Внутри функции lcd_puts указатель str в явном виде инкрементирует и почему кто-то (кроме меня, еще не вкурившего возню с указателями) должен подумать, что там останется старое значение, непонятно. А вне функции lcd_puts переменная опять же бессмыслена, т.к. локальная.
Но еще один вопрос вызывает использование класса static. Он тут применен для фукнции которая ничего не возвращает (void же), зачем это сделано?
Re: Вопросы по С/С++ (СИ)
Добавлено: Чт фев 26, 2015 08:53:46
WiseLord
baron_P писал(а):еще глупые вопросы: почему в функцию отправляется не конечный результат выражения *str++, а промежуточный (ведь все выражение стоит в качестве аргумента)?
Есть понятие преинкремента и постинкремента. Например:
Код: Выделить всё
char ch = 'a';
lcd_write(ch++);
lcd_write(ch);
- на дисплее отобразится "ab"
Код: Выделить всё
char ch = 'a';
lcd_write(++ch);
lcd_write(ch);
- на дисплее отобразится "bb"
При постинкременте для вычисления всего выражения используется старое значение переменной, и только потом переменная инкрементируется.
При преинкременте - сначала вычисляется значение переменной, а уж потом оно используется для других вычислений.
Так и здесь - при lcd_write(*str++) в функцию отправится число, находящееся по старому адресу, и лишь потом адрес изменяется.
Re: Вопросы по С/С++ (СИ)
Добавлено: Чт фев 26, 2015 09:03:25
uk8amk
baron_P
В функцию и отправляется только вычисленный результат выражения, а не набор каких-то непонятных операторов. Это происходит на этапе компиляции проекта.
К static функциям обращаться можно внутри одного файла-модуля C. Внешне они недоступны.
Re: Вопросы по С/С++ (СИ)
Добавлено: Чт фев 26, 2015 10:04:33
Siarzhuk
baron_P писал(а):Тогда еще глупые вопросы: почему в функцию отправляется не конечный результат выражения *str++, а промежуточный (ведь все выражение стоит в качестве аргумента)?
Результат выражения и есть символ по исходному указателю. Инкремент-же указателя - можете считать "побочным" эффектом.
baron_P писал(а):const *char str - указатель-константа на символ переменную
Окровенно говоря тип "*char" встречаю впервые - хотелось бы развёрнутый пример применения. Подозреваю имелось ввиду char* const str - т.е. указатель-константа - который нельзя изменить в отличие от переменной-указателя на константу менять который можно.
Попробую пояснить кодом:
Код: Выделить всё
const char* str1 = "const char* str1\n"; // указатель-переменная
const char* str2 = "const char* str2\n"; // указатель-переменная
char* const cstr = "const char* cstr\n"; // указатель-константа
int _tmain(int argc, _TCHAR* argv[])
{
printf(str1);
printf(str2);
printf(cstr);
str2 = str1; // можно присваивать
printf(str2);
str1 = cstr; // можно присваивать
printf(str1);
cstr = "just a char*"; // error C3892: 'cstr' : you cannot assign to a variable that is const
printf(cstr);
return 0;
}
baron_P писал(а):Внутри функции lcd_puts указатель str в явном виде инкрементирует и почему кто-то (кроме меня, еще не вкурившего возню с указателями) должен подумать, что там останется старое значение, непонятно.
Вы инструктаж по технике безопасности регулярно проходите? Тем не менее никого не удивляет что ноги-руки-пальцы регулярно отрезаются, отвёртки-молотки пробивают-калечат подушечки пальцев а киловольты исправно жгут тела нарушителей ТБ. Вот и считайте это своего рода инструктажем. Долгоживущий код имеет тенденцию к разбуханию и функция в процессе разработки и поддержки может принимать такие причудливые формы что то, что было очевидно исходному писателю просто теряется в глубине спагетти-кода. Разобрать даже собственный код по прошествии времени требует усилий - потому как оно всегда и бывает - лепим по быстрому, пробный прогон - косяк, отладчик - мать-мать-мать - минус десять минут жизни коорые можно было сохранить соблюдая правила. Посему если оптимизация не довлеет - писать нужно ясно и понятно и не только для себя самого.
baron_P писал(а):Но еще один вопрос вызывает использование класса static. Он тут применен для фукнции которая ничего не возвращает (void же), зачем это сделано?
Класс static для глобальных объектов - это гарантия того, что нигде кроме как в этом модуле этот объект не используется - в общем такая-же "техника безопасности".
Re: Вопросы по С/С++ (СИ)
Добавлено: Пт фев 27, 2015 08:33:27
baron_P
Спасибо.
Не сразу дошло, что *str++ и *(str+1) не одно и то же.
По static-функциям вроде понятно.
А с костантами-указателями путаница.
const char *str;
const char* str;
Это одно и тоже или нет? К чему тут применяется разыменование указателя?
Я правильно понял, что здесь str1 - это адрес первого элемента строки и *srt1 == 'c'?
Т.е. *str1 это константа, а str1 - переменная, за счет изменения которой я и могу считать несколько констант, составляющих строку?
А здесь костанты две - cstr и *cstr. Т.е. я не могу ни изменить записанный в cstr адрес, ни поменять записанный в *cstr символ. Так?
Re: Вопросы по С/С++ (СИ)
Добавлено: Пт фев 27, 2015 09:20:34
GARMIN
baron_P писал(а):
const char *str;
const char* str;
Это одно и тоже или нет? К чему тут применяется разыменование указателя?
Это одно и то же. Первая запись более правильная, так как указывает, что str является указателем.
Как пример, показывающий разницу в использовании:
const char* str1, str2;
const char *str1, str2;
в первом примере не видно, что str2 не указатель, а символ.
baron_P писал(а):
Я правильно понял, что здесь str1 - это адрес первого элемента строки и *srt1 == 'c'?
Т.е. *str1 это константа, а str1 - переменная, за счет изменения которой я и могу считать несколько констант, составляющих строку?
А здесь костанты две - cstr и *cstr. Т.е. я не могу ни изменить записанный в cstr адрес, ни поменять записанный в *cstr символ. Так?
здесь есть строка без имени "const char* cstr\n", расположенная где-то в памяти, и константный указатель на неё cstr. Строка может располагаться в ОЗУ, тогда её можно изменить. Явно это не указывается, зависит от контекста.
Re: Вопросы по С/С++ (СИ)
Добавлено: Пт фев 27, 2015 09:43:34
Siarzhuk
baron_P писал(а):А с костантами-указателями путаница.
const char *str;
const char* str;
Это одно и тоже или нет? К чему тут применяется разыменование указателя?
Это лишь разные стили объявления одного и того-же типа переменной - указателя на неизменяемые данные. И как водится по поводу положения этой звёздочки идут бесконечные битвы остроконечников с тупоконечниками - одни говорят, что звёздочка принадлежит типу и кошернее писать "char*" а другие моментально двигают им под нос вот такой код:
Код: Выделить всё
const char* str2 = "str2", str3 = "str3"; // ошибка!
const char* str2 = "str2", *str3 = "str3"; // ОК - но некрасиво-же!
const char *str2 = "str2", *str3 = "str3"; // И ОК и красиво!
После чего обычно приходит местный проектенфюрер и прописывает в кодестайл гайдлайнах проекта тот стиль, что ему больше нравится.
baron_P писал(а):str1 - переменная, за счет изменения которой я и могу считать несколько констант, составляющих строку?
Да
baron_P писал(а):
А здесь костанты две - cstr и *cstr. Т.е. я не могу ни изменить записанный в cstr адрес, ни поменять записанный в *cstr символ. Так?
Верно. Мне пожалуй стоило сразу сделать свой пример нагляднее добавлением вот такого такого копирования:
Код: Выделить всё
char data[10] = { 0 };
char* const cstr = data;
[...]
for (int i = 0; i < 9; i++)
cstr[i] = str1[i]; // ага.. работает!
cstr = "just a char*"; // error C3892: 'cstr' : you cannot assign to a variable that is const
PS: Поправил выделение cstr.

Re: Вопросы по С/С++ (СИ)
Добавлено: Пт фев 27, 2015 13:05:35
baron_P
Спасибо, вроде понял, но ваше дополнение вызвало к жизни еще пару вопросов.
Код: Выделить всё
for (int i = 0; i < 9; i++)
cstr[i] = str1[i]; // ага.. работает!
А почему работает? cstr и str1 это ведть не массивы и не строки, а только только одно число (адрес первого сивола соотв. строки). Разве можно к не заданному явно массиву обращаться по индексам?
Я правильно понял, что тут, если б cstr не было константой, в него б записался адрес первого символа строки?
Re: Вопросы по С/С++ (СИ)
Добавлено: Пт фев 27, 2015 13:51:15
Siarzhuk
baron_P писал(а):Спасибо, вроде понял, но ваше дополнение вызвало к жизни еще пару вопросов.
Код: Выделить всё
for (int i = 0; i < 9; i++)
cstr[i] = str1[i]; // ага.. работает!
А почему работает? cstr и str1 это ведть не массивы и не строки, а только только одно число (адрес первого сивола соотв. строки). Разве можно к не заданному явно массиву обращаться по индексам?
Указатель хранит лишь адрес им указываемого объекта. cstr и str1 - это соответственно константа и переменная, хранящие адрес на первые байты соответствующих данных. Содержимое по указателю появляется лишь как результат разыменования указателя. Тип указателя (char*, uint16_t*, uint32_t*) определяет размер элемента в виртуальном массиве на начало которого и указывает указатель. Т.е. к нему равноправно можно обращаться как разыменованием так и доступом а-ля массив [индекс]. Индекс массива - суть смещение от начала массива. Это смещение - на
размер элемента!
Код: Выделить всё
uint32_t data = 0x11223344;
uint16_t* p = (uint16_t*)&data;
// оба вызовы равнозначны - выведут "верхнее" и "нижнее" слова - нюансы endian-ности опустим для простоты ;-)
pritnf( "%04x %04x\n", p[0], p[1]);
pritnf( "%04x %04x\n", *p, *(p + 1));
baron_P писал(а):Я правильно понял, что тут, если б cstr не было константой, в него б записался адрес первого символа строки?
Разъяснение смотрите выше. Указатель хранит лишь адрес указуемого объекта.
Re: Вопросы по С/С++ (СИ)
Добавлено: Пт фев 27, 2015 14:00:33
WiseLord
Siarzhuk писал(а):cstr и str1 это ведть не массивы и не строки, а только только одно число
cstr - это не число, это указатель. Указатель константый, то есть он измениться не может. Поэтому ему нельзя присвоить значение, он может указывать только в одно место, т.е. хранить постоянный адрес. Но данные, на которые он указывает, менять никто не мешает.
А по поводу обращения по индексам - запись a
абсолютно эквивалентна записи *(a + i); Обращаться можно как угодно.
Например, код
вполне допустим. В результате в a будет записано значение, которое раньше было в байте на 7 ячеек правее.
Другое дело, что на компьютере такая программа хоть и скомпилируется, но, скорее всего, будет завершаться с ошибкой, так как операционная система обычно отслеживает, чтобы программа не копалась в областях памяти, которые выделены не ней, и прибивает её в таких случаях.
Re: Вопросы по С/С++ (СИ)
Добавлено: Пт фев 27, 2015 14:32:42
baron_P
Спасибо, кажется понял.
До разъяснения думал, что указатель все же хранит некоторое число, которое и есть адрес в памяти той ячейки, на которую он указывает. И с этим числом можно работать как с обычной переменной соотвествующего типа. Но все оказалось таки сложнее.
Re: Вопросы по С/С++ (СИ)
Добавлено: Пт фев 27, 2015 14:46:10
Siarzhuk
WiseLord писал(а):
вполне допустим. В результате в a будет записано значение, которое раньше было в байте на 7 ячеек правее.
Другое дело, что на компьютере такая программа хоть и скомпилируется, но, скорее всего, будет завершаться с ошибкой, так как операционная система обычно отслеживает, чтобы программа не копалась в областях памяти, которые выделены не ней, и прибивает её в таких случаях.
Позволю себе дополнить - если переменная 'а' выделена локально на стеке функции - при чтении ничего страшного не произойдёт, при записи - новое значение уйдёт в одну из переменных которая была объявлена за 'b' (если компилятор их не перетасовал) либо просто в неиспользуемую часть стека функции. При выделении в глобальной области - изменится одна из соседних переменных, либо, если 'a' выделена буквально в самом конце сегмента данных и в 7-ми байтах "правее" находится невыделенная область памяти - произойдёт исключение нарушения доступа - обработчик которого и завершит программу. Но всё конечно же зависит от конкретной архитектуры и того куда эту переменную "поселил" компилятор.
Re: Вопросы по С/С++ (СИ)
Добавлено: Пт фев 27, 2015 15:14:53
Siarzhuk
Что-то мы плохо разъясняем.
baron_P писал(а):До разъяснения думал, что указатель все же хранит некоторое число, которое и есть адрес в памяти той ячейки, на которую он указывает.
Именно так - там хранится адрес.
baron_P писал(а): И с этим числом можно работать как с обычной переменной соотвествующего типа.
Именно так © - лишь компилятор будет ворчать на несоответствия типов. Это число N можно использовать чтобы "отсчитать" N-ое количество байт от начала памяти и прочитать содержимое указуемой переменной:
Код: Выделить всё
int a = 1234567890;
int* pa = &a;
char* p = (char*)0; // указатель на "начало" памяти ;-)
char* pc = &p[(int)pa]; // используем 'pa' - как индекс в памяти, приведение типа - чтобы компилятор не ругался
int* pi = (int*)pc; // мы ведь хотим прочитать оттуда не один байт ( т.е. sizeof(char)) а целых sizeof(int) байт, верно?
printf("%d или %d\n", *pa, *pi); // и что нам покажет smoke test? ;-)
Re: Вопросы по С/С++ (СИ)
Добавлено: Пт фев 27, 2015 15:15:59
WiseLord
baron_P писал(а):До разъяснения думал, что указатель все же хранит некоторое число, которое и есть адрес в памяти той ячейки, на которую он указывает. И с этим числом можно работать как с обычной переменной соотвествующего типа.
Так и есть. Указатель - это именно число, которое есть некий адрес, на который он указывает. Но с этим числом-указателем можно работать только в ограниченном диапазоне операций - например, прибавлять-отнимать некое значение (перемещаться по памяти), разыменовывать указатель (*p - получить значение). Можно обращаться как к массиву (и получать значения из соседних с указываемой ячеек). Но нельзя, например, сложить два указателя, или умножить на что-то.
В AVR8 любой указатель занимает 2 байта, не важно на что он указывает. Хоть на char, хоть на long long.
В машинах с архитектурой фон Неймана уже другие размеры указателей: в x86 - 32 байта, в x86_64 - 64 байта. Собственно, разрядность системы и определяет размер указателя.
Отсюда, кстати, и принципиальная невозможность тех же 32битных архитектур адресовать больше 4Гб памяти. Потому что 2³² = 4 294 967 296, и указатель в принципе не может хранить большее число. Если очень нужно - приходится извращаться и вводить дополнительные абстракции типа страниц памяти, и тому подобных.
Re: Вопросы по С/С++ (СИ)
Добавлено: Сб фев 28, 2015 18:04:35
Chip115
Всем привет! Подскажите, пожалуйста, как реализовать не блокирующею функцию задержки.
В общем, вот псевдокод тела функции, которая, на ряду с остальными, вызывается в общем цикле
Код: Выделить всё
u8Funk_1 ();
vFunk_2(a);
vFunk_2(b);
delay_ms(1000);
u8Funk_1 ();
vFunk_2(a);
vFunk_2(c);
some_variable1 = 1;
some_variable2= 2;
some_variable3 = 3;
some_variable4 = 4;
Есть затык в виде delay_ms, но в этом месте надо выдержать 1 сек.
Нужна ф-ция, которая не вешает проц. ибо подобных мест в программе будет достаточно и если в каждой будем тупить, то будет плохо.
Можете подсказать решение проблемы?
В гугле пытался найти "не блокирующая функция задержки". Выдает всякую фигню.
Ставить FreeRTOS не охота.
Re: Вопросы по С/С++ (СИ)
Добавлено: Сб фев 28, 2015 18:28:59
WiseLord
Не совсем задача корректно поставлена, но попробую пояснить, как я подобное делал для термодатчика DS18B20.
Суть там в том, измерение он производит в течение 750мс. То есть, после команды на измерение надо выждать 750мс, и только тогда можно вызвать функцию, которая запросит результат у датчика.
Решение в лоб - такое:
Код: Выделить всё
measureTemp();
_delay_ms(750);
temp = getTemp();
Но возникает та же проблема, что Вы описали - МК висит в цикле ожидания. Не самое лучшее решение.
Идея обойти в этом - запустить функцию измерения температуры раз в 750мс по таймеру, по прерыванию. И тогда можно будет сделать такой набор функций:
Код: Выделить всё
updateTemp() { // вызывать не чаще раза в 750мс по таймеру
_temp = getTemp(); // узнаём у датчика последнее значение температуры
measureTemp(); // запускаем новое измерение
}
int getTemp() {
return _temp;
}
А в основном цикле температуру узнавать как
Главное - не вызывать updateTemp() чаще, чем надо.
Не знаю, для чего нужна задержка Вам, но принцип может быть похожим.
Re: Вопросы по С/С++ (СИ)
Добавлено: Сб фев 28, 2015 18:58:47
ks0
Так а че процессору делать если не висеть в цикле? Вот конкретно в вашем примере?
Если у него есть другая работа, то используются таймеры или прерывания. Тогда и вся структура программы другая будет. И RTOS для таких простых МК нафиг не уперлась.
Re: Вопросы по С/С++ (СИ)
Добавлено: Вс мар 01, 2015 17:36:45
uldemir
Подскажите гуру, как убрать предупреждения компилятора.
В общем, пишу знакогенератор:
Код: Выделить всё
#define seg_a ((uint8_t)(1<<7))
#define seg_b ((uint8_t)(1<<5))
#define seg_c ((uint8_t)(1<<3))
#define seg_d ((uint8_t)(1<<0))
#define seg_e ((uint8_t)(1<<1))
#define seg_f ((uint8_t)(1<<6))
#define seg_g ((uint8_t)(1<<4))
#define seg_h ((uint8_t)(1<<2))
#include "chars7.c"
----теперь фрагмент инклюда:
#define invertmask ~
#define let_1 invertmask(seg_b | seg_c)
#define let_2 invertmask(seg_a|seg_b|seg_d|seg_e|seg_g)
---- итд.
const uint8_t charset[16] = {let_0, let_1, let_2, let_3, \
let_4, let_5, let_6, let_7, \
let_8, let_9, let_A, let_b, \
let_c, let_d, let_E, let_ };
Собственно, проблема в предупреждении:
Код: Выделить всё
main.c(17): warning: #69-D: integer conversion resulted in truncation const uint8_t charset[16] = {let_0, let_1, let_2, let_3, \
В каком месте мне нужно поставить неопределённый артикль... Кхм, в каком месте тут всплывает int, если аргументы 8-ми разрядные, беззнаковые и операции побитные?
И очень не хотелось бы править инклюд, потому как этот инклюд, планируется использовать с любой шириной данных. В последнем проекте там был, вроде, вообще 32-х разрядный регистр.
Re: Вопросы по С/С++ (СИ)
Добавлено: Вс мар 01, 2015 17:46:07
ks0
В #define invertmask он и всплывает
Re: Вопросы по С/С++ (СИ)
Добавлено: Вс мар 01, 2015 17:53:47
uldemir
Спасибо. Действительно, стоит убрать, как предупреждений нет. осталось придумать как побитно инвертировать, чтобы не было больно.