Что за редактор используете?uk8amk писал(а):Только у меня не работает автозавершение перечислений?
Вопросы по С/С++ (СИ)
- Сообщения: 353
- Зарегистрирован: Вс янв 19, 2014 22:41:55
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! 
- Реклама
Keil mdk 4.73, иногда Codevision.
Похоже ограничение самих IDE. В принципе можно прикрутить и внешний редактор, но пока и так сойдёт.
Похоже ограничение самих IDE. В принципе можно прикрутить и внешний редактор, но пока и так сойдёт.
- Сообщения: 353
- Зарегистрирован: Вс янв 19, 2014 22:41:55
В uVision http://www.keil.com/support/man/docs/uv ... decomp.htm попробуйте Ctrl-<пробел> после набора первых символов.uk8amk писал(а):Keil mdk 4.73, иногда Codevision.
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! 
- Сообщения: 211
- Зарегистрирован: Пт окт 21, 2011 14:39:31
Код: Выделить всё
(void)0;
Это где такое встретилось? А то что-то у меня чувство, что в C/С++ это бессмысленно. Вот (void*)0 - другое дело.
- Реклама
- Сообщения: 353
- Зарегистрирован: Вс янв 19, 2014 22:41:55
Увы, определённо можно лишь сказать , что это строка вырванная [вами] из контекста - потому и непонятная. Если это часть макроса во не-отладочной конфигурации - что встречается повсеместно - то судя по всему упреждает предупреждения компилятора типа "неэффективная операция". Что-то типа nop() но без всякого представления в скомпилированном объекте.FeCat писал(а):Что это такое? Приведение ноля к типу void? Не понимаю. Что это с точки зрения стандарта, что делает компилятор?Код: Выделить всё
(void)0;
На эту тему ещё нагугливается следующее:
Кроме того приведение к void предотвращает от использования макро в операциях присваивания - своего рода эмуляция функции не возвращающей ничего.The (void) cast is not merely a choice by a particular implementation; it's required by the C standard. Quoting the 2011 ISO C standard (similar wording appears in the 1990 and 1999 editions):If NDEBUG is defined as a macro name at the point in the source file where <assert.h> is included, the assert macro is defined simply as
#define assert(ignore) ((void)0)
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! 
Доброго времени суток. Поскажите ответы на пару-тройку глупых вопросов. Железка ATMega16, пишется под AVR-GCC. Программа работает, но хочется понять как именно.
Есть пара функций:
И что-то я тут в двух соснах запутался. По первой все понятно: она выводит число на PORTB, формирует импульс разрешения чтения этих данных контроллером LCD-дисплея и формирует задержку, чтоб тот успел все переварить перед следющими командами. А вот со второй не совсем понятно.
Функция получает некоторую строку в таком виде: 'test'. При это запись *str означает, что в константу str запишется адрес первого элемента строки. Цикл while работает до тех пор, пока записанное по адресу str значение не равно нулю. И тут первый вопрос: оно же константа, цикл должен должен быть бесконечным, как может меняться значение *str? Дальше больше. Фукция записи получает значение находящееся по адресу str (т.е. первый элемент строки). После этого происходит инкремент. По логике, это должен быть инкремент адреса. Т.е. сперва над str происходит операция ++, потом *. Тут тоже вопрос: функция в какой момент времени получает свой аргумент? В нее улетит первый и второй элемент строки? Но, допустим, функция свое отработала, возвращаемся к циклу while. Куда сохраниться значение инкремента, которое должно проверятся в условии цикла, *str ведь константа?
Ужас какой-то с этими указателями, ничего не понятно.
Есть пара функций:
Код: Выделить всё
//Функция записи байта в контроллер
static void lcd_write(unsigned char data)
{
LCD_DATA = data; //выдача данных на PORTB
lcd_pulse_e(); //разрешение записи данных в контроллер
lcd_wait(); //ожиданием обработки данных котроллером
}
//Функция вывода строки
void lcd_puts(const char *str)
{
while(*str) //пока не кончится строка, для каждого элемента
{ //вызывается функция записи символа
lcd_write(*str++); //вывод символа на шину данных;
}
}Функция получает некоторую строку в таком виде: 'test'. При это запись *str означает, что в константу str запишется адрес первого элемента строки. Цикл while работает до тех пор, пока записанное по адресу str значение не равно нулю. И тут первый вопрос: оно же константа, цикл должен должен быть бесконечным, как может меняться значение *str? Дальше больше. Фукция записи получает значение находящееся по адресу str (т.е. первый элемент строки). После этого происходит инкремент. По логике, это должен быть инкремент адреса. Т.е. сперва над str происходит операция ++, потом *. Тут тоже вопрос: функция в какой момент времени получает свой аргумент? В нее улетит первый и второй элемент строки? Но, допустим, функция свое отработала, возвращаемся к циклу while. Куда сохраниться значение инкремента, которое должно проверятся в условии цикла, *str ведь константа?
Ужас какой-то с этими указателями, ничего не понятно.
We do what we must because we can (c) GLaDOS
str - не константа ! Это указатель на символ-константу.
"++" после переменной означает, что сначала с ней произойдут некие действия, а потом уже будет инкремент. В данном случае, сначала в функцию передастся символ по указателю, затем произойдёт инкремент указателя (для следующего символа). И так, пока указатель не будет указывать на нулевой символ.
Указатель-константа на символ:Указатель на символ-константу:Чувствуете разницу ? 
В первом случае указатель вы изменить не сможете, а символ по этому указателю сможете.
Во втором случает указатель сможете модифицировать, но символ по нему уже нет.
"++" после переменной означает, что сначала с ней произойдут некие действия, а потом уже будет инкремент. В данном случае, сначала в функцию передастся символ по указателю, затем произойдёт инкремент указателя (для следующего символа). И так, пока указатель не будет указывать на нулевой символ.
Указатель-константа на символ:
Код: Выделить всё
char* const ch;Код: Выделить всё
const char* ch;В первом случае указатель вы изменить не сможете, а символ по этому указателю сможете.
Во втором случает указатель сможете модифицировать, но символ по нему уже нет.
- Сообщения: 353
- Зарегистрирован: Вс янв 19, 2014 22:41:55
*str - это означает лишь разыменование указателя - а будет туда что-то писаться или наоборот, это значение куда-то присвоится - зависит от того куда вы в выражении это поставите - "слева" - будет попытка присваивания нового значения по указываемому адресу, "справа" - чтение значения по адресу. В данном примере очень важно уяснить следуюший момент (по крайней мере для стандартного соглашения о вызвах функций в С), что переменная str - это автоматическая переменная - копия той переменной которую вы передали в функцию - она живёт на стеке функции и уничтожается при выходе из функции. В вашем примере вы непосредственно инкрементируете указатель на начало строки меняя содержимое переменной str - т.е. экономите одну временую переменную-указатель на стеке функции. Это допустимо хотя и не приветствуется - поскольку str после исполнения цикла while указывает на конец строки - и когда парень, который будет после вас сопровождать и дорабатывать этот код, попытается ещё раз использовать str будучи наивно-уверенным что в str всё ещё есть исходная строчка - получит дополнительный опыт минус время жизни проведённое в отладчике.baron_P писал(а):При это запись *str означает, что в константу str запишется адрес первого элемента строки.Код: Выделить всё
//Функция вывода строки void lcd_puts(const char *str) { while(*str) //пока не кончится строка, для каждого элемента { //вызывается функция записи символа lcd_write(*str++); //вывод символа на шину данных; } }
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! 
Немного не соглашусь и уточню.
Парень, который будет после сопровождать и дорабатывать код, получит проблемы, только если будет пытаться использовать str там же, внутри функции lcd_puts(), что вряд ли. А вот снаружи ничего страшного не произойдёт, потому что str - это копия указателя, как Вы сами и сказали. И внешний указатель никуда не денется, и тот же lcd_puts() можно будет вызывать с одним и тем же аргументом много раз подряд.
Парень, который будет после сопровождать и дорабатывать код, получит проблемы, только если будет пытаться использовать str там же, внутри функции lcd_puts(), что вряд ли. А вот снаружи ничего страшного не произойдёт, потому что str - это копия указателя, как Вы сами и сказали. И внешний указатель никуда не денется, и тот же lcd_puts() можно будет вызывать с одним и тем же аргументом много раз подряд.
- Сообщения: 353
- Зарегистрирован: Вс янв 19, 2014 22:41:55
Да, спасибо, уточнение принято. Нахожу его полезным в случае если всё предыдущее изложение о физическом смысле параметра функции как локальной копии всё-же не было осознано адресатом.WiseLord писал(а):Немного не соглашусь и уточню.
Парень, который будет после сопровождать и дорабатывать код, получит проблемы, только если будет пытаться использовать str там же, внутри функции lcd_puts(), что вряд ли.
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! 
Парень, который полезет править код этой функции, не разобравшись или по неопытности наживший от этого проблемы, будет сам виноват в этом.
Спасибо.
Т.е. lcd_write(*str++) означает, что *str (символ-константа) отправится на обрабоку в функцию, а str (переменная-адрес) инкрементируется и идет дальше на проверку условия перед след. итераций цикла while. Тогда еще глупые вопросы: почему в функцию отправляется не конечный результат выражения *str++, а промежуточный (ведь все выражение стоит в качестве аргумента)?
const char *str - переменная-указатель на символ-константу
const *char str - указатель-константа на символ переменную
Логику в этих выражениях не уловил, постараюсь просто запомнить.
О состоянии str сложно написано, не понял в чем возможны проблемы с этим выражением. Я передаю *str в качестве аргумента функции lcd_write. Что она там с ним делает не важно - после выхода из функции эта переменная перестает существовать, ведь локальная. Внутри функции lcd_puts указатель str в явном виде инкрементирует и почему кто-то (кроме меня, еще не вкурившего возню с указателями) должен подумать, что там останется старое значение, непонятно. А вне функции lcd_puts переменная опять же бессмыслена, т.к. локальная.
Но еще один вопрос вызывает использование класса static. Он тут применен для фукнции которая ничего не возвращает (void же), зачем это сделано?
Т.е. lcd_write(*str++) означает, что *str (символ-константа) отправится на обрабоку в функцию, а str (переменная-адрес) инкрементируется и идет дальше на проверку условия перед след. итераций цикла while. Тогда еще глупые вопросы: почему в функцию отправляется не конечный результат выражения *str++, а промежуточный (ведь все выражение стоит в качестве аргумента)?
const char *str - переменная-указатель на символ-константу
const *char str - указатель-константа на символ переменную
Логику в этих выражениях не уловил, постараюсь просто запомнить.
О состоянии str сложно написано, не понял в чем возможны проблемы с этим выражением. Я передаю *str в качестве аргумента функции lcd_write. Что она там с ним делает не важно - после выхода из функции эта переменная перестает существовать, ведь локальная. Внутри функции lcd_puts указатель str в явном виде инкрементирует и почему кто-то (кроме меня, еще не вкурившего возню с указателями) должен подумать, что там останется старое значение, непонятно. А вне функции lcd_puts переменная опять же бессмыслена, т.к. локальная.
Но еще один вопрос вызывает использование класса static. Он тут применен для фукнции которая ничего не возвращает (void же), зачем это сделано?
We do what we must because we can (c) GLaDOS
Есть понятие преинкремента и постинкремента. Например:baron_P писал(а):еще глупые вопросы: почему в функцию отправляется не конечный результат выражения *str++, а промежуточный (ведь все выражение стоит в качестве аргумента)?
Код: Выделить всё
char ch = 'a';
lcd_write(ch++);
lcd_write(ch);Код: Выделить всё
char ch = 'a';
lcd_write(++ch);
lcd_write(ch);При постинкременте для вычисления всего выражения используется старое значение переменной, и только потом переменная инкрементируется.
При преинкременте - сначала вычисляется значение переменной, а уж потом оно используется для других вычислений.
Так и здесь - при lcd_write(*str++) в функцию отправится число, находящееся по старому адресу, и лишь потом адрес изменяется.
baron_P
В функцию и отправляется только вычисленный результат выражения, а не набор каких-то непонятных операторов. Это происходит на этапе компиляции проекта.
К static функциям обращаться можно внутри одного файла-модуля C. Внешне они недоступны.
В функцию и отправляется только вычисленный результат выражения, а не набор каких-то непонятных операторов. Это происходит на этапе компиляции проекта.
К static функциям обращаться можно внутри одного файла-модуля C. Внешне они недоступны.
- Сообщения: 353
- Зарегистрирован: Вс янв 19, 2014 22:41:55
Результат выражения и есть символ по исходному указателю. Инкремент-же указателя - можете считать "побочным" эффектом.baron_P писал(а):Тогда еще глупые вопросы: почему в функцию отправляется не конечный результат выражения *str++, а промежуточный (ведь все выражение стоит в качестве аргумента)?
Окровенно говоря тип "*char" встречаю впервые - хотелось бы развёрнутый пример применения. Подозреваю имелось ввиду char* const str - т.е. указатель-константа - который нельзя изменить в отличие от переменной-указателя на константу менять который можно.baron_P писал(а):const *char 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 в явном виде инкрементирует и почему кто-то (кроме меня, еще не вкурившего возню с указателями) должен подумать, что там останется старое значение, непонятно.
Класс static для глобальных объектов - это гарантия того, что нигде кроме как в этом модуле этот объект не используется - в общем такая-же "техника безопасности".baron_P писал(а):Но еще один вопрос вызывает использование класса static. Он тут применен для фукнции которая ничего не возвращает (void же), зачем это сделано?
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! 
Спасибо.
Не сразу дошло, что *str++ и *(str+1) не одно и то же.
По static-функциям вроде понятно.
А с костантами-указателями путаница.
const char *str;
const char* str;
Это одно и тоже или нет? К чему тут применяется разыменование указателя?
Я правильно понял, что здесь str1 - это адрес первого элемента строки и *srt1 == 'c'?
Т.е. *str1 это константа, а str1 - переменная, за счет изменения которой я и могу считать несколько констант, составляющих строку?
А здесь костанты две - cstr и *cstr. Т.е. я не могу ни изменить записанный в cstr адрес, ни поменять записанный в *cstr символ. Так?
Не сразу дошло, что *str++ и *(str+1) не одно и то же.
По static-функциям вроде понятно.
А с костантами-указателями путаница.
const char *str;
const char* str;
Это одно и тоже или нет? К чему тут применяется разыменование указателя?
Код: Выделить всё
const char* str1 = "const char* str1\n";Т.е. *str1 это константа, а str1 - переменная, за счет изменения которой я и могу считать несколько констант, составляющих строку?
Код: Выделить всё
char* const cstr = "const char* cstr\n";We do what we must because we can (c) GLaDOS
Это одно и то же. Первая запись более правильная, так как указывает, что str является указателем.baron_P писал(а): const char *str;
const char* str;
Это одно и тоже или нет? К чему тут применяется разыменование указателя?
Как пример, показывающий разницу в использовании:
const char* str1, str2;
const char *str1, str2;
в первом примере не видно, что str2 не указатель, а символ.
здесь есть строка без имени "const char* cstr\n", расположенная где-то в памяти, и константный указатель на неё cstr. Строка может располагаться в ОЗУ, тогда её можно изменить. Явно это не указывается, зависит от контекста.baron_P писал(а):Я правильно понял, что здесь str1 - это адрес первого элемента строки и *srt1 == 'c'?Код: Выделить всё
const char* str1 = "const char* str1\n";
Т.е. *str1 это константа, а str1 - переменная, за счет изменения которой я и могу считать несколько констант, составляющих строку?
А здесь костанты две - cstr и *cstr. Т.е. я не могу ни изменить записанный в cstr адрес, ни поменять записанный в *cstr символ. Так?Код: Выделить всё
char* const cstr = "const char* cstr\n";
- Сообщения: 353
- Зарегистрирован: Вс янв 19, 2014 22:41:55
Это лишь разные стили объявления одного и того-же типа переменной - указателя на неизменяемые данные. И как водится по поводу положения этой звёздочки идут бесконечные битвы остроконечников с тупоконечниками - одни говорят, что звёздочка принадлежит типу и кошернее писать "char*" а другие моментально двигают им под нос вот такой код:baron_P писал(а):А с костантами-указателями путаница.
const char *str;
const char* str;
Это одно и тоже или нет? К чему тут применяется разыменование указателя?
Код: Выделить всё
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* const cstr = "const char* cstr\n";
Код: Выделить всё
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
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! 
Спасибо, вроде понял, но ваше дополнение вызвало к жизни еще пару вопросов.
А почему работает? cstr и str1 это ведть не массивы и не строки, а только только одно число (адрес первого сивола соотв. строки). Разве можно к не заданному явно массиву обращаться по индексам?
Я правильно понял, что тут, если б cstr не было константой, в него б записался адрес первого символа строки?
Код: Выделить всё
for (int i = 0; i < 9; i++)
cstr[i] = str1[i]; // ага.. работает!Код: Выделить всё
cstr = "just a char*"We do what we must because we can (c) GLaDOS


