С++: правильность и аргументы указателя

Я понимаю, что указатель const можно объявить несколькими способами:

const int * intPtr1; // Declares a pointer that cannot be changed.
int * const intPtr2; // Declares a pointer whose contents cannot be changed.

// EDIT: THE ABOVE CLAIMS ARE INCORRECT, PLEASE READ THE ANSWERS.

Но как насчет тех же принципов в контексте аргументов функции?

Я бы предположил, что следующее избыточно:

void someFunc1(const int * arg);
void someFunc2(int * arg);

Так как someFunc 1 и 2 делают pass-by-value для самого указателя, его невозможно, чтобы someFunc1 изменил значение исходного указателя в данном вызове функции. Чтобы проиллюстрировать:

int i = 5;
int * iPtr = &i;

someFunc1(iPtr); // The value of iPtr is copied in and thus cannot be changed by someFunc1.

Если они верны, тогда нет смысла в том, чтобы объявлять какую-либо функцию с аргументом 'const int * ptr' arg, правильно?

Ответы

Ответ 1

У вас есть это в обратном порядке:

const int * intPtr1; // Declares a pointer whose contents cannot be changed.
int * const intPtr2; // Declares a pointer that cannot be changed.

Следующий const действительно не нужен, и нет причин помещать его в объявление функции:

void someFunc1(int * const arg);

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

Ответ 2

Ну, это не для вызывающего, а для кода внутри someFunc1. Так что любой код внутри someFunc1 обычно не меняет его. как

void someFunc1(int *arg) {
  int i = 9;
  arg = &i;  // here is the issue
  int j = *arg;
}

Давайте рассмотрим некоторые примеры:

1) Просто сделав заостренное значение const

void someFunc1(const int * arg) {
int i = 9;
*arg = i; // <- compiler error as pointed value is const
}

2) Просто сделав указатель const

void someFunc1(int * const arg) {
int i = 9;
arg = &i; // <- compiler error as pointer is const
}

3) Правильный способ использования const, если задействованные переменные могут быть const:

void someFunc1(const int * const arg) {
    int i = 9;
    *arg = i; // <- compiler error as pointed value is const
    arg = &i; // <- compiler error as pointer is const
}

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

EDIT:

  • Даже в декларациях функций хорошая практика объявить const. Это не только повысит удобочитаемость, но также и вызывающий абонент узнает о контракте и имеет больше уверенности в неизменности аргументов. (Это требуется bcoz, вы, как правило, делитесь своими файлами заголовков, поэтому у вызывающего может не быть файла реализации c/cpp).
  • Даже компилятор может указать лучше, если оба объявления и определения синхронизированы.

Ответ 3

У тебя своя логика неправильная. Вы должны прочитать тип назад, поэтому const int * является указателем на const int и int * const является указателем const для int.

Пример:

void foo() {
    int a = 0;
    int b = 0;

    int * const ptrA = &a;
    *ptrA = 1;
    ptrA = &b; ///< Error

    const int * ptrB = &a;
    *ptrB = 1; ///< Error
    ptrB = &b;

    const int * const ptrC = &a;
    *ptrC = 1; ///< Error
    ptrC = &a; ///< Error
}

Чтобы разработать и показать, почему вы хотите, чтобы ваш параметр функции был const int *, вы можете указать вызывающему, что они должны пройти в int, потому что вы как функция хотите изменить значение. Рассмотрим этот код, например:

void someFunc1(const int * arg) {
    // Can't change *arg in here
}

void someFunc2(int * arg) {
    *arg = 5;
}

void foo() {
    int a = 0;
    someFunc1(&a);
    someFunc2(&a);

    const int b = 0;
    someFunc1(&b);
    someFunc2(&b); ///< *** Error here. Must pass in an int not a const int.
}

Ответ 4

Да, вы правы (игнорируя тот факт, что вы ошиблись в них) - нет смысла принимать параметры без ссылки const. Кроме того, нет смысла возвращать значения без ссылки const.

Ответ 5

У вас это неправильно:

const int * intPtr1; // Declares a pointer whose contents cannot be changed.
int * const intPtr2; // Declares a pointer that cannot be changed.

Вообще говоря, легче рассуждать о константе при написании этого выражения несколько иначе: const int* - это тот же тип, что и int const *. В этих обозначениях правила намного яснее, const всегда относится к предшествующему ему типу, поэтому:

int const * intPtr1; // Declares a pointer to const int.
int * const intPtr2; // Declares a const pointer to int.
int const * * const * complexPtr; // A pointer to const pointer to pointer to const int

Когда тип написан с ведущим const, const обрабатывается так, как если бы он был написан после первого типа, поэтому const T* становится T const *.

void someFunc2(int * arg);

Поэтому не избыточно, так как someFunc2 может изменять содержимое arg, а someFunc1 - нет. void someFunc3(int * const arg); будет избыточным (и двусмысленным), хотя