Что означает две звездочки в c
Перейти к содержимому

Что означает две звездочки в c

  • автор:

Указатели и ссылки в языке C++

Указатели представляют собой объекты, значением которых служат адреса других объектов:

  • переменных
  • констант
  • функций
  • других указателей
Объявление указателей

<тип> *<имя_переменной>[,*<имя_переменной>].

Синтаксис объявления указателей аналогичен объявлению переменных, за исключением того, что между типом данных и именем переменной должен быть указан символ «*» («звездочка»).

Инициализация указателей

Указателю можно присвоить адрес объекта, полученный с помощью оператора взятия адреса &. Стоит отметить, что оператор & не возвращает напрямую адрес своего операнда. Вместо этого он возвращает указатель, содержащий адрес.

Указателю нельзя присвоить адрес переменной другого типа. То есть нельзя указателю типа int* присвоить адрес переменной типа double.

Также указателю можно присвоить значение другого указателя.

Указатель также может быть проинициализирован пустым значением. Это можно сделать несколькими способами:

  • использовать значение 0 или макроопределение NULL
  • использовать значение nullptr
  • использовать значение std::nullptr_t (C++ 11)

В некоторых случаях, использование значения 0 в качестве аргумента функции может привести к проблемам, так как компилятор не сможет определить, используется ли нулевой указатель или целое число. Поэтому использование значения nullptr является предпочтительным способом присвоить указателю пустое значение.

Тип std::nullptr_t может иметь только одно значение — nullptr. Использование этого типа поможет в тех редких случаях, когда существуют перегруженные функции и требуется передать нулевой указатель. В этом случае непонятно какую именно функцию нужно будет вызвать. Поэтому в таком случае в функции можно задать аргумент с типом std::nullptr_t.

Напрямую записать адрес в указатель можно только с помощью операций преобразования типов, либо операции reinterpret_cast.

int a = 0; int *p = &a; double v = 0.1; double *pv = &v; char *pc = nullptr;
Разыменование указателей

Для получения значения переменной, на которую ссылается указатель, используется операция разыменования указателя. Эта операция записывается как символ * (звездочка), написанный перед указателем.

int a = 123; int *p = &a; int b = *p; // b присваивается значение 123
Арифметические действия с указателями

С указателем можно производить следующие арифметические действия:

  • сложение и вычитание с целым числом
  • операции инкремента/декремента

При использовании арифметических операций, указатель изменяется на величину кратную размеру типа указателя. Например, если указатель имеет тип 32-разрядного int, то увеличение указателя на 1 приведет к увеличению значения адреса в указателе на 4.

Указатель на указатель

В языке C++ можно объявить указатель, который будет указывать на другой указатель.

Синтаксис объявления такой же, как и у объявления указателя, за исключением того, что ставится два символа * (звездочка).

<тип> **<имя_переменной>[,**<имя_переменной>].

Указатель на указатель работает подобно обычному указателю: его можно разыменовать для получения значения, на которое он указывает. И, поскольку этим значением является другой указатель, для получения исходного значения потребуется выполнить разыменование еще раз. Разыменования можно выполнять последовательно:

int value = 1234; int *p = &value; int **pp = &p; int val = **pp; // 1234

Использовать указатели на указатели может потребоваться, например для создания массива из массивов, и в частности массива из строк.

Язык C++ также позволяет работать с указателями на указатели на указатели, или сделать еще большую вложенность. Их можно объявлять просто увеличивая количество символов * (звездочек). Однако на практике такие указатели используются крайне редко.

Неконстантный указатель на неконстантное значение

int val1 = 10; int val2 = 20; int* ptr = &val1; std::cout 

В этом случае можно изменять как сам указатель, так и значение, на которое он указывает.

Неконстантный указатель на константное значение

const int val1 = 10; const int val2 = 20; const int* ptr = &val1; std::cout 

В этом случае указатель можно изменять. Но само значение, на которое он указывает изменять нельзя.

То же самое поведение можно получить, даже если переменные указаны как неконстантные. Для этого достаточно сам указатель объявить таким образом, чтобы он якобы указывал на константное значение:

int val1 = 10; int val2 = 20; const int* ptr = &val1; std::cout 

Константный указатель на неконстантное значение

int val1 = 10; int val2 = 20; int* const ptr = &val1; std::cout 

В этом случае можно изменять значение, на которое указывает указатель. Но нельзя изменять сам указатель.

Кроме того указатель при объявлении нужно сразу инициализировать.

Константный указатель на константное значение

int val1 = 10; int val2 = 20; const int* const ptr = &val1; std::cout 

В этом случае нельзя менять ни указатель, ни значение, на которое он указывает.

Ссылки

Ссылка - это тип переменной в языке C++, который работает как псевдоним другого объекта или значения. При объявлении ссылки перед её именем ставится символ амперсанда &. Сама же ссылка не может быть пустой, и должна быть обязательно проинициализирована именем переменной, на которую она ссылается. Изменить значение ссылки после инициализации невозможно.

<тип> &<имя_ссылки> = <имя_переменной>[, &<имя_ссылки> = <имя_переменной>].

При создании ссылки на константную переменную, ссылка тоже должна быть создана как константная. Можно также создать константную ссылку на обычную переменную: в этом случае изменить переменную через ссылку не получится.

Любые действия со ссылкой трактуются компилятором как действия, которые будут выполняться над объектом, на который она ссылается.

Ссылки чаще всего используются в качестве параметров в функциях. В этом контексте ссылка-параметр работает как псевдоним аргумента, а сам аргумент не копируется при передаче в параметр. Это в свою очередь улучшает производительность, если аргумент слишком большой или затратный для копирования.

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

int value = 123; int &refval = value; refval = 12345; std::cout 
Ссылки r-value

В стандарте C++11 ввели новый тип ссылок - ссылки r-value. Ссылки r-value - это ссылки, которые инициализируются только значениями r-values. Объявляются такие ссылки, в отличие от обычных, с помощью двух символов амперсанда &&.

<тип> &&<имя_ссылки> = <выражение r-value>[, &&<имя_ссылки> = <выражение r-value>].

Ссылки r-value, в отличие от обычных ссылок, ссылаются не на постоянный, а на временный объект, созданный при инициализации ссылки r-value.

Такие ссылки обладают двумя важными свойствами:

  • продолжительность жизни объекта, на который ссылается ссылка увеличивается до продолжительности жизни самой ссылки
  • неконстантные ссылки r-value позволяют менять значение r-values, на который они ссылаются
int &&ref = 10; ref = ref + 20; std::cout 

