Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.71/103: Рейтинг темы: голосов - 103, средняя оценка - 4.71
2 / 5 / 1
Регистрация: 13.09.2015
Сообщений: 100
1

Что означает запись int (*a)[10]?

15.02.2017, 21:48. Показов 21052. Ответов 24
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
C++
1
int (*a)[10];
Что создаёт компилятор если я забиваю это?
2
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
15.02.2017, 21:48
Ответы с готовыми решениями:

Что означает запись int **a
int * a-указатель на целочисленное.А это что- int **a ?

Что означает запись std::int?
Подскажите пожалуйста увидела в одном примере перед вектором было std::int я почему то думала что...

Что означает эта запись? int(*px)[5] = 0; cout << px;
Добрый день товарищи, недавно впал в ступор. Есть запись вида int(*px) = 0; cout &lt;&lt; px+2; Я...

Нюансы синтаксиса: что означает запись arr[(int)(u*10)]++; ?
arr++; скажите пожалуйста что это может означать ? arr - масив количеств попаданий псевд случ...

24
63 / 63 / 39
Регистрация: 18.11.2016
Сообщений: 562
15.02.2017, 21:50 2
Думаю, указатель на массив.
0
Эксперт С++
8739 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
15.02.2017, 22:06 3
Цитата Сообщение от ChadloveMary Посмотреть сообщение
Что создаёт компилятор если я забиваю это?
указатель на массив, который содержит 10 интов.
1
2 / 5 / 1
Регистрация: 13.09.2015
Сообщений: 100
15.02.2017, 22:17  [ТС] 4
hoggy, если это так, то как обращаться к элементам? Следующий код не компилится:
C++
1
2
3
4
int (*a)[10];
    (*a)[0] = 1;
    (*a)[1] = 2;
    cout << (*a)[0] << endl;
0
Эксперт .NET
5871 / 4748 / 2940
Регистрация: 20.04.2015
Сообщений: 8,361
15.02.2017, 22:18 5
Цитата Сообщение от hoggy Посмотреть сообщение
указатель на массив
А мне больше массив указателей напоминает. Не?
0
2549 / 1208 / 358
Регистрация: 30.11.2013
Сообщений: 3,826
15.02.2017, 22:31 6
Кажись hoggy, как всегда прав
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    #include <iostream>
    using namespace std;
 
 
    int main()
    {
        int b[10];
        b[ 0 ] = 7;
        b[ 1 ] = 13;
 
        int ( *a )[ 10 ];     
        a = &b;           
        auto c = &b;
 
        cout << sizeof( a ) << endl;
        cout << (*a)[0] << endl;
        cout << (*a)[1] << endl;
        cout << std::is_same< decltype(a), decltype(c) >::value << endl;
 
    }
0
Эксперт .NET
5871 / 4748 / 2940
Регистрация: 20.04.2015
Сообщений: 8,361
15.02.2017, 22:36 7
rikimaru2013,
А в чем отличие от такого варианта?
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
    using namespace std;
    int main()
    {
        int b[10];
        b[ 0 ] = 7;
        b[ 1 ] = 13;
 
        int *a=b;
 
        cout << sizeof( a ) << endl;
        cout << a[0] << endl;
        cout << a[1] << endl;
 
    }
0
2549 / 1208 / 358
Регистрация: 30.11.2013
Сообщений: 3,826
15.02.2017, 22:38 8
Даценд, вы кастите вниз указатель на массив к указателю на участок памяти. При касте вниз не узнать никогда размер переданного указателя на память. Если очень попросите - поищу у себя ссылку на тему, где обсуждалось про каст-вниз указателя на массив
1
70 / 70 / 35
Регистрация: 06.07.2016
Сообщений: 415
15.02.2017, 22:40 9
Цитата Сообщение от ChadloveMary Посмотреть сообщение
Что создаёт
Указатель на массив из 10 значений типа int.
Источник - http://algcourse.cs.msu.su/wp-... 8-2016.pdf , страница 9.
0
Evg
Эксперт CАвтор FAQ
21279 / 8301 / 637
Регистрация: 30.03.2009
Сообщений: 22,659
Записей в блоге: 30
15.02.2017, 22:52 10
Цитата Сообщение от Даценд Посмотреть сообщение
А в чем отличие от такого варианта?
Например, как выполнится операция "a++"

C
int (*a)[10];
int *b;
 
int main (void)
{
  a++;
  b++;
  return 0;
}
Код
$ gcc t.c -O1 -S
$ cat t.s
...
        addl    $40, _a
        addl    $4, _b
...
Одна штука того, на что указывает указатель "a", имеет размер 40 байт (десять по четыре), а одна штука того, на что указывает указатель "b", имеет размер 4 байта

Добавлено через 3 минуты
До кучи: Указатель на массив char и прочее
8
Эксперт С++
8739 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
15.02.2017, 22:54 11
Цитата Сообщение от Даценд Посмотреть сообщение
А в чем отличие от такого варианта?
массив неявно кастится к указателю на его самый первый элемент.
ну а дальше уже идет работа с обычным указателем.

Добавлено через 49 секунд
Цитата Сообщение от rikimaru2013 Посмотреть сообщение
вы кастите вниз указатель на массив к указателю на участок памяти.
он скастил массив по значению к указателю на первый элемент.
а не указатель на массив.
1
2 / 5 / 1
Регистрация: 13.09.2015
Сообщений: 100
15.02.2017, 23:00  [ТС] 12
Вроде понял, что это указатель на массив из 10 интов. Но как всё-таки обращаться к этим элементам?
0
2549 / 1208 / 358
Регистрация: 30.11.2013
Сообщений: 3,826
15.02.2017, 23:17 13
Цитата Сообщение от hoggy Посмотреть сообщение
он скастил массив по значению к указателю на первый элемент.
а не указатель на массив.
Для учебников обучение С++ указатель на первый элемент это и есть указатель на статический массив. Я вот тоже первый раз услышал, что есть реальный указатель на массив. А вы придираетесь!
0
Вездепух
Эксперт CЭксперт С++
11696 / 6375 / 1724
Регистрация: 18.10.2014
Сообщений: 16,071
15.02.2017, 23:35 14
Цитата Сообщение от ChadloveMary Посмотреть сообщение
как обращаться к элементам? Следующий код не компилится:
C++
1
2
3
4
int (*a)[10];
(*a)[0] = 1;
(*a)[1] = 2;
cout << (*a)[0] << endl;
Это как это? Этот код компилируется без проблем: http://coliru.stacked-crooked.... 5d22a8741f

Другое дело, что указатель не проинициализирован, т.е. содержит мусор. Соответственно доступ через такой указатель заставляет программу падать. Формально, компилятор, конечно имеет право отказаться компилировать программу, поведение которой не определено.

Проинициализируйте указатель и все будет в порядке

C++
1
2
3
4
5
int b[10];
int (*a)[10] = &b;
(*a)[0] = 1;
(*a)[1] = 2;
cout << (*a)[0] << " " << b[0] << endl;
http://coliru.stacked-crooked.... a724b2aa01

Добавлено через 5 минут
Присутствующим должно быть понятно, что

C++
1
int (*a)[10];
это то же самое, что

C++
1
2
typedef int A[10];
A *a;
т.е. ничего из ряда вон выходящего тут нет. Это обычный указатель.
0
2549 / 1208 / 358
Регистрация: 30.11.2013
Сообщений: 3,826
15.02.2017, 23:40 15
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
должно быть понятно
Мне к примеру не понятно зачем указатель на массив, если при передаче по значению статический массив не копируется, и передаётся ссылкой. Зачем указатель если дефакто все привыкли к ссылке на статический массив. Можете навести пример где это масс-хев или решает какуе-то проблему, что не решить другим способом? Даёт преимущества?
0
Вездепух
Эксперт CЭксперт С++
11696 / 6375 / 1724
Регистрация: 18.10.2014
Сообщений: 16,071
16.02.2017, 00:12 16
Лучший ответ Сообщение было отмечено rikimaru2013 как решение

Решение

Цитата Сообщение от rikimaru2013 Посмотреть сообщение
Можете навести пример где это масс-хев или решает какуе-то проблему, что не решить другим способом? Даёт преимущества?
Как раз таки наоборот! Передача массива по указателю на его элемент - это специализированный трюк, предназначенный для решения очень специфической задачи: поддержки возможности передачи массивов разного размера. При этом размер массива следует передавать отдельно.

Но если вас не интересует передача массивов разного размера, т.е. размер массива всегда фиксирован, то передавать его надо именно по указателю на массив (или, в С++, по ссылке на массив). Благодаря такой передаче компилятор сможет произвести контроль типов и убедиться, что размер массива правилен.

Например (искусственный пример), мы работаем с трехмерной графикой и точка у нас задается массивом из трех int координат. Мы хотим написать функцию трансформации координат точки. Как нам ее объявить? Так

C++
1
2
3
4
5
6
7
8
enum { X, Y, Z };
 
void shift_point(int point[], const int vector[])
{
   point[X] += vector[X];
   point[Y] += vector[Y];
   point[Z] += vector[Z];
}
или так

C++
1
2
3
4
5
6
void shift_point(int (*point)[3], const int (*vector)[3])
{
   (*point)[X] += (*vector)[X];
   (*point)[Y] += (*vector)[Y];
   (*point)[Z] += (*vector)[Z];
}
?

Оба варианта будут работать и генерировать один и тот же код.

Но!

В первом случае вы сможете сделать так

C++
1
2
3
4
5
int vasya = 0, petya = 0;
shift_point(&vasya, &petya);
 
int point[5] = { 0 }, vector[125] = { 0 };
shift_point(point, vector);
что является грубейшими ошибками, компилятором однако не замеченными.

А во втором случае такой номер не пройдет. Вам придется передавать указатели именно на массивы int[3] и больше ни на что. Вот ради этого контроля типов и используются указатели вроде int (*a)[10].

----

Отдельно стоит заметить, что работая с многомерными массивами в С или С++ вы сами того не замечая (или замечая) сталкиваетесь именно с такими указателями:

C++
1
2
3
double a[10][20] = { 0 };
...
a[i][j] = 5;
Выражение a в этом контексте имеет тип double (*)[20] и именно к нему вы применяете оператор [i].

Передавая массив в функцию как

C++
1
void foo(char a[20][40]);
вы передаете туда именно указатель типа char (*)[40].
8
70 / 70 / 35
Регистрация: 06.07.2016
Сообщений: 415
16.02.2017, 00:18 17
Цитата Сообщение от Evg Посмотреть сообщение
Одна штука того, на что указывает указатель "a", имеет размер 40 байт (десять по четыре), а одна штука того, на что указывает указатель "b", имеет размер 4 байта
То есть , можно сделать вот так :
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
 
int main()
{
    constexpr size_t CAPACITY = 10;
    int (*pointer_array)[CAPACITY];
    int array[CAPACITY];
    pointer_array = &array;
 
    for(int counter = 0 ; counter < CAPACITY; counter++)
    {
       std :: cout <<  ((*pointer_array)[counter] = counter) << '\t';
    }
 
    return 0;
}
Вы в прошлой теме, на которую сослались,писали что в данном случае - array будет трактоваться не как адрес первого элемента, как мы привыкли считать, а именно как адрес на переменную типа "массив из CAPACITY элементов". Как это понимает компилятор?
И что конкретно происходит на аппаратном уровне при вызове
C++
1
(*pointer_array)[counter] = counter
?
То есть мы разыменовываем указатель, получаем блок,на который он указывает, а затем обращаемся в этом блоке к "подблоку" размера int с индексом counter, как при работе с массивом?
0
2549 / 1208 / 358
Регистрация: 30.11.2013
Сообщений: 3,826
16.02.2017, 00:23 18
Цитата Сообщение от Notoriously Посмотреть сообщение
То есть , можно сделать вот так :
Если уж используете статический массив можно использовать все прелести его
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
using namespace std;
 
 
int main()
{
    constexpr size_t CAPACITY = 10; 
    int array[ CAPACITY ] = {0};
 
    for( const auto& it : array )
        cout << it << endl;
 
}
1
Evg
Эксперт CАвтор FAQ
21279 / 8301 / 637
Регистрация: 30.03.2009
Сообщений: 22,659
Записей в блоге: 30
16.02.2017, 20:26 19
Цитата Сообщение от Notoriously Посмотреть сообщение
Как это понимает компилятор?
Тип переменной array есть "int[10]". Тип выражения "&array" есть "int(*)[10]" и он совпадает с типом переменной pointer_array

Цитата Сообщение от Notoriously Посмотреть сообщение
И что конкретно происходит на аппаратном уровне при вызове
Цитата Сообщение от Notoriously Посмотреть сообщение
То есть мы разыменовываем указатель

Не по теме:

Эти два абзаца можно не читать, их тут описал только для полноты картины

Термин "разыменование указателя" по своей сути есть только в языке. В машине нет понятия типа, тип есть только в компиляторе. Компилятор, имея на руках тип, просто строит какую-то машинную операцию. Если переменные имеют тип "int", то глядя на выражение "a = b + c" компилятор построит операции чтения из памяти на целочисленный регистр, затем операцию целочисленного сложения, затем операцию записи в память из целочисленного регистра. Если у переменных тип float, то компилятор построит операции чтения из памяти на вещественных регистр, затем операцию вещественного сложения, затем операцию записи в память из вещественного регистра. Таким образом в коде программы (и в процессоре) нет никакого явного понятия типа

Поэтому всё то, что происходит при разыменовании указателя происходит внутри компилятора. Если переменная имеет тип int*, то операция "*p" приведёт к чтению переменной в регистр и дальнейшему использованию регистра в качестве адреса для операций чтения памяти в целочисленный регистр (или записи в обратную сторону). Если переменная имеет тип float*, то операция "*p" приведёт к чтению переменной в регистр и дальнейшему использованию регистра в качестве адреса для операций чтения памяти в вещественный регистр (или записи в обратную сторону)



Когда речь идёт об указателе на массив, то номинально компилятор работает с ним точно так же, как и с указателем на int или float. Т.е. как бы происходит чтение указателя в регистр, затем чтение из указателя массива в виртуальный 40-байтный регистр - отработали операцию языка (*pointer_array). Потом к значению этого виртуального 40-байтного регистра применяется операция [counter] (условно декомпозиция элемента массива) и выделяет из этих 40 байт нужное 4-байтное значение. Но поскольку 40-байтных регистров нет, то внутри компилятора вся эта цепочка виртуальных операций как бы схлопывается и строится код, который читает значение указателя, а потом читает 4 байта из адреса "указатель + counter * 4". Т.е. с точки зрения построенного машинного кода уже невозможно понять, что в исходнике было написано - работы с обычным указателем или работа с указателем на массив. Технически оба варианта приводят к одному и тому же коду

В принципе, TheCalligrapher уже всё это выше пояснил. Принципиальное различие между двумя вариантами в том, что в одном компилятор может проконтролировать выход за границу массива (поскольку после разыменования как бы образуется массив с известным размером), а в другом - нет
1
70 / 70 / 35
Регистрация: 06.07.2016
Сообщений: 415
16.02.2017, 23:34 20
Цитата Сообщение от Evg Посмотреть сообщение
поскольку после разыменования как бы образуется массив с известным размером
Да, вроде бы понятно, спасибо.
Цитата Сообщение от Evg Посмотреть сообщение
всё то, что происходит при разыменовании указателя происходит внутри компилятора
Пользуясь случаем, хотел бы попросить совета по поводу литературы именно по этой тематике - даже не знаю, как ее назвать. "Как компьютер понимает то, что я ему предлагаю", что ли. Я хочу знать, как он это интерпретирует, во что превращает мои выражения, что он может понять лучше, что хуже, где компилятору придется за меня сделать какую-то работу и как ему с этим помочь.
У меня нет такой дисциплины, все в один голос говорят, что это и не нужно "пока". Но я так не думаю. Даже на уровне простейших программ мне хотелось бы быть уверенным, что я всё понимаю правильно. Для меня абстракция - хорошо, но ее же и так хватает. Сейчас постепенно читаю "Пильщиков. Программирование на языке ассемблера IBM PC", но без набора АСМ-го кода, просто внимательно и с расстановкой пытаюсь понять, как это всё там происходит.
0
16.02.2017, 23:34
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
16.02.2017, 23:34
Помогаю со студенческими работами здесь

Нюансы синтаксиса: что означает запись vector<int*> a и vector <int>*a ?
Часто встречается вот такая запись: vector&lt;int&gt; a; это понятно что. Массив объектов int А вот...

Что означает ошибка too few arguments to function ‘int my_func(int, int)’ (язык C++)?
В чём проблема? #include &lt;iostream&gt; int my_func (int i, int j){ int z; z=i+j; return...

Что означает эта строка? int _tmain(int argc, _TCHAR* argv[])
Всем привет, до этого пользовался DEV, решил пересесть на Visual Studio 2010 Express C++. С++ еще...

Что означает int pos = 0; int value = 5; в данном коде?
#include&lt;conio.h&gt; #include&lt;stdio.h&gt; #include&lt;math.h&gt; void mass(int y); void main() { ...


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

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru