Visual Basic, .NET, ASP, VBScript
 

   
   
     

Форум - Олимпиады

Страница: 1 | 2 | 3 | 4 | 5 |

 

  Вопрос: VC++ Help Please ! (задачка) Добавлено: 08.11.05 23:06  

Автор вопроса:  HACKER

Ответить

  Ответы Всего ответов: 67  

Номер ответа: 16
Автор ответа:
 HACKER


 

Разработчик Offline Client

Вопросов: 236
Ответов: 8362
 Профиль | | #16 Добавлено: 09.11.05 22:51
RESPECT HOOLIGAN!
Большое спасибо!!!

Ответить

Номер ответа: 17
Автор ответа:
 HOOLIGAN



Вопросов: 0
Ответов: 1066
 Профиль | | #17 Добавлено: 09.11.05 23:17
Рано радуешься :)))
Там ещё валом что делать надо.
Добавил конвертацию в ДОС-кодировку, чтобы кириллица из исходника отображалась нормально, обработку пробелов в ФИО (пришлось разбить на три отдельных ввода), кое-какое форматирование добавил, чтобы в табличном виде было красиво, цвет шрифта... Короче изменений много, поэтому чтобы не запутался, снова выложу весь код:


    #pragma comment(lib, "user32.lib";)
    #pragma comment(lib, "kernel32.lib";)
    #pragma comment(lib, "shell32.lib";)
    
    #include <windows.h>

    #include <iostream>


    using namespace std;
    
//============= constants =======================================
    #define         STUDENT_COUNT 8     /* константа - кол-во элементов в массиве */
      
//============= structures ======================================
typedef struct STUDENT{
    char            name[128];
    int             group_ID;
    int             mark[5];
}STUDENT, *pSTUDENT;


    
//============= variables =======================================
    STUDENT*        array;              /* указатель на память, где сидит массив */
    int             sort_field_id;      /* переменная, указывающая по какому полю сортировать */
    


//===============================================================
static char* to_oem ( char* lpString )  /* перевод в дос-кодировку для отображения кириллицы*/
{
    char    buffer[512];
    CharToOem ( lpString, buffer );
    return ( buffer );
}

//===============================================================
int compare (STUDENT* index1, STUDENT* index2)  /* сравнение двух элементов */
{
    int             result=0;
    int             m1=0, m2=0;
    
    if ( sort_field_id==0 )
        result = _stricoll( index1->name, index2->name ); /*_stricoll - ф-ция сравнения строк*/

    else if ( sort_field_id==1 && index1->group_ID > index2->group_ID )
        result = 1;
    
    else if ( sort_field_id==2 )
    {
        /* считаем сумму оценок двух элементов и сравниваем их */
        for ( int i=0; i<5; i++ )
        {
            m1 += index1->mark[i];
            m2 += index2->mark[i];
        }
        if ( m1 > m2 ) result = 1;
    }
    /* если index1 > index2, возвращаем 1 (будем переставлять) */
    return result;

}

//==========================================================
void swap_index(STUDENT* index1, STUDENT* index2)   /* перестановка двух элементов */
{
    STUDENT     item;
    memcpy (&item, index1, sizeof(STUDENT));
    memcpy (index1,index2, sizeof(STUDENT));
    memcpy (index2, &item, sizeof(STUDENT));
}

//==========================================================
void sort( STUDENT* pArray, int count )     /* функция сортировки */
{
    int     swap;
    
    do
    {
        swap = 0;   /* флаг обмена */
        /* просмотр массива со сравнением соседних элементов */
        for ( int i=0; i<count-1; i++ )
        {
            if ( compare(&pArray[i], &pArray[i+1];) > 0 )
            {
                /*если элемент с меньшим индексом оказался больше,
                  переставляем их местами и взводим флаг */
                swap_index(&pArray[i], &pArray[i+1];);
                swap = 1;
            }
        }
    }
    /* повторяем цикл do - while пока были перестановки */
    while ( swap );
    
}

