Страница 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;
Это одно и тоже или нет? К чему тут применяется разыменование указателя?

Код: Выделить всё

const char* str1 = "const char* str1\n";
Я правильно понял, что здесь str1 - это адрес первого элемента строки и *srt1 == 'c'?
Т.е. *str1 это константа, а str1 - переменная, за счет изменения которой я и могу считать несколько констант, составляющих строку?

Код: Выделить всё

char* const cstr = "const char* cstr\n";
А здесь костанты две - 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 писал(а):

Код: Выделить всё

const char* str1 = "const char* str1\n";
Я правильно понял, что здесь str1 - это адрес первого элемента строки и *srt1 == 'c'?
Т.е. *str1 это константа, а str1 - переменная, за счет изменения которой я и могу считать несколько констант, составляющих строку?

Код: Выделить всё

char* const cstr = "const char* cstr\n";
А здесь костанты две - 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 писал(а):

Код: Выделить всё

char* const cstr = "const char* cstr\n";
А здесь костанты две - 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 = "just a char*"
Я правильно понял, что тут, если б 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); Обращаться можно как угодно.
Например, код

Код: Выделить всё

char a;
char *b;
b = &a;
a = b[7];
вполне допустим. В результате в a будет записано значение, которое раньше было в байте на 7 ячеек правее.

Другое дело, что на компьютере такая программа хоть и скомпилируется, но, скорее всего, будет завершаться с ошибкой, так как операционная система обычно отслеживает, чтобы программа не копалась в областях памяти, которые выделены не ней, и прибивает её в таких случаях.

Re: Вопросы по С/С++ (СИ)

Добавлено: Пт фев 27, 2015 14:32:42
baron_P
Спасибо, кажется понял.
До разъяснения думал, что указатель все же хранит некоторое число, которое и есть адрес в памяти той ячейки, на которую он указывает. И с этим числом можно работать как с обычной переменной соотвествующего типа. Но все оказалось таки сложнее.

Re: Вопросы по С/С++ (СИ)

Добавлено: Пт фев 27, 2015 14:46:10
Siarzhuk
WiseLord писал(а):

Код: Выделить всё

char a;
char *b;
b = &a;
a = b[7];
вполне допустим. В результате в 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;
}
А в основном цикле температуру узнавать как

Код: Выделить всё

temp = getTemp();
Главное - не вызывать 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
Спасибо. Действительно, стоит убрать, как предупреждений нет. осталось придумать как побитно инвертировать, чтобы не было больно.