Массив передается по значению или по ссылке?
Я точно знаю, что
function(int *a); function(int a[]);
в C одинаковы, функция (int a []) будет переведена в функцию (int * a)
int *a = malloc(20);
int b[] = {1,2,3,4,5};
Эти два не совпадают, первый - указатель, второй - массив. Что происходит, когда я вызываю функцию (b)? (Function (int * a))
Я знаю, что b находится в стеке, так как передается эта функция?
Во-вторых, строки:
char *c1 = "string";
char c2 [] = "string";
В этом случае я не знаю, где c1, и я полагаю, что c2 находится в стеке.
Предположим, что теперь функция: function (char * c), которая совпадает с функцией (char c []), что происходит, когда я вызываю функцию (c1) и функцию (c2), строки будут передано по ссылке или значению?
Ответы
Ответ 1
Здесь есть решающий момент: все действительно передается по значению, например, это передаст копию a
в foo()
(которая является указателем на некоторую память):
int *a = malloc(20);
foo(a);
Вот почему, если вы делаете что-то вроде этого в foo()
, это не меняет указатель a
в main()
, а изменяет локальную копию:
foo(int *a)
{
a = NULL; /*changes local copy of the pointer*/
}
Другими словами, вы можете использовать foo()
локальную копию a
, чтобы изменить память, на которую указывает "a", но не изменять то, что a
указывает на main()
.
Теперь, чтобы передать что-то "по ссылке", вы передаете копию указателя на указатель на функцию (что-то вроде a- > b- > memory):
int *a = malloc(20);
foo(&a);
Поэтому, когда вы назначаете его в foo()
для изменения указателя в main()
:
foo(int **a)
{
*a = NULL; /*changes the pointer in main */
}
Теперь, чтобы ответить на некоторые ваши другие вопросы, когда вы используете имя массива, оно преобразуется в указатель на первый элемент массива:
int *a = malloc(20);
int b[] = {1,2,3,4,5};
foo(a);
foo(b);
Два последних вызова функций эквивалентны тем, что оба передают указатель на первый элемент некоторой памяти, разница в памяти для a
выделяется в куче, однако выделяется память b
в стеке.
Наконец, строки похожи друг на друга, поскольку тот же принцип применяется, но первый из них является строковым литералом и должен быть определен как const
, и вы не должны пытаться его модифицировать, но вы можете изменить второй:
const char *c1 = "string";
char c2 [] = "string";
Ответ 2
Из K & R2
When an array name is passed to a function, what is passed is the
location of the initial element. Within the called function, this
argument is a local variable, and so an array name parameter is a
pointer, that is, a variable containing an address.
Объявление аргумента char c2 []
является просто синтаксическим сахаром для char* c2
.
Все в C передается как значение. Для дальнейшего объяснения этого используйте ссылку .
Кроме того, у Eli Bendersky есть отличная статья , обсуждающая то же самое.
Ответ 3
Когда вы передаете массив функции, ожидающей указателя, ссылка массива "распадается" на указатель на его первый элемент.
http://c-faq.com/aryptr/aryptrequiv.html
Фактический переход к функции осуществляется по ссылке, которая выполняется компилятором, помещая адрес (указатель) в стек или в регистр CPU.
Строки не являются исключением, поскольку они представляют собой просто массивы символов.
Ответ 4
int *a = malloc(20);
===========
|a|----------------> | | | | | |
===========
a is a pointer 20 bytes on heap each of 4 bytes
to int on stack
int b[] = {1,2,3,4,5};
=================================
| 1 | 2 | 3 | 4 | 5 |
=================================
b[0] b[1] b[2] b[3] b[4]
Full array is on stack
char *c1 = "string";
char c2 [] = "string";
In case of "string" it string literal and allocated on readonly memory.
In first case , |c1| ----> |s|t|r|i|n|g|\0|
In second case , ============================================
| s | t | r | i | n | g | \0 |
============================================
c2[0] c2[1] c2[2] c2[3] c2[4] c2[5] c2[6]
The full char array is on stack.
Ответ 5
Массивы всегда передаются по ссылке (указатель).
Если вы хотите передать их с использованием значения, инкапсулируйте их в struct
. Это приведет к работе с копией исходного массива. Пример, показанный ниже:
Первый аргумент foo - это объект struct, который содержит копию массива. Второй аргумент - это ссылка на массив.
#include <stdio.h>
struct a{
int i[2]; // An array encapsulated in struct.
};
void foo(struct a b, int j[]){
b.i[0]=30;
j[1]=30;
}
int main(){
struct a c;
c.i[0]=10;
c.i[1]=20;
printf("Before %d %d\n",c.i[0], c.i[1]);
foo(c, c.i);
printf("After %d %d \n",c.i[0], c.i[1]);
return 0;
}
$ ./a.out
Before 10 20
After 10 30
Ответ 6
Массивы передаются по ссылке почему? потому что вы передали адрес указателя по значению, поэтому изменение этого указателя изменится в указанной памяти, а не на адрес указателя, поэтому изменение указателя не заменит исходный указатель на функцию, поэтому при вводе a = ANOTHER_POINTER внутри этой функции он не потеряет переданный массив после использования функции
int a [] эквивалентно int a, а при вызове функции foo (int a) он получит адрес указателя
теперь, если вы хотите изменить указатель, вы можете передать адрес указателя по ссылке foo (int * & a), поэтому теперь изменение адреса указателя a = ANOTHER_POINTER изменит адрес указатель