//==========================================================
static void fill_array( STUDENT* pArray, int count )
{
    char    buffer[128];
    int     pos;
    
    /* заполнение массива */
    for ( int i=0; i<count; i++ )
    {
        system("cls";);
        printf ( to_oem("\n========= Студент № %lu ==========\n\n";), i+1 );
        printf ( to_oem("Введите фамилию студента: ";) );
        cin >> pArray[i].name;
        pArray[i].name[ (pos = strlen (pArray[i].name)) ]=' ';
        pos++;
        printf ( to_oem("Введите имя студента: ";) );
        cin >> & pArray[i].name[pos++];
        pArray[i].name[pos++] = '.';
        printf ( to_oem("Введите отчество студента: ";) );
        cin >> & pArray[i].name[pos++];
        pArray[i].name[pos++] = '.';
        pArray[i].name[pos] = 0;
        printf ( to_oem("Введите номер группы: ";) );
        cin >> buffer;
        pArray[i].group_ID = atoi(buffer);  /* atoi - перевод строки в число int */
        printf ( to_oem("Введите 5 оценок, разделяя  запятыми /например: 1,5,3,4,2/: ";) );
        cin >> buffer;
        /* нечетные символы отметок преобразуем в цифры и в массив отметок */
        for ( int j=1; j<6; j++ )
            pArray[i].mark[j-1] = buffer[;(j-1)*2]-0x30;
    }

}

//==========================================================
static void pad_string ( char* string, int len )    /* дополнение строки пробелами до длины len */
{
    for ( int i = strlen(string); i<len; i++ )
        string[i]=' ';
    string[i]='|';
    string[i+1]=' ';
    string[i+2]=0;
}
        
//==========================================================
static void show_array( STUDENT* pArray, int count )    
{
    char    buffer[128];
    char    small_buffer[64];
    float   average;
    
    /* показываем массив со всеми полями + среднюю оценку */
    system ("cls";);
    
    printf (       "\n    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n";);
    printf ( to_oem ("    |  Ф И О студента         | Номер группы  |   Оценки   | Средний балл |\n";) );
    printf (         "    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n";);
    
    
    for ( int i=0; i<count; i++ )
    {
        average = 0;
        sprintf ( buffer, "    | %s", pArray[i].name );
        pad_string(buffer,30);

        sprintf (small_buffer, " %lu", pArray[i].group_ID );
        strcat  ;(buffer, small_buffer);
        pad_string(buffer,46);

        for ( int j=0; j<5; j++ )
        {
            sprintf (small_buffer, "%lu ", pArray[i].mark[j] );
            strcat  ;(buffer, small_buffer);
            average += pArray[i].mark[j];
        }
        pad_string(buffer,59);
        
        sprintf ( small_buffer, " %f", average/5 );
        strcat  ;(buffer, small_buffer);
        pad_string(buffer,74);
        
        printf  ;("%s\n", buffer );
    }
    
    printf (         "    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n";);
    
}

//==========================================================
char get_action()           /* получение выбора действия от юзера*/
{
    char    buffer[128];
    
    printf ( to_oem("\n\t\t===========================================\n";) );
    printf ( to_oem("\t\t=    Выберите действие и нажмите ENTER    =\n";) );
    printf ( to_oem("\t\t=       сортировать по имени  - 0         =\n";) );
    printf ( to_oem("\t\t=       сортировать по группе - 1         =\n";) );
    printf ( to_oem("\t\t=   сортировать по успеваемости  - 2      =\n";) );
    printf ( to_oem("\t\t=        выйти из программы  - 3          =\n";) );
    printf ( to_oem("\t\t===========================================\n";) );
    printf ( to_oem("\t\t\t\tВыбор : ";) );
    
    /* запуск юзера по кольцу, пока не сделает выбор от 0 до 3*/
    while ( buffer[0]<'0' || buffer[0]>'3' )
        cin >> buffer;
    /* возвращаем выбор юзера */
    return ( buffer[0]-0x30 );
}

//==========================================================
int main()
{
    HANDLE      hStdout;
    
    hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hStdout, FOREGROUND_BLUE|
                                     FOREGROUND_GREEN|
                                     FOREGROUND_INTENSITY);

    /* создаём, очищаем, заполняем и показываем массив*/
    array = new STUDENT[STUDENT_COUNT];    
    memset ( array, 0, STUDENT_COUNT*sizeof(STUDENT) );
    fill_array ( array, STUDENT_COUNT );
    show_array ( array, STUDENT_COUNT );
    
    
    /* пока юзер не нажмет 3, будем сортировать и показывать результат */
    while ( ( sort_field_id = get_action() ) != 3 )
    {
        sort ( array, STUDENT_COUNT );
        show_array ( array, STUDENT_COUNT );
    }
    
    /* уничтожаем массив, освобождая память*/
    delete[] array;
    
    /* выход из программы*/
    return 0;
}


Напильник в руки, барабан на шею...

Ответить

Номер ответа: 18
Автор ответа:
 HACKER


 

Разработчик Offline Client

Вопросов: 236
Ответов: 8362
 Профиль | | #18 Добавлено: 10.11.05 06:43
прокоментируй ещё плиз swap_index, чё то я в эту memcpy неврублюсь...

обработку пробелов в ФИО (пришлось разбить на три отдельных ввода

всмысле? а до этого он чё с пробелами делал?

И ещё... Нельзя ли в консольном режиме использовать графику? Ну типа как в QB... SCREEN 12: LINE (0,0)-(100,100) итп... ???

Ответить

Номер ответа: 19
Автор ответа:
 HACKER


 

Разработчик Offline Client

Вопросов: 236
Ответов: 8362
 Профиль | | #19 Добавлено: 10.11.05 06:48
А, и что означает static ?
static char* to_oem (...)
static void show_array(

итп?

Во, и ещё...
    #define         STUDENT_COUNT 8     /* константа - кол-во элементов в массиве */

Но если делать НЕ константой, походу
array = new STUDENT[STUDENT_COUNT];    
матюкатся начнёт? там надо типа как в массиве динамически память вывести, или чё?

Ответить

Номер ответа: 20
Автор ответа:
 HOOLIGAN



Вопросов: 0
Ответов: 1066
 Профиль | | #20 Добавлено: 10.11.05 07:34
Короче надо поменять два элемента массива. swap_index для этого получает два параметра - index1 и index2. Это указатели на куски памяти, находящиеся в пределах памяти, отведенной под массив, и соответствующие началам двух элементов. Запись STUDENT* index1 означает, что это указатель на память, организованную в соответствии с описанием структуры STUDENT. Т.е. index - указатель на элемент массива.
//==========================================================
void swap_index(STUDENT* index1, STUDENT* index2)   /* перестановка двух элементов */
{
    STUDENT     item;   /* временная локальная переменная типа STUDENT */

    /* memcpy - си-функция для копирования одного участка памяти в другой
       наподобие CopyMemory. 1-й параметр - адрес куда копировать, 2-й -адрес
       откуда копировать, 3-й -сколько байт копировать.
       1-й адрес (куда) - адрес переменной item. Сама переменная -item,
       а её адрес указывается со знаком & - &item
       2-й адрес у нас уже готовый есть - это указатель,переданный как параметр
       функции - index1.
       Количество байт для копирования указывается sizeof(STUDENT) - т.е.
       размер структуры STUDENT. И копируем элемент массива во временную переменную
       item на стеке.*/
    memcpy (&item, index1, sizeof(STUDENT));

    /* теперь на то место в массиве, где был элемент index1, копируем элемент index2
       точно таким же способом */
    memcpy (index1,index2, sizeof(STUDENT));

    /* и в заключение на место элемента index2 в массиве копируем старый элемент
       index1, который был спрятан во временной переменной. Так поменялись местами
       два куска памяти, соответствующие элементам index1 и index2 */
    memcpy (index2, &item, sizeof(STUDENT));
}
С получением пробелов из консоли есть заморочки. Символ пробела воспринимается не как любой другой символ, а как некий специальный символ, и он принудительно прерывает поток символов, вводимых с клавы. Если набрать "Иванов А А" то функция cin считает в буфер только "Иванов". Но это не самое страшное :) Те два пробела перед буквами А будут восприняты как два ответа юзера на последующие в очереди вопросы. Т.е. информацию о номере группы и оценках ты уже не получишь, сами вопросы выведутся на экран, но программа не будет ждать, пока юзер наберет ответы на них, и проскочит дальше. Как с этим явлением бороться я не знаю, в инете ничего не видел, поэтому сделал несколько иначе: Запросил отдельно фамилию, затем сам прицепил пробел и сохранил в буфере, затем запросил имя, взял из него первую букву и прицепил к фамилии как инициал, и запросил третьим запросом отчество, с которым также поступил, как с именем - взял первую букву и прицепил к буферу. Получатся Иванов А.А.

С графикой вроде где-то видел некое подобие, но не вспомню. Это надо рыть в сторону установки аттрибутов буфера вывода.
Нашел один баг :) В процедуре get_action() замени строку
while ( buffer[0]<'0' || buffer[0]>'3' )
на
while ( buffer[0]<'0' || buffer[0]>'3' || buffer[0]==(sort_field_id+0x30) )
и объявление глобальной переменной sort_field_id переделай так:
int sort_field_id = -1;

Это чтобы если сортировка по уже сортированному полю не зацикливала вывод на экран.

На всякий случай повесил готовый exe на http://webfile.ru/624007

Ответить

Номер ответа: 21
Автор ответа:
 HOOLIGAN



Вопросов: 0
Ответов: 1066
 Профиль | | #21 Добавлено: 10.11.05 08:02
Если нужно сделать чтобы можно разное количество студентов выбирать, то константу STUDENT_COUNT можно заменить на переменную. Т.е. #define STUDENT_COUNT 8 убрать из кода, а к глобальным переменным добавить строку: int STUDENT_COUNT; Матюкаться не будет :)))

После этого в начале main запросить у юзера количество студентов и сохранить в переменную.

int main()
{
    HANDLE      hStdout, hStdin;
    char        buffer[128];
    
    hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hStdout, FOREGROUND_BLUE|
                                     FOREGROUND_GREEN|
                                     FOREGROUND_INTENSITY);

    do
    {
        system ("cls";); /* очистка экрана */
        printf ( to_oem("\n\n\n\t\tВведите количество студентов /от 1 до 20/ : ";) );
        /* получаем количество студентов */
        cin >> buffer;
        /*переводим строку в int и сохраняем в глобальной переменной */
        STUDENT_COUNT = atoi(buffer);
    }
    /* если количество нас не устраивает (<1 или > 20), запрашиваем снова */
    while ( STUDENT_COUNT < 1 || STUDENT_COUNT > 20 );

    .....
    .....


static означает, что объект определен локально в данном файле/модуле

Ответить

Номер ответа: 22
Автор ответа:
 HOOLIGAN



Вопросов: 0
Ответов: 1066
 Профиль | | #22 Добавлено: 10.11.05 11:46
В общем задолбал этот cin со своим пробелом, заменил его старым добрым апи ReadConsole, сделал проверку правильности ввода номера группы и оценок. Вынес показ "менюшки" в отдельную процедуру и сделал чтобы при неправильном выборе действия курсор не перескакивал на след. строку, портя картинку :)

Сорец и exe на http://webfile.ru/624265

Ответить

Номер ответа: 23
Автор ответа:
 vito



Разработчик Offline Client

Вопросов: 23
Ответов: 879
 Web-сайт: softvito.narod2.ru
 Профиль | | #23
Добавлено: 10.11.05 13:18
HOOLIGAN
Так на всякий случай.

char inBuf[ 1024 ];

int  cnt = 0;

while (cnt<3&&cin >> inBuf) {
       

char *str = new char[ strlen( inBuf ) + 1 ];
           strcpy( str, inBuf );
          cout << inBuf<<"\n";
  
   // ... сделать что-то с массивом символов str
           delete [] str;
    ++cnt;
}


К примеру для строки из трех символов.

Ответить

Номер ответа: 24
Автор ответа:
 HOOLIGAN



Вопросов: 0
Ответов: 1066
 Профиль | | #24 Добавлено: 10.11.05 16:12
vito, этот пример работает как раз так, как я говорил: пробелы воспринимаются как окончание ввода. При попытке ввести строку с пробелами, например "Текст в консоли" <ENTER> этот код разобьёт строку на 3 части. На один ввод он три раза проскочит цикл и по cout выдаст 3 подстроки:
Текст
в
консоли

Если заранее неизвестно, сколько пробелов введёт юзер, и введёт ли вообще, предсказать, сколько раз будет приглашение ввести строку невозможно. Код неуправляемый. Если юзер введёт "Текст в", то прога покажет
Текст
в
и будет ждать третьего ввода (хотя на самом деле он только второй). По третьему вводу он опять отобразит только слово до первого пробела.

От этих глюков свободен такой код с апи:
//===============================================================
static int read_console ( char* lpBuffer, int buf_len ) /* получение ввода с клавиатуры */
{
    ;DWORD       Read;
    int         i=0;

    if ( ReadConsole(hStdin, lpBuffer, buf_len, &Read, NULL) )
    {
        while ( i < Read && lpBuffer[i++] != 0xD);
        lpBuffer[i-1]=0;
        return (i-1);
    }
    return 0;
}

.......

    char    buffer[128];
    read_console( buffer, sizeof(buffer) );
    cout << buffer;



На каждый ввод строки, независимо с пробелами или нет, он отобразит всю строку целиком: "Текст в консоли"

Ответить

Номер ответа: 25
Автор ответа:
 vito



Разработчик Offline Client

Вопросов: 23
Ответов: 879
 Web-сайт: softvito.narod2.ru
 Профиль | | #25
Добавлено: 10.11.05 16:49
HOOLIGAN
Я не спорю, просто привел пример(можно жестко не привязывать, а проверку сделать внутри цикла).
Выбор вопрос вкуса, API действительно удобнее.

Кстати ты чем компилил, а то силно "поумневший" VC 7 плевался.
/* запуск юзера по кольцу, пока не сделает выбор от 0 до 3*/
    while ( buffer[0]<'0' || buffer[0]>'3' )
        cin >> buffer;
    /* возвращаем выбор юзера */
    return ( buffer[0]-0x30 );


Требуя инициализации buffer перед сравнением, что в принципе логично.

Кстати неплохо было бы подробнее закооментировать. Простенький пример превратился в полноценную прграмму.
Там очень много вылезло нюансов. HACKERU тяжеловато будет понять(боюсь как бы у него руки не опустились).

Ответить

Номер ответа: 26
Автор ответа:
 HOOLIGAN



Вопросов: 0
Ответов: 1066
 Профиль | | #26 Добавлено: 10.11.05 18:02
У тебя устаревший код :))) По последней ссылке, что на webfile.ru/624265 есть исходник с новой процедурой, которая выглядит так:
char get_action()           /* получение выбора действия от юзера*/
{
char    buffer[128];

show_menu();
read_console( buffer, sizeof(buffer) );

/* запуск юзера по кольцу, пока не сделает выбор от 0 до 3*/
while ( buffer[0]<'0' || buffer[0]>'3' || buffer[0]==(sort_field_id+0x30) )
    {
     system("cls";);
     if ( buffer[0]==(sort_field_id+0x30) )
            show_array ( array, STUDENT_COUNT );
     show_menu();
     read_console( buffer, sizeof(buffer) );
    }

        
Перед циклом стоит read_console в буфер. Так что инициализация есть.
Если у HACKER'а есть вопросы, пусть спрашивает

Ответить

Номер ответа: 27
Автор ответа:
 HACKER


 

Разработчик Offline Client

Вопросов: 236
Ответов: 8362
 Профиль | | #27 Добавлено: 10.11.05 20:37
Да в принципе нет, вроде всё понятно, описания API я сам в сотоянии найти. А примерчик действительно нефиговый получился, как интерестно на меня препод посмотрит? :) Ведь у нас в группе все на уровне cout<<"Hello World"; ну может быть кто-то чуть знает более... А тут я таккую барашуру принесу :) Её и препод проверить несможет :) Скажет иди... всё правельно :) Ну вообщем БААААЛЬШОЕ!! спасибо Хулигану, который не пожелел времени...!!! Respect тебе чувак! сам бы я сделал всё на уровне примитива...

Ответить

Номер ответа: 28
Автор ответа:
 Sharp


Лидер форума

ICQ: 216865379 

Вопросов: 106
Ответов: 9979
 Web-сайт: sharpc.livejournal.com
 Профиль | | #28
Добавлено: 11.11.05 01:39
Почему не сделать вместо while{} do{}while?

Ответить

Номер ответа: 29
Автор ответа:
 HACKER


 

Разработчик Offline Client

Вопросов: 236
Ответов: 8362
 Профиль | | #29 Добавлено: 11.11.05 03:51
да нафиг надо, так работает, и так понятно, do while, while циклы вроде освоил :) Лучше расскажите что за static int read_console

Static что означает?

Ответить

Номер ответа: 30
Автор ответа:
 HOOLIGAN



Вопросов: 0
Ответов: 1066
 Профиль | | #30 Добавлено: 11.11.05 07:59
Почему не сделать вместо while{} do{}while?


Потому что нарушится логика программы: при вызове get_action первый показ менюшки (show_menu) должен быть без очистки экрана (cls), все остальные возможные показы меню должны сопровождаться предварительной cls.

static (насколько я понимаю) означает локальный, видимый только из данного модуля. Можешь убрать его, если не нравится :)

Ответить

Страница: 1 | 2 | 3 | 4 | 5 |

Поиск по форуму



© Copyright 2002-2011 VBNet.RU | Пишите нам