Ссылки r-value - позволяют избегать логически ненужного копирования и обеспечивать возможность идеальной передачи (perfect forwarding). Прежде всего они предназначены для использования в высокопроизводительных проектах и библиотеках.

  • Уголок в Вконтакте
  • Уголок в Телеграм
  • Уголок в YouTube
  • Все справочники
  • Справочник по JavaScript
  • Справочник по Ассемблеру
  • Справочник по C++
    • - Типы данных
    • - Переменные
    • - Указатели и ссылки
    • - Массивы
    • - Циклы и ветвления
    • - Перечисления
    • - Структуры и объединения
    • - Классы
    • - Наследование классов
    • - Перегрузка операторов

    Что значит две звездочки у char?

    Author24 — интернет-сервис помощи студентам

    Что значит char c[10]
    объясните пожалуйста пошагово этот код #include <iostream> using namespace std; int main(.

    что значит static char?
    подскажите что означает static char ret;

    Что значит неявное преобразование типа string[] в char[] невозможно?
    using System; using System.Collections.Generic; using System.Linq; using System.Text; using.

    50 / 49 / 29 Регистрация: 11.11.2014 Сообщений: 332 Указатель на строковый двумерный массив.

    Эксперт C

    27706 / 17322 / 3812 Регистрация: 24.12.2010 Сообщений: 38,979 Я бы даже сказал "массив указателей на строки" или "указатель на массив указателей на строки"

    Эксперт С++

    4773 / 3267 / 497 Регистрация: 19.02.2013 Сообщений: 9,046

    ЦитатаСообщение от Байт Посмотреть сообщение

    "массив указателей на строки" или "указатель на массив указателей на строки" Что-то одно другому противоречит.

    ЦитатаСообщение от mihaprad Посмотреть сообщение

    Что значит две звездочки у char?

    В данном случае через argv обеспечивается доступ к аргументам выполняемой программы, argc задает кол-во этих аргументов. Если учесть, что по соглашению в среде си-программистов, char* (указатель на символ) является строкой (которая завершается нулевым символом), то исходную запись можно несколько видоизменить:

    typedef char* string; // задаем вспомогательный тип - строка int main(int argc, string* argv )

    В таком варианте уже должно быть более очевидно, что argv - есть указатель на строки. А учитывая, что типы T* и T[], указанные в списке аргументов функции, эквивалентны, можно представить и в такой варианте:

    int main(int argc, string argv[] )

    Указатель на указатель + динамическое выделение памяти (часть 1)

    Обращаюсь к новичкам, которые только начали изучать указатели: «Если вас заинтересовала эта тема и вы хотите в ней разобраться, что я могу вам сказать — ситуация не из приятных!» ))) Кто бы и как бы усердно и старательно не объяснял вам что к чему, понять указатели на указатели сложно. Сам указатель на указатель содержит в себе адрес, который ссылается на другой адрес, а он, в свою очередь, ссылается на адрес в памяти, где хранятся данные. Вроде бы и можно понять. Но как это применять на практике? Зачем оно надо. Это понять сложнее. А надо «оно», среди прочего, для возможности работы с массивами указателей, которые указывают на память с данными (строками, например). Каждый элемент этого массива — это указатель, который содержит в себе адрес строки (первого элемента символьного массива):

    Снимок

    Наша ситуация усложняется еще и тем, что в данной статье мы постараемся доступно показать, как выделять динамическую память под двумерный массив указателей и как ее освобождать. Ну что, испугались? Тогда начнем разбираться ! Если вы еще слабо знаете тему Указатели, прочтите все таки сначала эту статью. Она поможет вам подготовиться к восприятию темы Указатель на указатель.

    А в данной статье мы будем рассматривать пример, в котором перед нами ставится следующая задача: у нас есть указатель на указатель char **pp (он будет содержать адрес массива указателей на строки) и размер этого массива int size , который изначально равен 0. Нам надо написать функцию, которая будет выделять динамическую память для новых элементов массива указателей и для хранения символов новых строк. Эта функция будет принимать, как параметры, указатель на указатель, размер массива указателей и строку, которую надо будет записать в выделенную под нее память. Чтобы не усложнять задачу, в ней не будет диалога с пользователем. Пять строк мы определим сразу при вызовах функции.

    Если у вас есть возможность, пишите исходный код по мере прочтения. Так будет легче его понять. Детальные объяснения увидите под кодом.

    #include ; #include ; using namespace std; char **AddPtr (char **pp, int size, char *str); //прототип функции int main() < setlocale(LC_ALL, "rus"); int size = 0;//количество указателей на строки char **pp = 0;//указатель на массив указателей, которые содержат адреса строк cout delete [] pp; // потом выделенную под массив указателей return 0; > char **AddPtr (char **pp, int size, char *str) < if(size == 0)< pp = new char *[size+1]; //выделяем память для указателя на строку >else < //если массив уже не пустой, данные надо скопировать во временный массив **copy char **copy = new char* [size+1]; //создаем временный массив for(int i = 0; i < size; i++) //копируем в него адреса уже определенных строк < copy[i] = pp[i]; >//теперь строки хранятся в адресах copy delete [] pp; //освобождаем память, которая указывала на строки pp = copy; //показываем указателю на какие адреса теперь ссылаться > pp[size] = new char [strlen(str) + 1]; //выделяем память на новую строку strcpy(pp[size], str); //и копируем новую строку в элемент pp[size]. return pp; >

    В строке 6 объявляем прототип функции char **AddPtr (char **pp, int size, char *str); . Перед названием функции ставим две звездочки, так как функция будет возвращать указатель на указатель. В главной функции main() все достаточно просто. Создаем указатель на указатель типа char **pp , который изначально ни на что не указывает, и счетчик элементов массива указателей size — строки 12-13. Далее (строки 17 — 30) идет поочередное наращивание массива указателей и добавление в него данных, посредством вызова функции AddPtr() . При этом, каждый раз после вызова функции мы увеличиваем значение size на единицу. Теперь переместимся к самому интересному — к определению функции AddPtr() строки 44 — 66. Как уже говорилось выше, в виде параметров функция будет принимать уже объявленный нами указатель на указатель, счетчик элементов массива указателей и определённую нами строку. При первом вызове, в функцию передаётся нулевое значение счетчика size . Срабатывает if (size == 0) (строки 46 — 48) в котором мы выделяем динамическую память для первого элемента массива указателей pp = new char *[size+1]; . Перед квадратными скобками стоит оператор звездочка * , который показывает компилятору , что нужно выделить динамическую память под один указатель (а не просто под символ char , если бы звездочки не было). If отработал и мы перемещаемся в строку 62. Тут мы «говорим» — пусть 0-й элемент массива указателей (указатель pp[size] ) указывает на массив символов размером [strlen(str) + 1] (размер определённой нами строки + 1 символ для '\n' ). И следующим логичным шагом будет копирование строки, переданной в функцию, в этот выделенный участок памяти — строка 63. И в завершении работы, функция возвращает в программу указатель на указатель (тот самый указатель, который хранит адрес нулевого элемента массива указателей на строки). И наш, объявленный в main() , char **pp теперь будет хранить в себе значение этого адреса, так как вызов функции выглядит так pp = AddPtr(pp, size, "11111111111111111"); (присвоить значение, которое вернет функция). Функция отработала — память выделена, данные внесены.

    Вызываем функцию второй раз — строка 20. При этом вызове уже сработает блок else определённый в строках 49 — 60. У нас уже есть строка, данные которой нам надо не потерять и добавляется еще одна, для которой надо создать новый указатель в массиве указателей, выделить динамическую память и записать туда данные. Поэтому создаем временную копию нашего указателя и выделяем память уже под два элемента массива указателей char **copy = new char* [size+1]; . Копируем в него указатель на перовую строку (нулевой элемент массива указателей) — copy[i] = pp[i]; . Освобождаем память, которая указывала на первую строку. Так как это массив указателей (пусть даже пока с одним элементом) чтобы освободить занимаемую им память, надо перед именем указателя поставить квадратные скобки — delete [] pp; . Нам эта память больше не нужна, так как на нее уже указывает copy[0] . И показываем указателю pp на какой новый участок памяти надо теперь ссылаться — строка 59 . Так — первая строка у нас сохранена и на нее теперь указывает pp[0] . И теперь мы снова переходим к строкам 62 — 63, где выделяется память для второй строки и строка копируется в этот участок памяти.

    Таких вызовов функций у нас пять. Постепенно массив указателей растет, а новые строки заполняются данными. Чтобы убедиться, что все работает правильно и все данные сохранены, показываем все строки на экран с помощью цикла for — строки 32-33. Как видите, мы обращаемся к элементам массива указателей. А так как они ссылаются на адреса строк (на 0-е элементы символьных массивов), на экран выводятся соответствующие строки.

    Перед завершением работы программы, нам надо освободить память занимаемую строками. Это мы реализуем с помощью цикла:

    for(int i = 0; i

    Так освобождаем динамическую память, на которую ссылаются указатели из массива указателей. А далее освобождаем память, выделенную под сам массив указателей — строка 40.

    CppStudio.com

    ~~~~~Добавляем указатели на пять строк и заполняем строки данными~~~~~
    11111111111111111
    22222222222222222
    33333333333333333
    44444444444444444
    55555555555555555

    Условие этой задачи мы выполнили. Надеюсь, вы оценили главное преимущество использования указателей вместо обычных массивов. При входе в программу мы не знаем, сколько строк нам будет необходимо и какой объем памяти они будут занимать. Но мы не объявляли несколько десятков символьных массивов с размером [много памяти] (а вдруг пригодятся, если пользователь будет вводить много длинных строк). Вместо этого у нас получился один динамический массив указателей на строки, память для которых так же выделяется динамически.

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

    К сожалению, для данной темы пока нет подходящих задач. Если у вас есть таковые на примете, отправте их по адресу: admin@cppstudio.com. Мы их опубликуем!

    Что значит звёздочка после типа указателя?

    *pointer - разыменование указателя,
    а что значить Звёздочка после типа?
    int*
    Например:

    double* ptd; ptd = (double *)malloc(10 * sizeof(double));

    Зачем перед malloc стоит (double*), если это пишется в sizeof(double), Для чего после double стоит *, ведь это символ разыменования, а что мы разыменуем?

    • Вопрос задан более трёх лет назад
    • 8507 просмотров

    1 комментарий

    Простой 1 комментарий

    В первом случае * относится не к double , а к ptd и означает то же самое, что и double *ptd; (а также double*ptd; или double * ptd; ).
    Подобная запись может служить источником заблуждений, т. к.
    double* ptd, x;
    означает то же, что и
    double *ptd; double x; ,
    а не
    double *ptd; double *x; .
    (Действия подобного второму объявлению можно достичь при помощи typedef :

    typedef double *pdouble; pdouble ptd, x;

    Решения вопроса 2

    myjcom

    что значить Звёздочка после типа?

    Для чего после double стоит *, ведь это символ разыменования, а что мы разыменуем?

    В данном случае звездочка не является оператором а относится к типу.

    Зачем перед malloc стоит (double*)

    Выделяет память в байтах (возвращает указатель на void), и ничего не знает о том как вы ими будете распоряжаться.

    N.B. В современном Си такой необходимости нет.

    приведение к типу (double*) сообщает компилятору, что вы хотите.

    Ответ написан более трёх лет назад
    Нравится 4 2 комментария
    Zakhar Delov @Del0v Автор вопроса
    Спасибо за ответ и ссылки.

    myjcom

    CityCat4

    Внимание! Изменился адрес почты!

    Первая запись обьявляет переменную ptd указателем на double. Компилятор ожидает, что в данной переменной хранится адрес памяти ячейки, который указывает на переменную типа double, хотя сразу после обьявления в ней находится мусор - неопределенное значение.
    Вторая запись выделяет память под хранение переменных.
    sizeof(double) - получить размер переменной double для текущей платформы
    malloc - выделить память под десять переменных размером в double - обычная вещь для массива например.
    (double *) - привести тип возвращаемого значения (malloc всегда возвращает void) к типу "указатель на double"

    Надо сказать, что данный код можно использовать только в том случае, если сразу за malloc() идет чтение или инициализация массива. Потому что malloc() вернул область памяти, заполненную случайными данными. Для гарантии, что там не будет мусора лучше использовать calloc:
    ptd = (double *) calloc(sizeof(double), 10);

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *