Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.70/56: Рейтинг темы: голосов - 56, средняя оценка - 4.70
 Аватар для Svid
5 / 5 / 2
Регистрация: 02.03.2011
Сообщений: 40

Понять указатели:)

10.03.2011, 13:35. Показов 11324. Ответов 55
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Добрый день!
Совсем недавно начал изучать С++, в основном по методичке, выданной в ВУЗе и по нескольким книгам (Страуструп Б., Стефан Дэвис) и плюс то, что нахожу в интернете. Практически, всё это привязано к ряду лабораторных и контрольных, которые необходимо сделать.
Дошел до указателей....О_о. Проблема вот в чем: те практические примеры, которые показывают использование указателей, не отвечают на вопрос - зачем? То есть, все что сделано в этих примерах с помощью указателей, можно сделать и без них, и нельзя сказать, что это будет сложнее. Из-за этого очень трудно понять смысл указателей и они для меня пока что пустая абстракция.
Есть ли какие-нибудь простые практические примеры, показывающие... почему без указателей не обойтись? Например, очень легко практически объяснить суть цикла или там функции.
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
10.03.2011, 13:35
Ответы с готовыми решениями:

Указатели не могу понять
Все вопросы указал в комментариях к коду. Не могу понять почему так #include <iostream> using namespace std; int main() { ...

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

Строки (массив char), указатели. Не могу понять логику! Help!
Вообщем проблема в том что выводятся лишние символы в строках.. хотя в них есть признак окончания строк - нулевой байт я новичок в С...

55
40 / 38 / 5
Регистрация: 22.01.2011
Сообщений: 670
10.03.2011, 19:00
Студворк — интернет-сервис помощи студентам
так же дополню
C++
1
2
3
4
5
6
7
8
9
void real_swap(int *a, int *b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
 
    printf("Реальный обмен местами:\ta = %d\tb = %d\n", *a, *b);
}
... real_swap(&a, &b);
Здесь указатели как бы указавают, где находятся переменные в памяти, чтобы можно было их изменить.
C++
1
2
3
4
5
6
7
8
void image_swap(int a, int b)
{
    int temp = a;
    a = b;
    b = temp;
 
    printf("Мнимый обмен местами:\ta = %d\tb = %d\n", a, b);
}
а здесь a,b в функции это не те же самые переменные.
Реальные переменные a,b из main соприкасаются с функцией в момент вызова функции
image_swap(a, b);
при этом создаётся копия значений a,b , и в функции уже совсем другие переменные, которые уничтожатся при выходе из функции, а про реальные переменные эта функция и не знает, где они находятся. Поэтому и не может их изменить.
0
Эксперт С++
5058 / 3118 / 271
Регистрация: 11.11.2009
Сообщений: 7,044
10.03.2011, 19:06
dimon1984, перечитайте пятое и вторую половину двенадцатого сообщения в этой теме, и тогда, возможно, перестанете повторять то, что уже не один раз было разжёвано и на что ТС отозвался вполне удовлетворительным ответом "вроде понял".
1
 Аватар для talis
794 / 546 / 61
Регистрация: 11.05.2010
Сообщений: 1,298
Записей в блоге: 1
10.03.2011, 19:19
Лучший ответ Сообщение было отмечено как решение

Решение

Svid, попробую вербализовать некоторые аспекты в менее гуманитарной манере Для начала - думайте об указателях, как об адресах памяти. То есть:

C++
1
2
3
4
5
int var = 18; //допустим, лежит по адресу 0x0022ff74
int * ptr; //указатель
ptr = &var; // ptr = 0x0022ff74
 
cout << ptr << ": " << *ptr << endl;
Вывод:
Code
1
0x22ff74: 18
Амперсанд (&) - это операция взятия адреса; Вы присваиваете АДРЕС переменной var, а не её значение. Звёздочка перед именем указателя ( *ptr ) - это операция разыменовывания указателя, то есть вы берёте не значение указателя (то есть адрес), а значение, лежащее по этому адресу.

Теперь о практике: представьте себе, что вам нужно написать функцию, которая выводит массив int, но вы заранее не знаете, массив какого размера ей передадут. Это могут быть 2 int'а, а может быть десь-тыщ. В этом случае проще сделать так:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void int_out( int * array, int size )
{
   for( int i = 0; i < size; i++ )
      cout << array[i] << "; " << endl;
 
   cout << "=============\nTotal " << size << " numbers" << endl;
}
 
int main()
{
   int var = 18; 
   int test[15] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
 
   int_out( &var, 1 ); //передаём адрес var
   cout << endl;
   int_out( test, 15 ); //массивы всегда передаются по указателю; операция взятия адреса не требуется
 
   return 0;
}
Теперь вопрос: вы задумывались, почему в C и C++ нумерация элементов массива идёт с нуля, а не с единицы? Смотрите, вот так выглядит массив в памяти:

00 AF 14 28 A8 76 42 00 00 00 01 00 00 00 02 00 00 04 A1 00 00 A7 70 21 17 86 61 0A

При синтаксисе var[5] берётся адрес начала массива var и к нему прибавляется ( размер_элемента_массива_var * 5 ). То есть нулевой элемент массива int будет равен ( адрес_начала + (4 * 0) = адрес_начала ), первый - ( адрес_начала + (4 * 1) = адрес_начала + 4 ) и так далее. Вы в любом случае работаете с адресами, указатели просто позволяют это делать более просто.

Теперь на счёт ссылки. По сути, ссылка - это константный указатель на неконстантные данные, то есть вы можете изменить данные по ссылке, но не можете изменить адрес, на который она указывает.

Теперь на счёт синтаксиса printf():

C++
1
2
3
4
5
int var = 18;
   
// сначала функции передаётся значение переменной var,
// а затем её адрес
printf( "var = %i\naddress = 0x%x\n", var, &var );
Всё очень просто, если всё понимать.

Другой пример: вам нужно передать функции структуру, которая занимает в памяти 1 килобайт (ну, жирная такая структура). Вы можете либо это запихать целиком и подавиться, а можете вместо 1 килобайта записать 4 байта адреса этой структуры и на этом успокоиться. Что проще будет?

Если что - спрашивайте.

Добавлено через 1 минуту

Не по теме:

О, пока я писал, тема развилась... Если что - простите за повторения, ещё не ознакомился :)

4
 Аватар для Svid
5 / 5 / 2
Регистрация: 02.03.2011
Сообщений: 40
10.03.2011, 19:24  [ТС]
Да нет, я не
упрямо не желаем согласиться
, я и как раз и хочу понять.
Как объяснить, начинаешь изучать С++ - все понятно и последовательно, переменные,циклы, функции - есть какая-то задача, которая реализовывается с помощью цикла и т.д. А с указателями, такое впечатление, что их понятие вводится намного раньше того, когда их использование становится необходимым.
0
 Аватар для talis
794 / 546 / 61
Регистрация: 11.05.2010
Сообщений: 1,298
Записей в блоге: 1
10.03.2011, 19:25
Возможно, так оно и есть в вашей книжке. Но ведь указатели-то от этого своего значения не теряют Постарайтесь разобраться.
0
Эксперт С++
5058 / 3118 / 271
Регистрация: 11.11.2009
Сообщений: 7,044
10.03.2011, 19:32
Циклы и функции - это понятия, которые позволяют решать задачи (условный переход, как мы помним, совершил революцию в развитии вычислительной техники). Указатели - инструмент, который помогает их решать. Во многих языках нету указателей, поскольку там это не так необходимо. Поэтому указатели можно, по сути, вводить в любом месте материала, и везде они на первых парах будут одинаково непонятны.
0
 Аватар для Svid
5 / 5 / 2
Регистрация: 02.03.2011
Сообщений: 40
10.03.2011, 19:46  [ТС]
Спасибо, talis!
Стало намного понятней, особенно с функцией вывода массива.
Я еще усвою, что Вы написали по поводу того, как выглядит массив в памяти, и думаю, все встанет на свои места.
В методичке, блин, всего этого нет - там указатели появляются как сама собой разумеющаяся вещь
0
 Аватар для talis
794 / 546 / 61
Регистрация: 11.05.2010
Сообщений: 1,298
Записей в блоге: 1
10.03.2011, 20:08
Не за что, Svid. Главное запомните: указатели - это точно такие же переменные, как и char, int, float, double и технически от них не отличаются ровно ничем. Более того, это самые обычные числа, то есть их можно (хотя и не нужно) складывать и вычитать. Иногда даже для перехода на следующий элемент массива используется инкремент указателя - это не всегда хорошая идея, но иногда так делают, и ничего плохого в этом нет, разве что тут выше риск сделать первоклассную утечку памяти. Единственный нюанс - тут значение указателя увеличивается не на 1, а на ( 1 * размер_элемента_массива ), то есть, в случае с int - сразу на 4.

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
int main()
{
   int array[18];
   
   
   // arr = адрес начала массива. Пока arr != адресу последнего элемента + 1 (аккурат конец массива)
   for( int * arr = &array[0]; arr != &array[19]; arr++ )
      *arr = 12;
      
   // оно же:
   //for( int i = 0; i < 18; i++ )
   //   arr[i] = 12;
   
   
   
   
   for( int * arr = &array[0]; arr != &array[18]; arr++ )
      cout <<  *arr << "; ";
   
   // оно же:
   //for( int i = 0; i < 18; i++ )
   //   cout << arr[i] << "; ";
   
   cout << endl;
   system("pause > nul");
   
   return 0;
}
У неё тоже есть свои плюсы.
0
9 / 9 / 4
Регистрация: 04.03.2010
Сообщений: 40
11.03.2011, 07:34
talis, спасибо за развернутый ответ. Теперь еще понятнее стало Я так понял, что при создании объекта без использования new, объект помещается в кучу, а в стек только указатель на адрес в куче. В таком случае, какие плюсы создания объекта без new? Получается, что структуры, объекты классов лучше создавать в динамической памяти. А тогда почему в книжках ( по Qt например ) создаются часть экземпляров класса без new, а часть с new?
Например, GUI элементы, объект приложения ( QAplication ) в книге создаются без new.
0
122 / 85 / 16
Регистрация: 14.02.2011
Сообщений: 340
11.03.2011, 17:21
хоть тему я не открывал, но благодаря Вам всем. я тоже усек что такое указатели и как их использовать . спс удачи всем!
п.с. поставил закладку..если что еще раз прочту
0
 Аватар для talis
794 / 546 / 61
Регистрация: 11.05.2010
Сообщений: 1,298
Записей в блоге: 1
12.03.2011, 13:03
Лучший ответ Сообщение было отмечено как решение

Решение

Feax, для ответа на ваш вопрос нужно объяснить, что такое пространство имён.

Запустите этот пример:

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
#include <cstdlib>
#include <iostream>
 
using namespace std;
 
int main(int argc, char *argv[])
{
    int var = 18;
    
    cout << "main() var = " << var << endl;
    
    
    if( 1 == 1 ) //всегда истинно
    {
        int var = 24;
        
        cout << "   if var = " << var << endl;
    }
    
    cout << "main() var = " << var << endl;
    
    
    
    {
         int var = 36;
         cout << "   another var = " << var << endl;
    }
    
    
    {
         cout << "var = " << var << endl;
    }
    
    cout << "main() var = " << var << endl;
     
        
    system("pause > nul");
    return 0;
}
Обратите внимание, что int var, созданный непосредственно в операторных скобках main() отличается от int var, созданного в операторных скобках if. Так же на строчке 24 и 30 есть отдельно стоящие операторные скобки, которые не привязаны ни к функции, ни к оператору if, while, for, switch и так далее. Созданная в них int var тоже отличается от int var функции main().

Теперь давайте всё назовём своими именами. Часть кода между операторными скобками называется пространством имён. Подумайте над названием, оно вам многое скажет. У функции main() есть своё пространство имён (тело функции), в нём есть вложенные пространства имён - у оператора if и отдельно стоящее на строчках 24 и 30. int var, объявленная внутри пространств имён if и на строчке 24, перекрывает int var функции main(), но не удаляет её. Они существуют только внутри своих пространств имён и уничтожаются при выходе из них. Теперь посмотрите на пространство имён строчки 30. В нём не объявлена своя int var, поэтому берётся int var из родительского пространства имён, если такая существует. Если нет - просматривается пространство имён ещё более высокого уровня, и так, пока мы не дойдём до глобального. Разумеется, это делается на этапе компиляции и никак не влияет на производительность программы.

Теперь на счёт вашего вопроса. Если вы хотите, чтобы объект существовал после выхода из пространства имён, в котором он создан, создавайте его динамически (new (C++) или malloc (C), или аналогичные). То есть на этапе выполнения программы, память выделяется из кучи (heap). Если он вам нужен только внутри этого пространства имён, и не требуется после него, создавайте его как обычно. Вот пример:

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
#include <cstdlib>
#include <iostream>
 
using namespace std;
 
char * text = 0; //В глобальном пространстве имён.
                 //ВСЕГДА инициализируйте указатели нулём.
 
void allocText()
{
     text = new char[ 256 ];
     cout << "> ";
     cin.getline( text, 256 ); //ввести текст
     
     // выделенная память будет существовать и после выхода из allocText().
}
 
void coutText()
{
    if( text == 0 )
       cout << "text does not exist\n"; //в данный момент text не существует
    else
       cout << "text: " << text << endl;
}
 
int main(int argc, char *argv[])
{
    coutText();
    
    allocText();
    
    coutText();
    
    // удалить text
    delete [] text;
    text = 0; // после удаления ВСЕГДА обнуляйте указатель
    cout << "[deleted]\n";
    
    coutText();
        
    system("pause > nul");
    return 0;
}
Здесь память выделяется динамически функцией allocText(). Ей же заполняется чем-то вменяемым. coutText() выводит содержимое этой памяти, находя нужный участок памяти через char * text. Если text = 0 (нулевой указатель), то память ещё (или уже) не выделена. Всем этим функциям буфер доступен при помощи указателя, который находится в родительском пространстве имён (глобальном в данном случае, хотя это и не обязательно). Тут есть по крайней мере три замечания:

1) Всегда удаляйте динамически выделенную память сами, за вас это никто не сделает. Иначе будут утечки памяти. Вспомните патч к вашей любимой игрушке, где сказано "увеличена производительность за счёт исправления множественных утечек памяти", после которого игрушка стала жрать на 300 метров оперативки меньше. Вот это оно.

2) Если вы случайно удалите указатель (например, при выходе из его пространства имён), вы потеряете буфер. Вы уже никогда не узнаете, в каком участке памяти лежит ваша драгоценная информация. Ну и, опять же, сделаете утечку памяти.

3) Если память не выделена, предназначенный для неё указатель ВСЕГДА должен быть равен нулю. Во-первых, так вы всегда сможете сказать, выделили ли вы память под что-то или нет. Во-вторых, в случае ошибки, операционка отстрелит вашу программу (Инструкция по адресу "0x0022ff77" обратилась к памяти по адресу "0x00000000". Память не может быть read. Ну или written. Помните? ). Подобная ошибка происходит, когда вы пытаетесь обратиться к участку памяти, не доступному вашей программе. Если в указателе будет адрес, который доступен вашей программе, вы можете затереть свою собственную память. Нулевой адрес никогда вам доступен не будет.
4
9 / 9 / 4
Регистрация: 04.03.2010
Сообщений: 40
12.03.2011, 13:24
talis, большое спасибо за разъяснение. Очень понятно объясняешь С пространством имен был знаком до этого, но возможность использования фигурных скобок для создания нового пространства имен, для меня новость. За это отдельное спасибо
Не понятно только, почему в книжках не говорят про эту особенность указателей.Про замечания очень интересно, также как и интересно все, чего не знал раньше
С++ мне с каждым днем все больше нравится, особенно в сравнении с Delphi.
P.S.: думаю тема скоро перерастет из "Понять указатели" в "Понять С++"
0
 Аватар для Temirlan90
137 / 137 / 25
Регистрация: 30.09.2010
Сообщений: 333
12.03.2011, 21:57
1) Всегда удаляйте динамически выделенную память сами, за вас это никто не сделает. Иначе будут утечки памяти. Вспомните патч к вашей любимой игрушке, где сказано "увеличена производительность за счёт исправления множественных утечек памяти", после которого игрушка стала жрать на 300 метров оперативки меньше. Вот это оно.
talis, Типа в памяти будут хранится лишние данные?
0
Эксперт С++
5058 / 3118 / 271
Регистрация: 11.11.2009
Сообщений: 7,044
12.03.2011, 21:59
Temirlan90, типа система не будет знать, что память приложению уже не нужна и что её теперь можно отдать под более актуальные нужды.
1
 Аватар для Temirlan90
137 / 137 / 25
Регистрация: 30.09.2010
Сообщений: 333
12.03.2011, 22:04
И еще вопрос, в Java и C# это вроде делается на автомате? Я про удаления динамической памяти.

Добавлено через 2 минуты
silent_1991, и в Вашем примере вы показали, что в функцию копируются лишние данные?
0
Эксперт С++
5058 / 3118 / 271
Регистрация: 11.11.2009
Сообщений: 7,044
12.03.2011, 22:08
Вряд-ли вы имеете ввиду мой пример, примеры в основном тут показывал tails.
И да, в Яве и шарпе мусор собирается автоматически.
0
 Аватар для Temirlan90
137 / 137 / 25
Регистрация: 30.09.2010
Сообщений: 333
12.03.2011, 22:13
silent_1991, Я про Real_swap и Image_swap это ведь ваш пример =) В этом исходнике как Я понял, там копируются не нужные данные в функции Image_swap?
Яве и шарпе мусор собирается автоматически
Это лучше или хуже? (Простите за глупый вопрос)
0
Эксперт С++
5058 / 3118 / 271
Регистрация: 11.11.2009
Сообщений: 7,044
12.03.2011, 22:16
Нет, там вообще никакой речи об утечке памяти нету. Там просто было показано, как могут быть использованы указатели.

Добавлено через 2 минуты
Цитата Сообщение от Temirlan90 Посмотреть сообщение
Это лучше или хуже?
Ну, с одной стороны лучше, поскольку вы можете систематически забывать очищать память, и утечки станут критическими. А с другой, когда вы делаете это вручную, то осуществляете более строгий и точный контроль за объектами (можете в любое время удалить объект, когда он станет ненужным). Так что у всего есть и плюсы, и минусы.
1
 Аватар для Temirlan90
137 / 137 / 25
Регистрация: 30.09.2010
Сообщений: 333
12.03.2011, 22:18
C
1
2
3
4
5
6
7
8
void image_swap(int a, int b)
{
    int temp = a;
    a = b;
    b = temp;
 
    printf("In image swap:  \ta = %d\tb = %d\n", a, b);
}
Нет, там вообще никакой речи об утечке памяти нету. Там просто было показано, как могут быть использованы указатели.
Я не про утечку говорю. Вот в этой функции ведь создаются 2 новые переменные? Которые как Я понял Нам не нужны...
0
Эксперт С++
5058 / 3118 / 271
Регистрация: 11.11.2009
Сообщений: 7,044
12.03.2011, 22:21
С чего вы взяли? Что в этой функции, что в real_swap, создаются три переменные, только в данном случае это переменные типа int (a, b, temp), а в случае real_swap - два указателя на int и одна int (тоже a, b (указатели) и temp).
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
12.03.2011, 22:21
Помогаю со студенческими работами здесь

Символьные литералы, указатели и функция. Не могу понять, почему именно так
Доброго времени суток всем! :) Изучаю С++, всегда стараюсь добить до последнего код, чтобы не было непоняток. Но сейчас наткнулся на...

Не могу понять, как "живут" файловые указатели
Не могу понять как живут файловые указатели... Есть у меня файл, и пишу в него 100 интов. Указатель ввода сдвигается на 400, так и надо....

Указатели и указатели на указатели, а также типы данных
Недавно начал изучать Си, перешел с Delphi. Много непонятного и пока процесс идет медленно. Накачал литературы, буду изучать) Щас...

Через указатели на указатели посчитать сумму двух чисел и записать в третье
1. Через указатели на указатели посчитать сумму двух чисел и записать в третье. 2. Написать примитивный калькулятор, пользуясь только...

Почему Лафоре использует указатели на указатели, вместо обмена значениями указателей?
Доброго времени суток! Задался теоретическим вопросом. Читал пример из книги Лафоре ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ В C++,...


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

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Thinkpad X220 Tablet — это лучший бюджетный ноутбук для учёбы, точка.
Programma_Boinc 23.12.2025
Thinkpad X220 Tablet — это лучший бюджетный ноутбук для учёбы, точка. Рецензия / Мнение Это мой обзор планшета X220 с точки зрения школьника. Недавно я решила попытаться уменьшить свой. . .
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Как объединить две одинаковые БД Access с разными данными
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru