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

Если ваш вопрос не влез ни в одну из вышеперечисленных тем, вам сюда.
Ответить
Потрогал лапой паяльник
Аватара пользователя
Сообщения: 353
Зарегистрирован: Вс янв 19, 2014 22:41:55

Сообщение Siarzhuk »

uk8amk писал(а):Только у меня не работает автозавершение перечислений?
Что за редактор используете?
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! ;-)
Реклама
Поставщик валерьянки для Кота
Сообщения: 2222
Зарегистрирован: Вт ноя 27, 2007 11:32:06
Откуда: Tashkent

Сообщение uk8amk »

Keil mdk 4.73, иногда Codevision.
Похоже ограничение самих IDE. В принципе можно прикрутить и внешний редактор, но пока и так сойдёт.
Реклама
Потрогал лапой паяльник
Аватара пользователя
Сообщения: 353
Зарегистрирован: Вс янв 19, 2014 22:41:55

Сообщение Siarzhuk »

uk8amk писал(а):Keil mdk 4.73, иногда Codevision.
В uVision http://www.keil.com/support/man/docs/uv ... decomp.htm попробуйте Ctrl-<пробел> после набора первых символов.
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! ;-)
Прорезались зубы
Аватара пользователя
Сообщения: 211
Зарегистрирован: Пт окт 21, 2011 14:39:31

Сообщение FeCat »

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

(void)0;
Что это такое? Приведение ноля к типу void? Не понимаю. Что это с точки зрения стандарта, что делает компилятор?
Реклама
Эиком - электронные компоненты и радиодетали
Друг Кота
Аватара пользователя
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск

Сообщение WiseLord »

Это где такое встретилось? А то что-то у меня чувство, что в C/С++ это бессмысленно. Вот (void*)0 - другое дело.
Контактная информация:
Реклама
Потрогал лапой паяльник
Аватара пользователя
Сообщения: 353
Зарегистрирован: Вс янв 19, 2014 22:41:55

Сообщение Siarzhuk »

FeCat писал(а):

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

(void)0;
Что это такое? Приведение ноля к типу void? Не понимаю. Что это с точки зрения стандарта, что делает компилятор?
Увы, определённо можно лишь сказать , что это строка вырванная [вами] из контекста - потому и непонятная. Если это часть макроса во не-отладочной конфигурации - что встречается повсеместно - то судя по всему упреждает предупреждения компилятора типа "неэффективная операция". Что-то типа nop() но без всякого представления в скомпилированном объекте.
На эту тему ещё нагугливается следующее:
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)
Кроме того приведение к void предотвращает от использования макро в операциях присваивания - своего рода эмуляция функции не возвращающей ничего.
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! ;-)
Реклама
Нашел транзистор. Понюхал.
Аватара пользователя
Сообщения: 183
Зарегистрирован: Вт сен 14, 2010 23:07:10
Откуда: Ростов

Сообщение baron_P »

Доброго времени суток. Поскажите ответы на пару-тройку глупых вопросов. Железка ATMega16, пишется под AVR-GCC. Программа работает, но хочется понять как именно.

Есть пара функций:

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

//Функция записи байта в контроллер
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++);  //вывод символа на шину данных;
  }
}
И что-то я тут в двух соснах запутался. По первой все понятно: она выводит число на PORTB, формирует импульс разрешения чтения этих данных контроллером LCD-дисплея и формирует задержку, чтоб тот успел все переварить перед следющими командами. А вот со второй не совсем понятно.
Функция получает некоторую строку в таком виде: 'test'. При это запись *str означает, что в константу str запишется адрес первого элемента строки. Цикл while работает до тех пор, пока записанное по адресу str значение не равно нулю. И тут первый вопрос: оно же константа, цикл должен должен быть бесконечным, как может меняться значение *str? Дальше больше. Фукция записи получает значение находящееся по адресу str (т.е. первый элемент строки). После этого происходит инкремент. По логике, это должен быть инкремент адреса. Т.е. сперва над str происходит операция ++, потом *. Тут тоже вопрос: функция в какой момент времени получает свой аргумент? В нее улетит первый и второй элемент строки? Но, допустим, функция свое отработала, возвращаемся к циклу while. Куда сохраниться значение инкремента, которое должно проверятся в условии цикла, *str ведь константа?
Ужас какой-то с этими указателями, ничего не понятно.
We do what we must because we can (c) GLaDOS
Модератор
Аватара пользователя
Сообщения: 4618
Зарегистрирован: Чт мар 18, 2010 23:09:57
Откуда: Планета Земля

Сообщение Аlex »

str - не константа ! Это указатель на символ-константу.
"++" после переменной означает, что сначала с ней произойдут некие действия, а потом уже будет инкремент. В данном случае, сначала в функцию передастся символ по указателю, затем произойдёт инкремент указателя (для следующего символа). И так, пока указатель не будет указывать на нулевой символ.

Указатель-константа на символ:

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

char* const ch;
Указатель на символ-константу:

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

const char* ch;
Чувствуете разницу ? :)
В первом случае указатель вы изменить не сможете, а символ по этому указателю сможете.
Во втором случает указатель сможете модифицировать, но символ по нему уже нет.
Потрогал лапой паяльник
Аватара пользователя
Сообщения: 353
Зарегистрирован: Вс янв 19, 2014 22:41:55

Сообщение Siarzhuk »

baron_P писал(а):

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

//Функция вывода строки
void lcd_puts(const char *str)
{
  while(*str)  //пока не кончится строка, для каждого элемента
  {  //вызывается функция записи символа
    lcd_write(*str++);  //вывод символа на шину данных;
  }
}
При это запись *str означает, что в константу str запишется адрес первого элемента строки.
*str - это означает лишь разыменование указателя - а будет туда что-то писаться или наоборот, это значение куда-то присвоится - зависит от того куда вы в выражении это поставите - "слева" - будет попытка присваивания нового значения по указываемому адресу, "справа" - чтение значения по адресу. В данном примере очень важно уяснить следуюший момент (по крайней мере для стандартного соглашения о вызвах функций в С), что переменная str - это автоматическая переменная - копия той переменной которую вы передали в функцию - она живёт на стеке функции и уничтожается при выходе из функции. В вашем примере вы непосредственно инкрементируете указатель на начало строки меняя содержимое переменной str - т.е. экономите одну временую переменную-указатель на стеке функции. Это допустимо хотя и не приветствуется - поскольку str после исполнения цикла while указывает на конец строки - и когда парень, который будет после вас сопровождать и дорабатывать этот код, попытается ещё раз использовать str будучи наивно-уверенным что в str всё ещё есть исходная строчка - получит дополнительный опыт минус время жизни проведённое в отладчике.
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! ;-)
Друг Кота
Аватара пользователя
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск

Сообщение WiseLord »

Немного не соглашусь и уточню.

Парень, который будет после сопровождать и дорабатывать код, получит проблемы, только если будет пытаться использовать str там же, внутри функции lcd_puts(), что вряд ли. А вот снаружи ничего страшного не произойдёт, потому что str - это копия указателя, как Вы сами и сказали. И внешний указатель никуда не денется, и тот же lcd_puts() можно будет вызывать с одним и тем же аргументом много раз подряд.
Контактная информация:
Потрогал лапой паяльник
Аватара пользователя
Сообщения: 353
Зарегистрирован: Вс янв 19, 2014 22:41:55

Сообщение Siarzhuk »

WiseLord писал(а):Немного не соглашусь и уточню.
Парень, который будет после сопровождать и дорабатывать код, получит проблемы, только если будет пытаться использовать str там же, внутри функции lcd_puts(), что вряд ли.
Да, спасибо, уточнение принято. Нахожу его полезным в случае если всё предыдущее изложение о физическом смысле параметра функции как локальной копии всё-же не было осознано адресатом. ;-)
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! ;-)
Модератор
Аватара пользователя
Сообщения: 4618
Зарегистрирован: Чт мар 18, 2010 23:09:57
Откуда: Планета Земля

Сообщение Аlex »

Парень, который полезет править код этой функции, не разобравшись или по неопытности наживший от этого проблемы, будет сам виноват в этом.
Нашел транзистор. Понюхал.
Аватара пользователя
Сообщения: 183
Зарегистрирован: Вт сен 14, 2010 23:07:10
Откуда: Ростов

Сообщение 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 же), зачем это сделано?
We do what we must because we can (c) GLaDOS
Друг Кота
Аватара пользователя
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск

Сообщение 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++) в функцию отправится число, находящееся по старому адресу, и лишь потом адрес изменяется.
Контактная информация:
Поставщик валерьянки для Кота
Сообщения: 2222
Зарегистрирован: Вт ноя 27, 2007 11:32:06
Откуда: Tashkent

Сообщение uk8amk »

baron_P
В функцию и отправляется только вычисленный результат выражения, а не набор каких-то непонятных операторов. Это происходит на этапе компиляции проекта.

К static функциям обращаться можно внутри одного файла-модуля C. Внешне они недоступны.
Потрогал лапой паяльник
Аватара пользователя
Сообщения: 353
Зарегистрирован: Вс янв 19, 2014 22:41:55

Сообщение 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 для глобальных объектов - это гарантия того, что нигде кроме как в этом модуле этот объект не используется - в общем такая-же "техника безопасности".
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! ;-)
Нашел транзистор. Понюхал.
Аватара пользователя
Сообщения: 183
Зарегистрирован: Вт сен 14, 2010 23:07:10
Откуда: Ростов

Сообщение 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 символ. Так?
We do what we must because we can (c) GLaDOS
Держит паяльник хвостом
Аватара пользователя
Сообщения: 960
Зарегистрирован: Вс дек 02, 2012 16:58:33
Откуда: от туда

Сообщение 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. Строка может располагаться в ОЗУ, тогда её можно изменить. Явно это не указывается, зависит от контекста.
Контактная информация:
Потрогал лапой паяльник
Аватара пользователя
Сообщения: 353
Зарегистрирован: Вс янв 19, 2014 22:41:55

Сообщение 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. :-)
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! ;-)
Нашел транзистор. Понюхал.
Аватара пользователя
Сообщения: 183
Зарегистрирован: Вт сен 14, 2010 23:07:10
Откуда: Ростов

Сообщение baron_P »

Спасибо, вроде понял, но ваше дополнение вызвало к жизни еще пару вопросов.

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

for (int i = 0; i < 9; i++)
    cstr[i] = str1[i]; // ага.. работает!
А почему работает? cstr и str1 это ведть не массивы и не строки, а только только одно число (адрес первого сивола соотв. строки). Разве можно к не заданному явно массиву обращаться по индексам?

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

cstr = "just a char*"
Я правильно понял, что тут, если б cstr не было константой, в него б записался адрес первого символа строки?
We do what we must because we can (c) GLaDOS
Ответить

Вернуться в «Разные вопросы по МК»