Форум программистов, компьютерный форум CyberForum.ru

И снова malloc... - C++

Восстановить пароль Регистрация
 
Рейтинг: Рейтинг темы: голосов - 13, средняя оценка - 4.69
tes
 Аватар для tes
0 / 0 / 0
Регистрация: 19.01.2011
Сообщений: 3
07.06.2011, 16:33     И снова malloc... #1
Всем привет!

Читаю книгу "Герберт Шилдт - самоучитель С++"
В нём такое задание:
Код
Измените класс stack так, чтобы память для стека выделялась динамически.
При этом длина стека должна задаваться параметром конструктора. 
(Не забудьте освободить эту память с помощью деструктора.)
Я выполнил в таком виде:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include <cstdlib>
#include <iostream>
 
using namespace std;
 
#define max_size 100
 
class stack{
     char *stck;
     int tos;
     char who;
public:
     stack (char c, int pam);
     ~stack();
     void push(char ch);
     char pop(); 
};
 
stack::stack(char c, int pam)
{
     tos = 0;
     who = c;
     stck = (char*) malloc(pam);
     cout << pam << endl;
     if (!stck)
     {
          cout << "Ошибка выделения памяти" << endl;
          exit(1);
     }    
}
 
void stack::push(char ch)
{
     if (tos >= max_size){
          cout << "Стек " << who << " полон." << endl;
          return;
     }
 
     stck[tos] = ch;
     ++tos;
}
 
char stack::pop()
{
     if (tos == 0) {
          cout << "Стек " << who << " пуст." << endl;
          return 0x20;
     }
     else
     {
          --tos;
          return  stck[tos];
     };
}
 
stack::~stack()
{
     cout << "Освобождение памяти" << endl;
     free(stck);              
}
 
int main()
{
    stack s1('A',0), s2('B',0);
    int i;
    
    s1.push('a');
    s2.push('x');
    s1.push('b');
    s2.push('y');
    s1.push('c');
    s2.push('z');
    
 
    for (i = 0; i < 5; ++i) cout << "Символ из стека s1: " << s1.pop() << endl; //Для проверки запихиваем 3 , вытаскиваем 5
    for (i = 0; i < 5; ++i) cout << "Символ из стека s2: " << s2.pop() << endl;
 
    return EXIT_SUCCESS;
}
Потом меня взяли сомнения по поводу выделения памяти и я решил посмотреть ответы данные в конце книги, как оказалось там память выделяется так же...

Так собственно вопрос, почему когда я ставлю stack s1('A',0), s2('B',0);, у меня всё проходит нормально? Почему УМВР? ЧЯДН? Может кто нибудь прояснить ситуацию?
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
07.06.2011, 16:33     И снова malloc...
Посмотрите здесь:

malloc в С++ C++
malloc() C++
C++ malloc vs new
new на malloc C++
new, malloc, C++
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16824 / 5245 / 319
Регистрация: 30.03.2009
Сообщений: 14,121
Записей в блоге: 26
07.06.2011, 17:06     И снова malloc... #2
Цитата Сообщение от tes Посмотреть сообщение
Потом меня взяли сомнения по поводу выделения памяти
Тебя сомнения одолели правильные и с памятью работа сделана некорректно. В качестве pam ты всюду подаёшь 0. Когда 0 подаётся в malloc, то во многих реализациях номинально выделяется 1 байт. Далее malloc выделяет выровненную память, в слчае i386 она выравнивается на 16 байт (или на 8). Т.е. при вызове malloc(0) у тебя реально выделяется на машине 8 или 16 байт. При этом в стек ты суёшь только по 3 элемента, а потому у тебя ошибка не проявляется

Модифицируй немного свою программу. В main у тебя стеки создаются сначала s1, потом s2. Поменяй их местами. И сделай не по 3 push'а, а по 17. При таком раскладе у меня на твоём коде программа сломалась

Добавлено через 11 секунд
Цитата Сообщение от tes Посмотреть сообщение
Почему УМВР? ЧЯДН?
Цэ шо?

Добавлено через 13 секунд
А... понял
tes
 Аватар для tes
0 / 0 / 0
Регистрация: 19.01.2011
Сообщений: 3
07.06.2011, 21:04  [ТС]     И снова malloc... #3
Цитата Сообщение от Evg Посмотреть сообщение
И сделай не по 3 push'а, а по 17. При таком раскладе у меня на твоём коде программа сломалась
Очень странно программа себя ведёт... Сколько malloc "по умолчанию" выделяет не совсем понятно...

Вот немного переписал программу:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include <cstdlib>
#include <iostream>
 
using namespace std;
 
#define max_size 100
 
class stack{
     char *stck;
     unsigned int tos;
     char who;
public:
     stack (char c, int pam);
     ~stack();
     void push(char ch);
     char pop(); 
};
 
stack::stack(char c, int pam)
{
     tos = 0;
     who = c;
     stck = (char*) malloc(pam);
     cout << pam << endl;
     if (!stck)
     {
          cout << "Ошибка выделения памяти" << endl;
          exit(1);
     }    
}
 
void stack::push(char ch)
{
     if (tos >= max_size){
          cout << "Стек " << who << " полон." << endl;
          return;
     }
 
     stck[tos] = ch;
     ++tos;
}
 
char stack::pop()
{
     if (tos == 0) {
          cout << "Стек " << who << " пуст." << endl;
          return 0x20;
     }
     else
     {
          --tos;
          return  stck[tos];
     };
}
 
stack::~stack()
{
     cout << "Освобождение памяти" << endl;
     free(stck);              
}
 
int main()
{
    stack s1('A',85), s2('B',85), s3('C',85);
    unsigned int i;
    
    cout << "В стек A->>>>" << endl;
    for (i = 0; i < max_size; ++i) s1.push('f');
 
    cout << "В стек B->>>>" << endl;
    for (i = 0; i < max_size; ++i) s2.push('g');    
 
    cout << "В стек C->>>>" << endl;
    for (i = 0; i < max_size; ++i) s3.push('h'); 
    
    cout << "Из стека A->>>>" << endl;
    for (i = 0; i < max_size; ++i) 
    {
        if ((i % 100) != 99)
           if (s1.pop() == 'f') cout << "*";
           else cout << "x";
        else
           if (s1.pop() == 'f') cout << "*" << endl;
           else cout << "x" << endl;
    }
    
    cout << "Из стека B->>>>" << endl;
    for (i = 0; i < max_size; ++i) 
    {
        if ((i % 100) != 99)
           if (s2.pop() == 'g') cout << "*";
           else cout << "x";
        else
           if (s2.pop() == 'g') cout << "*" << endl;
           else cout << "x" << endl;
    }
 
    cout << "Из стека C->>>>" << endl;
    for (i = 0; i < max_size; ++i) 
    {
        if ((i % 100) != 99)
           if (s3.pop() == 'h') cout << "*";
           else cout << "x";
        else
           if (s3.pop() == 'h') cout << "*" << endl;
           else cout << "x" << endl;
    }
    
    cout << endl << endl;
    return EXIT_SUCCESS;
}
Вроде бы она в таком случае ( stack s1('A',85), s2('B',85), s3('C',85) ) должна выделить 16 + 85 или 8 + 85, но нет ведь....

http://www.cyberforum.ru/attachment....1&d=1307466054
Миниатюры
И снова malloc...  
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16824 / 5245 / 319
Регистрация: 30.03.2009
Сообщений: 14,121
Записей в блоге: 26
07.06.2011, 22:57     И снова malloc... #4
Цитата Сообщение от tes Посмотреть сообщение
Сколько malloc "по умолчанию" выделяет не совсем понятно...
Чтобы не думать о том, что там по умолчанию, надо работать правильно и не задумываться о таких вопросах. Т.е. выделять памяти столько, сколько надо, а не в количестве 0 байт.

Цитата Сообщение от tes Посмотреть сообщение
должна выделить 16 + 85 или 8 + 85
Наверное я не так выразился. Память, возвращаемая malloc'ом, всегда должна быть выровнена на 8 байт (или 16, но пусть будет 8). Это означает, что после того, как ты выделил 1 байт, то последующий вызов malloc'а пропустит 7 байт, чтобы следующий адрес был так же кратен 8 байтам. В итоге те 7 байт останутся неиспользоваными. Но при работе с указателем от первого вызова в эти ненужные 7 байт ты можешь нормально обращаться и программа не упадёт (хотя номинально код будет ошибочным). Ну и в таком предположении malloc всегда как бы выделяет количество байт памяти, округлённое до кратности 8 (с округлением в большую сторону). Т.е. если в malloc подать значения от 1 до 8, то всё равно как бы будет выделено 8 байт (выделено будет столько, сколько нужно плюс неиспользуемый хвост). Если подать значение от 9 до 16, то будет выделено 16 байт и т.п.

Всё, что написано выше - я объяснял причину того, почему твой некорректный код работал нормально и не падал. На i386 оно работает так, на другой архитектуре может работать по-другому. Поэтому неправильная работа с динамической памятью опасна тем, что код может работать на одной машине и ломаться на другой

Можешь ещё немного тут почитать: http://www.cyberforum.ru/cpp-experts...ml#post1671703

Что касается твоего кода, то он уже почти правильный. Только в 35 строке надо сравнивать не с max_size, а со значением pam, которое ты подал в конструктор (понятно, что его надо сохранить в экземпляре класса)
Kastaneda
Модератор
Эксперт С++
 Аватар для Kastaneda
4236 / 2769 / 218
Регистрация: 12.12.2009
Сообщений: 7,104
Записей в блоге: 1
Завершенные тесты: 1
07.06.2011, 23:33     И снова malloc... #5
Цитата Сообщение от Evg Посмотреть сообщение
чтобы следующий адрес был так же кратен 8 байтам
А с чем это связанно? Я читал просто про 2 байта (т.е. чтоб адрес был кратен 2).
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16824 / 5245 / 319
Регистрация: 30.03.2009
Сообщений: 14,121
Записей в блоге: 26
07.06.2011, 23:40     И снова malloc... #6
Цитата Сообщение от Kastaneda Посмотреть сообщение
А с чем это связанно? Я читал просто про 2 байта (т.е. чтоб адрес был кратен 2).
Указатель, возвращаемый malloc'ом должен быть приводим к указателю на любой базовый класс. Другими словами, результат malloc'а должен быть выровнен на максимальное из выравниваний среди базовых типов (по сути на выравнивание long double'а). Это как минимум. На i386 дополнительно есть какие-то магические операции с памятью, которые требуют адреса, выровненного на 8 байт. Если я ничего не напутал

Про 2 байта, которые ты читал - это скорее всего ещё в старых книгах, где ещё DOS'овские соглашения были описаны.
OstapBender
 Аватар для OstapBender
581 / 519 / 35
Регистрация: 22.03.2011
Сообщений: 1,585
07.06.2011, 23:44     И снова malloc... #7
Evg, по-моему malloc(0) это UB и никакие правила тут не действуют...
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16824 / 5245 / 319
Регистрация: 30.03.2009
Сообщений: 14,121
Записей в блоге: 26
07.06.2011, 23:46     И снова malloc... #8
Цитата Сообщение от OstapBender Посмотреть сообщение
Evg, по-моему malloc(0) это UB и никакие правила тут не действуют...
А я что-то другое сказал?

Цитата Сообщение от Evg
Когда 0 подаётся в malloc, то во многих реализациях номинально выделяется 1 байт
ForEveR
Модератор
Эксперт C++
 Аватар для ForEveR
7927 / 4709 / 318
Регистрация: 24.06.2010
Сообщений: 10,524
Завершенные тесты: 3
07.06.2011, 23:46     И снова malloc... #9
malloc с классами? оригинально
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16824 / 5245 / 319
Регистрация: 30.03.2009
Сообщений: 14,121
Записей в блоге: 26
07.06.2011, 23:47     И снова malloc... #10
Цитата Сообщение от ForEveR Посмотреть сообщение
malloc с классами? оригинально
Когда человек учится и сам разбирается - это более чем нормально
OstapBender
 Аватар для OstapBender
581 / 519 / 35
Регистрация: 22.03.2011
Сообщений: 1,585
07.06.2011, 23:51     И снова malloc... #11
Ага, увидел
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16824 / 5245 / 319
Регистрация: 30.03.2009
Сообщений: 14,121
Записей в блоге: 26
08.06.2011, 00:05     И снова malloc... #12
Вот другая реализация malloc'а. Если в вызывать malloc(0), то будет возвращать ZEROSIZEPTR - это вообще получается некая константа, видимо такое значение возвращается из тех соображений, чтобы обращения по такому адресу вызывали слом программы. Там так же есть malloc_minsize - некий минимальный выделяемый кусок. В этой (да и в большинстве других реализаций) получается, что malloc'ать память маленькими кусочками - выходит очень неэффективно, а потому любая мало-мальски серьёзная программа содержит свой менеджер памяти, который строится поверх malloc'а
Kastaneda
08.06.2011, 10:09
  #13

Не по теме:

Цитата Сообщение от Evg Посмотреть сообщение
Указатель, возвращаемый malloc'ом должен быть приводим к указателю на любой базовый класс. Другими словами, результат malloc'а должен быть выровнен на максимальное из выравниваний среди базовых типов (по сути на выравнивание long double'а)
Точно, мог бы сам догаться)
Цитата Сообщение от Evg Посмотреть сообщение
Про 2 байта, которые ты читал - это скорее всего ещё в старых книгах, где ещё DOS'овские соглашения были описаны.
Да, это я когда асм под ДОС осваивал, тогда и читал.

MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
15.07.2011, 19:37     И снова malloc...
Еще ссылки по теме:

C++ Как сделать чтобы таймер дойдя до 0 стартовал снова и снова?
И снова динамическая память, malloc и free в конструкторе и деструкторе C++
C++ Malloc vs new

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
easybudda
Модератор
Эксперт С++
 Аватар для easybudda
9371 / 5421 / 914
Регистрация: 25.07.2009
Сообщений: 10,423
15.07.2011, 19:37     И снова malloc... #14
Цитата Сообщение от Evg Посмотреть сообщение
Когда 0 подаётся в malloc, то во многих реализациях номинально выделяется 1 байт. Далее malloc выделяет выровненную память, в слчае i386 она выравнивается на 16 байт (или на 8). Т.е. при вызове malloc(0) у тебя реально выделяется на машине 8 или 16 байт.
Интересно стало...
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
 
int main(void){
    char * p;
    
    if ( ! ( p = malloc(0) ) ){
        fprintf(stderr, "Nicht gemacht, kaput!\n");
        exit(1);
    }
    
    printf("p has %d bytes allocated.\n", malloc_usable_size(p));
    
    free(p);
    exit(0);
}
Код
andrew@debnout:~/cpp/system$ gcc -o p0 p0.c 
andrew@debnout:~/cpp/system$ ./p0
p has 12 bytes allocated.
andrew@debnout:~/cpp/system$
Yandex
Объявления
15.07.2011, 19:37     И снова malloc...
Ответ Создать тему
Опции темы

Текущее время: 21:57. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru