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

Если ваш вопрос не влез ни в одну из вышеперечисленных тем, вам сюда.
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18647
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

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

Сообщение ARV »

все понятно, если внимательно изучить возможности компилятора и стандарт языка.
как правило, под любое число выделяется int, и одновременно, практически каждый компилятор имеет опциональную возможность выделять под enum минимально возможный элемент, т.е. байт (если, конечно, перечисление содержит не более 256 элементов или не содержит элементов с явно заданными большими значениями).

для чего это нужно? снова понятно. язык Си слабо контролирует типы, стандарт С99 немного ужесточает контроль типов, но более-менее нормальный контроль есть только в С++. это означает, что когда в качестве параметра функции вы задаете тип-перечисление, компилятор может на этапе компиляции сообщить (обычно warning, но в С++ может быть и error), когда вы в функцию передаете число, не попадающее в перечисление - это немного помогает избежать грубых ошибок.

например, в WinAVR попробуйте скомпилировать оператор switch с аргументом перечисляемого типа - если вариантов case будет меньше, чем определено в перечислении, компилятор вам тут же сообщит, что не все варианты switch обработаны. для обычных типов чисел так он ругается только на отсутствие default.

но даже при полном отсутствии контроля типов в обычном Си человеческий фактор нельзя сбрасывать со счетов: абстрактный char практически ничего не скажет человеку-программисту, в то время как interrupt_mode уже содержит конкретный смысл, а взгляд на спрятанный за этим словом тип-перечисление сразу показывает допустимый диапазон значений.

P.S. я не в курсе, есть ли в каком-либо диалекте Си или С++ возможность определять "края" перечислений, как в Pascal... но это реально полезная фича!
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Реклама
uk8amk
Поставщик валерьянки для Кота
Сообщения: 2222
Зарегистрирован: Вт ноя 27, 2007 11:32:06
Откуда: Tashkent

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

Сообщение uk8amk »

Спасибо за разъяснения, потихоньку начинает проясняться.
Реклама
mastech
Грызет канифоль
Сообщения: 269
Зарегистрирован: Чт мар 11, 2010 17:45:37
Откуда: г.фрязино

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

Сообщение mastech »

ковыряю код принимающий и отправляющий данные по UART(AVR) и выводящий что отправлено/принято на дисплей, задачя убрать все из кода, что отвечает за отправку обратно в компьютер, начинаю с самого явного убираю из цикла ифы с кнопками и перестает работать вывод на лцд, вступительная заставка работает, стоит вернуть хоть один иф с кнопкой, начинает работать дисплей.
Спойлер

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

//*** Пример работы с USART интерфейсом микроконтроллеров AVR ***

#include <avr/io.h>
#include <avr/interrupt.h>

#define BAUDRATE 9600 // Скорость обмена данными
#define F_CPU 8000000UL // Рабочая частота контроллера

unsigned char NUM = 0;  
unsigned char count = 0;
unsigned char byte_receive = 0;
unsigned char i = 1;

// Функция задержки в мкс
void _delay_us(unsigned char time_us)
{ register unsigned char i;

for(i = 0; i < time_us; i++)
{
asm volatile(" PUSH  R0 ");
asm volatile(" POP   R0 ");
}
}

// Функция задержки в мс
void _delay_ms(unsigned int time_ms)
{ register unsigned int i;

for(i = 0; i < time_ms; i++)
{ 
_delay_us(250);
_delay_us(250);
_delay_us(250);
_delay_us(250);
}
}

#define RS PD2 
#define EN PD3

// Функция передачи команды
void lcd_com(unsigned char p)
{
PORTD &= ~(1 << RS); // RS = 0 (запись команд)
PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p & 0xF0); // старший нибл
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p << 4); // младший нибл
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
}

// Функция передачи данных
void lcd_data(unsigned char p)
{
PORTD |= (1 << RS)|(1 << EN); // RS = 1 (запись данных), EN - 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p & 0xF0); // старший нибл
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p << 4); // младший нибл
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
}

// Функция инициализации LCD
void lcd_init(void)
{
_delay_ms(50); // Ожидание готовности ЖК-модуля

// Конфигурирование четырехразрядного режима
PORTD |= (1 << PD5);
PORTD &= ~(1 << PD4);

// Активизация четырехразрядного режима
PORTD |= (1 << EN);
PORTD &= ~(1 << EN);
_delay_ms(5); 

lcd_com(0x28); // шина 4 бит, LCD - 2 строки
lcd_com(0x08); // полное выключение дисплея
lcd_com(0x01); // очистка дисплея
_delay_us(100);
lcd_com(0x06); // сдвиг курсора вправо
lcd_com(0x0C); // включение дисплея, курсор не видим
}

// Функция вывода строки на LCD
void lcd_string(unsigned char command, char *string)
{
lcd_com(0x0C);
lcd_com(command);
while(*string != '\0')
{
lcd_data(*string);
string++;
}
}

// Функция передачи данных по USART
void uart_send(char data)
{
while(!( UCSRA & (1 << UDRE)));	// Ожидаем когда очистится буфер передачи
UDR = data; // Помещаем данные в буфер, начинаем передачу
}

// Функция передачи строки по USART
void str_uart_send(char *string)
{
while(*string != '\0')
{
uart_send(*string);
string++;
}
}

// Функция приема данных по USART
int uart_receive(void)
{
while(!(UCSRA & (1 << RXC))); // Ожидаем, когда данные будут получены
return UDR; // Читаем данные из буфера и возвращаем их при выходе из подпрограммы
}

// Функция инициализации USART
void uart_init(void)
{
// Параметры соединения: 8 бит данные, 1 стоповый бит, нет контроля четности
// USART Приемник: Включен
// USART Передатчик: Включен
// USART Режим: Асинхронный
// USART Скорость обмена: 9600

UBRRL = (F_CPU/BAUDRATE/16-1); // Вычисляем скорость обмена данными  
UBRRH = (F_CPU/BAUDRATE/16-1) >> 8;

UCSRB |= (1 << RXCIE)| // Разрешаем прерывание по завершению приема данных
          (1 << RXEN)|(1 << TXEN); // Включаем приемник и передатчик
                                                
UCSRC |= (1 << URSEL)| // Для доступа к регистру UCSRC выставляем бит URSEL
         (1 << UCSZ1)|(1 << UCSZ0); // Размер посылки в кадре 8 бит
}

// Прерывание по окончанию приема данных по USART
ISR(USART_RXC_vect)
{
NUM = UDR; // Принимаем символ по USART
byte_receive = 1;
uart_send(NUM); // Посылаем символ по USART
	
if(NUM == 'a') // Если принят символ "a", включаем светодиод
PORTB |= (1 << PB0);
if(NUM == 'b') // Если принят символ "b", выключаем светодиод
PORTB &= ~(1 << PB0);
}

// Главная функция
int main(void)
{
DDRB |= (1 << PB0); // Светодиод
PORTB = 0x00;        			

DDRC  = 0x00;	
PORTC = 0xFF;
	
DDRD  = 0b11111110;
PORTD = 0x00;
	
lcd_init(); // Инициализация LCD
uart_init(); // Инициализация USART
	
sei(); // Глобально разрешаем прерывания

str_uart_send("Initialization system\r\n"); // Передаем строку по USART
lcd_string(0x80, " AVR USART TEST "); // Выводим строку на LCD
_delay_ms(2500);
lcd_com(0x01); // Очищаем LCD
	
while(1)
{

if((PINC & (1 << PC0)) == 0) // Если нажата кнопка 1
{
while((PINC & (1 << PC0)) == 0){} // Ждем отпускания кнопки 1
str_uart_send("Button 1 TEST\r\n"); // Передаем строку по USART
lcd_string(0x80, "Button 1 TEST"); // Выводим строку на LCD
_delay_ms(1000);
lcd_com(0x01); // Очищаем LCD
}

if((PINC & (1 << PC1)) == 0) // Если нажата кнопка 2
{
while((PINC & (1 << PC1)) == 0){} // Ждем отпускания кнопки 2
str_uart_send("Button 2 TEST\r\n"); // Передаем строку по USART
lcd_string(0x80, "Button 2 TEST"); // Выводим строку на LCD
_delay_ms(1000);
lcd_com(0x01); // Очищаем LCD
}

if((PINC & (1 << PC2)) == 0) // Если нажата кнопка 3
{
while((PINC & (1 << PC2)) == 0){} // Ждем отпускания кнопки 3
str_uart_send("Button 3 TEST\r\n"); // Передаем строку по USART
lcd_string(0x80, "Button 3 TEST"); // Выводим строку на LCD 
_delay_ms(1000);
lcd_com(0x01); // Очищаем LCD
}		

if(byte_receive)
{
byte_receive = 0;
count++;
lcd_data(NUM); // Выводим символ на LCD
if(count > 16) // Если строка заполнена
{
count = 0;
lcd_com(0x01); // Очищаем LCD
}
}
}
}
доб 22:14: странно, удаляя все "кнопки" из цикла, if(byte_receive) перестает работать, добавив задержку в 10мс. все работает, почему.

еще вопрос. как иначе записать if(byte_receive) если он один в цикле получается if ненужен?
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18647
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

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

Сообщение ARV »

а у меня встречный вопрос: почему с задержками изобрели велосипед, если существует отличный модуль для формирования задержек util/delay.h?

на счет кнопок - у вас, как я понял, дребезг не отлавливается?
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Реклама
Эиком - электронные компоненты и радиодетали
uk8amk
Поставщик валерьянки для Кота
Сообщения: 2222
Зарегистрирован: Вт ноя 27, 2007 11:32:06
Откуда: Tashkent

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

Сообщение uk8amk »

Не знаю как правильно сформулировать запрос для гугла, поэтому попробую спросить здесь.
Есть такая штука как автозавершение кода. Допустим мы пишем имя структуры
MyStruct.
И здесь в редакторе вываливается доступный список её элементов, из них можно выбрать нужный.
Есть к примеру машина состояний, принимающая одно из N возможных константных значений(UP, DOWN, START, STOP и т.д.). Тогда мы пишем
state_machine = STATE_START;
Было бы удобнее собрать эти константные значения в какой-то объект SM_VAL и написать так:
state_machine = SM_VAL.[вываливается список, из которого выбираем]

Использую Си(не ++), есть ли такое?
Реклама
Аватара пользователя
Аlex
Модератор
Сообщения: 4614
Зарегистрирован: Чт мар 18, 2010 23:09:57
Откуда: Планета Земля
Контактная информация:

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

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

uk8amk писал(а): Было бы удобнее собрать эти константные значения в какой-то объект
Перечисления (enum).
Реклама
uk8amk
Поставщик валерьянки для Кота
Сообщения: 2222
Зарегистрирован: Вт ноя 27, 2007 11:32:06
Откуда: Tashkent

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

Сообщение uk8amk »

Только у меня не работает автозавершение перечислений?
Аватара пользователя
WiseLord
Друг Кота
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск
Контактная информация:

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

Сообщение WiseLord »

Эти выпадающие списки уже от используемого IDE.

Я, например, QtCreator использую - там такое есть.
Аватара пользователя
Siarzhuk
Потрогал лапой паяльник
Сообщения: 353
Зарегистрирован: Вс янв 19, 2014 22:41:55

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

Сообщение Siarzhuk »

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

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

Сообщение uk8amk »

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

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

Сообщение 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! ;-)
Аватара пользователя
FeCat
Прорезались зубы
Сообщения: 211
Зарегистрирован: Пт окт 21, 2011 14:39:31

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

Сообщение FeCat »

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

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

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

Сообщение WiseLord »

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

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

Сообщение 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! ;-)
Аватара пользователя
baron_P
Нашел транзистор. Понюхал.
Сообщения: 183
Зарегистрирован: Вт сен 14, 2010 23:07:10
Откуда: Ростов

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

Сообщение 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
Аватара пользователя
Аlex
Модератор
Сообщения: 4614
Зарегистрирован: Чт мар 18, 2010 23:09:57
Откуда: Планета Земля
Контактная информация:

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

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

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

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

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

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

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

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

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

Сообщение 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! ;-)
Аватара пользователя
WiseLord
Друг Кота
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск
Контактная информация:

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

Сообщение WiseLord »

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

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

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

Сообщение Siarzhuk »

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

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

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

Парень, который полезет править код этой функции, не разобравшись или по неопытности наживший от этого проблемы, будет сам виноват в этом.
Ответить

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