Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Проблема при работе с многомерными массивами

10K
10 марта 2007 года
Shalfey
47 / / 10.03.2007
У меня следующая ситуация.
Пользователь вводит строку. Я считаю кол-во слов и длину максимального слова. Затем я создаю символьный двумерный динамический массив, кол-во строк которого равняется кол-ву слов в строке, а кол-во столбцов - длине максимального слова. Я запихиваю по одному слову из строки в каждую строку матрицы и дописываю символ конца строки. Затем я сортирую полученный массив слов по возрастанию, т.е. в первой строке матрицы находится самое короткое слово, далее - длиннее и т.д.
Вот тут и начинатеся самое интересное...
Я не знаю, как по запросу пользователя вывести несколько первых слов, содержащихся в матрице.
Далее излагаю суть проблемы.

Задача состоит в том, чтобы по запросу пользователя вывести несколько самых коротких слов. Так вот, функция build_array возвращает указатель на первую строку матрицы (words_array[0]). Далее пользователь просит вывести i самых коротких слов. В функции main значение words_array[0] принимает указатель tmp_arr:
 
Код:
char *tmp_arr = build_array(users_string, max_len_word(users_string),
        words_count(users_string));

С помощью следующей конструкции пытаюсь осуществить вывод первых строк матрицы:
 
Код:
cout<<"\nHow many shortest words d'you want to see?\n";
    unsigned n_words = 0;
    cin>>n_words;
    char *tmp_arr = build_array(users_string, max_len_word(users_string),
        words_count(users_string));
    cout<<"Here are "<<n_words<<" shortest words:\n";
    for(unsigned i = 0; i < n_words - 1; i++)
        cout<<tmp_arr<<endl;

Как я уже написал, каждая строка матрицы заканчивается символом '\0', поэтому я полагал, что конструкция cout<<tmp_arr выведет всю строку с индексом i, но она выводит симолы, а не строки.

Я не знаю, что возврщать из функции build_array и как это обрабатывать, чтобы выводить выводить определённые строки матрицы. Помогите мне с этим, пожалуйста.

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

my_str.h
 
Код:
#ifndef __MY_STR_H__
#define __MY_STR_H__
#include<cstring>
#include<iostream>
using namespace std;
void delete_spaces(char my_string[]);
unsigned int max_len_word(char my_string[]);
unsigned words_count(char str[]);
char *build_array(char str[], unsigned max_length, unsigned num_words);
#endif


functions.cpp
Код:
#include "my_str.h"
void delete_spaces(char my_string[]){
    char *new_string = new char[strlen(my_string)];
    unsigned new_str_ctr = 0;
    char *symbol = my_string;
    while(*symbol == ' ')symbol++;
    while(*symbol != '\0'){
        if((*symbol == ' ')&&
            ((*(symbol+1) == ' ')||(*(symbol+1) == '\0')))
            symbol++;
        else {
            new_string[new_str_ctr++] = *symbol;
            symbol++;
        }
    }
    new_string[new_str_ctr] = '\0';
    for(unsigned i = 0; i < new_str_ctr + 1; i++)
        my_string = new_string;
    delete [] new_string;
}
unsigned int max_len_word(char my_string[]){
    unsigned max_len, cur_len;
    cur_len = max_len = 0;
    char *ptr = my_string;
    do{
        if((*ptr != ' ')&&(*ptr != '\0'))cur_len++;
        else{
            if(cur_len > max_len)max_len = cur_len;
            cur_len = 0;
        }
        ptr++;
    }while(*ptr != '\0');
    return max_len;
}
unsigned words_count(char str[]){
    unsigned words_counter = 0;
    char *ptr = str;
    while(*ptr != '\0'){
        if((*ptr != ' ')&&((*(ptr+1) == ' ')||(*(ptr+1) == '\0'))){
            words_counter++;
        }
        ptr++;
    }
    return words_counter;
}
char *build_array(char str[], unsigned max_length, unsigned num_words){
    char **words_array;
    words_array = new char*[num_words];
    // Создали 1 столбец
    for(unsigned i = 0; i < num_words; i++)
        words_array = new char[max_length];
    char *ptr = str;
    unsigned width, height;
    width = height = 0;
    while(*ptr != '\0'){
        if((isalpha(*ptr))||(*ptr == '\'')||(*ptr == '-'))
            words_array[height][width++] = *ptr;
        else{
            if((isalpha(*(ptr-1)))||(*(ptr-1) == '\'')||(*(ptr-1) == '-')){
        // Переходим на следующую строку матрицы тогда, когда предыдущий символ
        // был из разряда допустимых. Таким образом исключим переход на новую строку
        // матрицы при встрече подряд идущих символов не из нашего диапазона.
                words_array[height][width] = '\0';
                height++;
                width = 0;
            }
        }
        ptr++;
    }
    // Строку разрезали на слова и распихали по слову в каждую строку
    // динамической матрицы.
    bool flag = false;
    do{
        flag = false;
        for(unsigned i = 0; i < num_words-1; i++){
            if(strlen(words_array) > strlen(words_array[i+1])){
                flag = true;
                char *temp = new char[strlen(words_array)];
                temp = words_array;
                words_array = words_array[i+1];
                words_array[i+1] = temp;
            }
        }
    }while(flag);
    // Отсортировали массив по возрастанию.
    return words_array[0];
}


main.cpp
Код:
#include "my_str.h"
int main(void){
    cout<<"Please, enter the string:\n\n";
    char *users_string = new char[100];
    cin.getline(users_string, strlen(users_string)+1);
    delete_spaces(users_string);
    cout<<"Here's your string with only one space between words:\n\n";
    cout<<users_string;
    cout<<"\nHow many shortest words d'you want to see?\n";
    unsigned n_words = 0;
    cin>>n_words;
    char *tmp_arr = build_array(users_string, max_len_word(users_string),
        words_count(users_string));
    cout<<"Here are "<<n_words<<" shortest words:\n";
    for(unsigned i = 0; i < n_words - 1; i++)
        cout<<tmp_arr<<endl;
    // Вывод симворлов, а не строк!!!
    cout<<endl<<"Good bye!"<<endl;
    delete [] tmp_arr;
                // Очистка памяти конечно же некорректная, но это я и сам в состоянии поправить,
                //сейчас не это самое главное.
    cin.get();
    return 0;
}
3
10 марта 2007 года
Green
4.8K / / 20.01.2000
Чем объясняется применение двумерных массивов?
Почему бы не использовать С++ ?
Т.е. использовать стандартный строковый класс, какой-нибудь из контейнеров.
Твоя задача будет решаться программой всего в десяток строк, а не таким жутким монстром, как ты привел.
274
10 марта 2007 года
Lone Wolf
1.3K / / 26.11.2006
точно двумерные массивы то зачем? если, написать нужно именно на С, то непроще ли использывать массив указателей? кода раза в 2 меньше будет
10K
10 марта 2007 года
Shalfey
47 / / 10.03.2007
Насчёт строкового класса... Во-первых, я не знаю, как с ним работать. Во-вторых, я хочу поиграться именно с низкоуровневым кодом.

На счёт массива указателей и Си... Именно это я и использовал. Конструкция вида
 
Код:
char **words_array;
    words_array = new char*[num_words];

как раз и создаёт массив указателей. Как сделать меньше кода я не знаю, но мне не это нужно.
Дело осталось за малым - обеспечить корректный вывод на экран за счёт нормального интерфейса между двумя функциями. Всё остальное у меня готово и работает, зачем менять?
274
10 марта 2007 года
Lone Wolf
1.3K / / 26.11.2006
да эо массив указателей, но ты зачем-то дублируеш строки в памяти. Зачем тебе вобще динамичаская память. Пробегая по строке, выдели указатели на слова, и вноси их в массив, заменяя первый пробел после слова нулем. Что-то типа этого(пишу сходуб не проверяя)
Код:
j=0;
for(i=0;i<strlen(in_string);i++)
{
   if(instring==' ')
      instring='\0';
   else if(instring[i-1]=='\0')
   {
      p_arr[j]=(instring+i);   //не уверен что  это правильно, но идея думаю ясна
      j++;
   }
}
10K
12 марта 2007 года
Shalfey
47 / / 10.03.2007
Lone Wolf
Очень хороший подход, большое спасибо, я обязательно попробую. Должно получиться значительно более рационально. Благодарю!
10K
13 марта 2007 года
Shalfey
47 / / 10.03.2007
Проблемы продолжаются...
Нашёл глюк в функции delete_spaces. Она должна оставить не более одного пробела между словами. Естественно, она удаляет все пробелы и в начале, и в конце строки. Так вот, она работает нормально, если строка пользователя начинается с пробела(ов). Если же в начале строки пробела нет, то при выполнении очистки памяти, занимаемой динамическим массивом, во время выполнения прграммы Visual C++ ругается на повреждение кучи вот так:
Цитата:
HEAP CORRUPTION DETECTED: after Normal block (#115) at 0x00355C90. CRT detected that the application wrote to memory after end of heap buffer


Трассировал функцию, не могу понять, в чём дело. Строка динамического массива индексируется правильно вне зависимости от содержимого.
Код функции:

Код:
void delete_spaces(char my_string[]){
    char *new_string = new char[strlen(my_string)];
    unsigned new_str_ctr = 0;
    char *symbol = my_string;
    while(*symbol == ' ')symbol++;
    while(*symbol != '\0'){
        if((*symbol == ' ')&&
            ((*(symbol+1) == ' ')||(*(symbol+1) == '\0')))
            symbol++;
        else {
            new_string[new_str_ctr++] = *symbol;
            symbol++;
        }
    }
    new_string[new_str_ctr] = '\0';
    for(unsigned i = 0; i < new_str_ctr + 1; i++)
        my_string = new_string;
    delete [] new_string;
// Именно здесь, при освобождении памяти происходит повреждение
// кучи, но лишь тогда, когда строка начинается не с пробела.
}

--------------------
Проблема 2 - очистка динамической памяти. Использую следующий фрагмент:
 
Код:
for(unsigned i = 0; i < words_count(users_string); i++)
        delete [] tmp_arr;
    delete [] tmp_arr;

Трассирую. Всё адресуется нормально, первые 4 строки матрицы удаляются корректно, но вот на 5 выскакивает Heap Corruption Detected. В чём дело - ну хоть убей не знаю!

В очередной раз прошу вас о помощи.
10K
05 апреля 2007 года
Shalfey
47 / / 10.03.2007
Кстати, если кому-то ещё интересно, Lone Wolf меня натолкнул на значительно более простое решение задачи, но без использования string & STL.
Цитата:

Зачем тебе вобще динамичаская память. Пробегая по строке, выдели указатели на слова, и вноси их в массив, заменяя первый пробел после слова нулем. Что-то типа этого(пишу сходуб не проверяя)
Код:
j=0;
for(i=0;i<strlen(in_string);i++)
{
   if(instring==' ')
      instring='\0';
   else if(instring[i-1]=='\0')
   {
      p_arr[j]=(instring+i);   //не уверен что  это правильно, но идея думаю ясна
      j++;
   }
}



Как видите, легко обходимся одномерным массивом указателей.

К сожалению в последние дни было очень мало времени, поэтому только сейчас выкладываю код, который отлаживали вместе с CJ, за что ему огромное спасибо.
Задача полностью решена. Хоть рациональность сильно хромает (почему - смотрите приведённый выше код), но зато разобрался с указателями и динамической памятью.

 
Код:
#ifndef __MY_STR_H__
#define __MY_STR_H__
#include<cstring>
#include<iostream>
using namespace std;
void delete_spaces(char my_string[]);
unsigned int max_len_word(char my_string[]);
unsigned words_count(char str[]);
char **build_array(char str[], unsigned max_length, unsigned num_words);
#endif

Код:
#include "my_str.h"
void delete_spaces(char my_string[]){
     char *new_string = new char[strlen(my_string)+1];
     unsigned new_str_ctr = 0;
     char *symbol = my_string;
     while(*symbol == ' ')symbol++;
     while(*symbol != '\0'){
          if((*symbol == ' ')&&
               ((*(symbol+1) == ' ')||(*(symbol+1) == '\0')))
               symbol++;
          else {
               new_string[new_str_ctr++] = *symbol;
               symbol++;
          }
     }
     new_string[new_str_ctr] = '\0';

     for(unsigned i = 0; i < new_str_ctr + 1; i++)
          my_string = new_string;
     delete [] new_string;
}
unsigned int max_len_word(char my_string[]){
     unsigned max_len, cur_len;
     cur_len = max_len = 0;
     char *ptr = my_string;
     do{
          if((*ptr != ' ')&&(*ptr != '\0'))cur_len++;
          else{
               if(cur_len > max_len)max_len = cur_len;
               cur_len = 0;
          }
          ptr++;
     }while(*ptr != '\0');
     if(cur_len > max_len)max_len = cur_len;
     return max_len;
}
unsigned words_count(char str[]){
     unsigned words_counter = 0;
     char *ptr = str;
     while(*ptr != '\0'){
          if((*ptr != ' ')&&((*(ptr+1) == ' ')||(*(ptr+1) == '\0'))){
               words_counter++;
          }
          ptr++;
     }
     return words_counter;
}
char **build_array(char str[], unsigned max_length, unsigned num_words){
     char **words_array;
     words_array = new char*[num_words];
     for(unsigned i = 0; i < num_words; i++)
          words_array = new char[max_length+1];
     char *ptr = str;
     unsigned width, height;
     width = height = 0;
     while(*ptr != '\0'){
          if((isalpha(*ptr))||(*ptr == '\'')||(*ptr == '-'))
               words_array[height][width++] = *ptr;
          else{
               if((isalpha(*(ptr-1)))||(*(ptr-1) == '\'')||(*(ptr-1) == '-')){
                    words_array[height][width] = '\0';
                    height++;
                    width = 0;
               }
          }
          ptr++;
     }
     bool flag = false;
     char* temp;
     do{
          flag = false;
          for(unsigned i = 0; i < num_words-1; i++){
               if(strlen(words_array) > strlen(words_array[i+1])){
                    flag = true;
                    temp = words_array;
                    words_array = words_array[i+1];
                    words_array[i+1] = temp;
               }
          }
     }while(flag);
     return words_array;
}

Код:
#include "my_str.h"
int main(void){
     cout<<"Please, enter the string(not more than 100 symbols):\n\n";
     char *users_string = new char[100];
     cin.getline(users_string, strlen(users_string)+1);
     delete_spaces(users_string);
     cout<<"Here's your string with only one space between words:\n\n";
     cout<<users_string;
     cout<<"\nHow many shortest words d'you want to see?\n";
     unsigned n_words = 0;
     cin>>n_words;
     char **tmp_arr = build_array(users_string, max_len_word(users_string),
          words_count(users_string));
     cout<<"Here are "<<n_words<<" shortest words:\n";
     for(unsigned i = 0; i < n_words; i++)
          cout<<tmp_arr<<endl;
     cout<<endl<<"Good bye!"<<endl;
     cin.get(); cin.get();
     for(unsigned i = 0; i < words_count(users_string); i++)
          delete [] tmp_arr;
     delete[] tmp_arr;
     delete[] users_string;
     return 0;
}


Получил особое моральное удовлетворение, доведя программу до ума.
3
05 апреля 2007 года
Green
4.8K / / 20.01.2000
Жуть! Ты же микроскопом гвозди забиваешь.
Я настоятельно рекомендую тебе изучить std::string.
502
05 апреля 2007 года
Jail
550 / / 30.01.2007
[LEFT]
Цитата:
Жуть! Ты же микроскопом гвозди забиваешь.
Я настоятельно рекомендую тебе изучить std::string.


Вобще кошмарищё!!!!!! :eek:
Вот Вам и преимущество классов,наследования, полиморфизма и инкапсуляции))))) :)

Цитата:
Насчёт строкового класса... Во-первых, я не знаю, как с ним работать. Во-вторых, я хочу поиграться именно с низкоуровневым кодом.


Есть ниже...ассемблер не пробовал??? Можно поизвращаться и переписать твою задачу на asm, ну а потом потренироваться в HEX редакторе на машинном языке написать. :D

[/LEFT]

3
06 апреля 2007 года
Green
4.8K / / 20.01.2000
Сарказм сарказмом, но твою программу можно было уместить в десятка полтора-два строчек хорошо читабельного кода.
Кроме std::string советую также хорошенько изучить потоки (stream) в т.ч. и cin.
63
06 апреля 2007 года
Zorkus
2.6K / / 04.11.2006
А так же, хоть десяток самых употребительных алгоритмов:).
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог