Одинаковы ли arr, * arr, arr [0] и & arr [0] [0] в C? - PullRequest
0 голосов
/ 28 мая 2020

я нахожу похожие вопросы, но не этот ...

если я получил это:

 double B[3][3] = {{1.,0.,0.},{0.,1.,0.},{0.,0.,1.}};

тогда:

B, *B, B[0] и &B[0][0]

одинаковы?

например, если я создаю этот метод:

void suma(double A[][3], double *B, double *C);

int main(int argc, char **argv) {
     double A[3][3] = {{1.,2.,3.},{4.,5.,6.},{7.,8.,9.}};
     double B[3][3] = {{1.,0.,0.},{0.,1.,0.},{0.,0.,1.}};
     double C[3][3];

     suma(A, &B[0][0], C[0]);

     return 0;
}

void suma(double A[][3], double *B, double *C){
    for(int i = 0; i < 3; i++){
        for(int j = 0; j < 3; j++){
            *(C+i*3+j) = A[i][j] + *(B+i*3+j);
        }
    }
}

Я могу вызвать функцию с 4 способы разные, и работают все

Ответы [ 6 ]

3 голосов
/ 28 мая 2020

В C массив в большинстве ситуаций может распадаться на указатель на первый элемент массива. Итак:

  • B имеет тип double[3][3] и распадается на double (*)[3] (указатель на массив из 3 двойников)

  • *B и B[0] идентичны и относятся к типу double[3] (массив из 3-х двойников) и распадаются на double * (указатель на double).

  • &B[0][0] имеет введите double *.

И еще есть параметры функции: В C массивы не могут быть параметрами функции. Таким образом, при использовании в качестве типа параметра функции то, что выглядит как массив, не является массивом, а фактически является указателем, игнорируя первое измерение массива, например:

void foo(double arr[3])
void foo(double arr[])

На самом деле:

void foo(double* arr)

То же самое верно и для многомерных массивов:

void foo(double arr[2][3])
void foo(double arr[][3])

На самом деле:

void foo(double (*arr)[3])

Имея в виду вышеизложенное, давайте go через ваши примеры :

void foo(double p[][3]);
// is actually:
void foo(double (*p)[3]);

foo(A);

A распадается на double (*)[3], который является ожидаемым типом параметра. Так что все в порядке.

void foo(double* p);

foo(&B[0][0])

Здесь тоже нет проблем, вы напрямую передаете указатель на первый двойной элемент.

void foo(double* p)

foo(C[0]);

C[0] имеет тип double[3] и распадается на double*, который является ожидаемым типом параметра. Опять хорошо.

2 голосов
/ 28 мая 2020

Нет, B, *B, B[0] и &B[0][0] не совпадают.

Их типы различаются:

  • B: массив 3 из массива 3 двойных
  • *B: массив 3 двойных
  • B[0]: массив 3 двойных
  • &B[0][0]: указатель на двойные

Когда вы позволяете им распадаться на указатели (четвертый элемент уже является указателем), типы по-прежнему разные:

  • B: указатель на массив 3 из double
  • *B: указатель на double
  • B[0]: указатель на double
  • &B[0][0]: указатель на double

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

Обратите внимание, что последние 3 имеют один и тот же тип. Они полностью эквивалентны. Тип первого содержит больше информации. Он «знает», что есть группы по 3 двойных.

Это различие типов можно показать, запустив следующую программу:

#include <stdio.h>

double B[3][3] = {
    { 1., 0., 0. },
    { 0., 1., 0. },
    { 0., 0., 1. }
};

int main() {
    printf("%p: %zu\n", (void *) B, sizeof(*B));
    printf("%p: %zu\n", (void *) (*B), sizeof(*(*B)));
    printf("%p: %zu\n", (void *) (B[0]), sizeof(*(B[0])));
    printf("%p: %zu\n", (void *) (&B[0][0]), sizeof(*(&B[0][0])));
}

Что для меня выведет:

0x404040: 24
0x404040: 8
0x404040: 8
0x404040: 8

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

1 голос
/ 28 мая 2020

При использовании в выражениях (кроме, например, операнда оператора sizeof):

B - указатель на массив из 3 double, double (*)[3] - Тем не менее, он получает адрес первого элемента первого массива.

*B - это указатель на double, double * - собственно указатель на первый элемент в первом массиве.

B[0] - указатель на double, double * - собственно указатель на первый элемент в первом массиве.

&B[0][0] - указатель на double, double * - фактически указатель на первый элемент в первом массиве.

Там не то. B имеет другой тип. - Однако на самом деле все они имеют одинаковый результат. - Оценка адреса первого элемента в первом массиве.

1 голос
/ 28 мая 2020

B обозначает массив из трех массивов из трех double. При использовании в выражении, отличном от операнда sizeof или унарного &, оно будет автоматически преобразовано в указатель на свой первый элемент. Таким образом, он будет действовать так, как если бы это было &B[0].

В *B, B действует, как указано выше; он действует как &B[0]. Итак, *B - это *&B[0], то есть B[0]. Это обозначает массив из трех double, который является первым таким массивом в B. При использовании в выражении, отличном от операнда sizeof или унарного &, оно будет автоматически преобразовано в указатель на свой первый элемент. Таким образом, он будет действовать так, как если бы это было &B[0][0].

B[0], как описано выше.

&B[0][0] - это адрес первого элемента в первом массиве B .

Таким образом, *B, B[0] и &B[0][0] эквивалентны, за исключением случаев использования в качестве операнда sizeof или унарного &.

Однако B отличается. B - это адрес массива, а остальные - адрес double. Эти адреса относятся к одному и тому же месту в памяти, потому что этот double является первым элементом в массиве, поэтому он начинается в том же месте, где начинается массив. Однако:

  • Они бывают разных типов. B после автоматического преобразования c является указателем на массив, тогда как *B, B[0] и &B[0][0] являются указателями на double. C имеет правила о том, как могут использоваться типы, и вы не должны использовать один тип, если ожидается несовместимый тип.
  • Они могут иметь разные представления. Многие реализации C используют плоское адресное пространство, в котором адреса формируются просто путем нумерации байтов в памяти. Однако у них могут быть более сложные схемы адресации, и значения для B и B[0] (оба после преобразования) могут иметь разные представления и не могут быть взаимозаменяемыми.
  • Они могут вести себя по-разному в выражениях. Например, B+1 и B[0]+1 дадут указатели на разные места, потому что B+1 будет указывать на второй массив в B (который начинается с B[1][0]), тогда как B[0]+1 будет указывать на второй элемент. в B[0] (B[0][1]).
1 голос
/ 28 мая 2020

Я думаю, что когда вы говорите о записи arr, вы имеете в виду использование указателя массива в выражениях. В этом случае он неявно конвертируется (за редкими исключениями, например, при использовании в операторе sizeof) в указатель на свой первый элемент и имеет тип double ( * )[3].

Это выражение *arr возвращает lvalue первого элемента массива, который является элементом arr [0], имеющим тип double[3]. Это обозначение массива снова используется в выражениях и преобразуется в указатель на его первый элемент, имеющий тип double. Это то же самое, что написать &arr[0][0].

Короче говоря, все три выражения дадут одно и то же значение - адрес памяти экстентов, занятой массивом, но имеет разные типы.

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

arr имеет тип double( * )[3]

*arr имеет тип double *.

arr[0] совпадает с *arr и имеет тот же тип double *.

&arr[0][0] совпадает с arr[0] и *arr и имеет тип double *.

Изменить: После того, как вы добавили свой вопрос с программным кодом, тогда в этом вызове

 suma(A, &B[0][0], C[0]);

первое выражение аргумента A имеет тип double( * )[3] второе выражение аргумента имеет тип double *, а третье выражение аргумента также имеет тип double *.

Фактически внутри функции вы переинтерпретировали двумерные массивы B и C как одномерные массивы.

1 голос
/ 28 мая 2020

EDITED : они имеют одинаковое значение.

Этот код C:

#include <stdio.h>


int main()

{

    double arr[3][3] = {{1.,0.,0.},{0.,1.,0.},{0.,0.,1.}};


    printf("arr=%p \n", arr);
    printf("*arr=%p \n", *arr);
    printf("arr[0]=%p \n", arr[0]);
    printf("&arr[0][0]=%p \n", &arr[0][0]);

}

говорит:

arr=0x7ffc17d4dba0 
*arr=0x7ffc17d4dba0 
arr[0]=0x7ffc17d4dba0 
&arr[0][0]=0x7ffc17d4dba0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...