Почему при передаче указателя в функцию её параметр получает копию указателя, а не ссылку на сам оригинальный указатель?

Привет! <br/> <pre><code>int input(int *arr, int *n) {
  • 17 июня 2026 г. 10:00
Ответы на вопрос 3
Короткий ответ: потому что в C (и по умолчанию в C++) все параметры функций передаются по значению. Указатель — это просто переменная, которая хранит адрес; при передаче вы копируете его значение (адрес). Чтобы изменить саму переменную-указатель в вызывающем коде, нужно передать её адрес (указатель на указатель) или в C++ — ссылку на указатель.

Пояснение на примерах.

1) Передача указателя по значению (копия указателя):
```c
void set_null(int *p) {
    p = NULL;        // меняется локальная копия p
}

int *q = malloc(sizeof(int));
set_null(q);
// q всё ещё указывает на выделенную память
```
Здесь p внутри функции — отдельная переменная, и присваивание p = NULL не влияет на q.

2) Изменить оригинал — передать адрес указателя (int **):
```c
void set_null_ptr(int **pp) {
    *pp = NULL;      // меняем значение указателя в вызывающем коде
}

int *q = malloc(sizeof(int));
set_null_ptr(&q);
// теперь q == NULL
```

3) В C++ можно проще — ссылка на указатель:
```cpp
void set_null_ref(int *&p) {
    p = nullptr;     // меняем сам указатель в вызывающем коде
}
```

Важно: если вы передаёте указатель по значению, то изменять через него можно содержимое памяти, на которое он указывает:
```c
void set_value(int *p) {
    *p = 42;         // изменяет данные по адресу, поэтому видимо в вызывающем коде
}

int x = 0;
set_value(&x);
// x == 42
```

Ещё момент: при передаче массива в функцию имя массива преобразуется в указатель на первый элемент — опять же копируется адрес. Поэтому внутри функции вы можете менять элементы массива, но не «переименовать» сам массив в вызывающей функции.

Итог: указатель передаётся как значение, потому что так устроена семантика параметров в C (и по умолчанию в C++). Если нужно изменить сам указатель у вызывающего — передайте указатель на указатель (int **) или в C++ — ссылку на указатель (int *&), либо верните новый указатель из функции и присвойте его в вызывающем коде.
<code class="cpp">arr = (int*)malloc(sizeof(*arr) * *n);</code> Здесь вы меняете значение переменной arr, но это изменённое значение не выйдет за пределы функции. Нужно передавать в функцию ссылку на переменную и изменять значение по этой ссылке. То, что переменная arr сама является ссылкой ничего принципиально не меняет. <br/> <pre><code class="cpp">int input(int **arr, int *n) {
    if (scanf("%d", n) != 1) return -1;
    *arr = (int*)malloc(sizeof(int) * *n);
    if (*arr == NULL) return -1;

    for (int i = 0; i &lt; *n; ++i) {
        if (scanf("%d", (*arr)[i]) != 1) return -1;
    }

    return 0;
}

int main(void) {
    int *arr = NULL;
    int n = 0;

    input(&amp;arr, &amp;n);</code></pre>
Тут ключевой момент: <code>arr[i] = что-то</code> и <code>arr = malloc()</code> — принципиально разные операции. Первое меняет данные по адресу (туда, куда указатель смотрит), это видно из main. Второе меняет сам указатель, а он в функции — локальная копия. <br/> <br/> Поэтому <code>int **arr</code> и <code>*arr = malloc()</code> — передаёшь адрес самого указателя и меняешь его через разыменование.
Похожие вопросы