Проблема при работе с многомерными массивами
Пользователь вводит строку. Я считаю кол-во слов и длину максимального слова. Затем я создаю символьный двумерный динамический массив, кол-во строк которого равняется кол-ву слов в строке, а кол-во столбцов - длине максимального слова. Я запихиваю по одному слову из строки в каждую строку матрицы и дописываю символ конца строки. Затем я сортирую полученный массив слов по возрастанию, т.е. в первой строке матрицы находится самое короткое слово, далее - длиннее и т.д.
Вот тут и начинатеся самое интересное...
Я не знаю, как по запросу пользователя вывести несколько первых слов, содержащихся в матрице.
Далее излагаю суть проблемы.
Задача состоит в том, чтобы по запросу пользователя вывести несколько самых коротких слов. Так вот, функция build_array возвращает указатель на первую строку матрицы (words_array[0]). Далее пользователь просит вывести i самых коротких слов. В функции main значение words_array[0] принимает указатель tmp_arr:
words_count(users_string));
С помощью следующей конструкции пытаюсь осуществить вывод первых строк матрицы:
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
#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
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
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;
}
Почему бы не использовать С++ ?
Т.е. использовать стандартный строковый класс, какой-нибудь из контейнеров.
Твоя задача будет решаться программой всего в десяток строк, а не таким жутким монстром, как ты привел.
На счёт массива указателей и Си... Именно это я и использовал. Конструкция вида
words_array = new char*[num_words];
как раз и создаёт массив указателей. Как сделать меньше кода я не знаю, но мне не это нужно.
Дело осталось за малым - обеспечить корректный вывод на экран за счёт нормального интерфейса между двумя функциями. Всё остальное у меня готово и работает, зачем менять?
for(i=0;i<strlen(in_string);i++)
{
if(instring==' ')
instring='\0';
else if(instring[i-1]=='\0')
{
p_arr[j]=(instring+i); //не уверен что это правильно, но идея думаю ясна
j++;
}
}
Очень хороший подход, большое спасибо, я обязательно попробую. Должно получиться значительно более рационально. Благодарю!
Нашёл глюк в функции delete_spaces. Она должна оставить не более одного пробела между словами. Естественно, она удаляет все пробелы и в начале, и в конце строки. Так вот, она работает нормально, если строка пользователя начинается с пробела(ов). Если же в начале строки пробела нет, то при выполнении очистки памяти, занимаемой динамическим массивом, во время выполнения прграммы Visual C++ ругается на повреждение кучи вот так:
Трассировал функцию, не могу понять, в чём дело. Строка динамического массива индексируется правильно вне зависимости от содержимого.
Код функции:
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 - очистка динамической памяти. Использую следующий фрагмент:
delete [] tmp_arr;
delete [] tmp_arr;
Трассирую. Всё адресуется нормально, первые 4 строки матрицы удаляются корректно, но вот на 5 выскакивает Heap Corruption Detected. В чём дело - ну хоть убей не знаю!
В очередной раз прошу вас о помощи.
Зачем тебе вобще динамичаская память. Пробегая по строке, выдели указатели на слова, и вноси их в массив, заменяя первый пробел после слова нулем. Что-то типа этого(пишу сходуб не проверяя)
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, за что ему огромное спасибо.
Задача полностью решена. Хоть рациональность сильно хромает (почему - смотрите приведённый выше код), но зато разобрался с указателями и динамической памятью.
#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
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;
}
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;
}
Получил особое моральное удовлетворение, доведя программу до ума.
Я настоятельно рекомендую тебе изучить std::string.
Я настоятельно рекомендую тебе изучить std::string.
Вобще кошмарищё!!!!!! :eek:
Вот Вам и преимущество классов,наследования, полиморфизма и инкапсуляции))))) :)
Есть ниже...ассемблер не пробовал??? Можно поизвращаться и переписать твою задачу на asm, ну а потом потренироваться в HEX редакторе на машинном языке написать. :D
[/LEFT]
Кроме std::string советую также хорошенько изучить потоки (stream) в т.ч. и cin.