Массив передается по значению или по ссылке?

Я точно знаю, что

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 изменит адрес указатель