У меня есть массив, который уже находится в куче (динамической) памяти. Это не очень сложно понять, но записать его в файл с помощью функции fwrite() может быть немного сложно понять, если это также двойной указатель, как я вижу в Интернете, что я не понимаю с точки зрения логики, что это то, что я пытаюсь прояснить здесь.
int ** A;
A = new int* [randomSize];
A[0] = new int [randomSize];
Например, одиночный указатель на fwrite() будет выглядеть так:
fwrite(&A[0], sizeof(teststruct), 54, dogfile);
Итак, если массив с одним указателем выглядит так в функции fwrite(), я думаю, что логика массива с двойным указателем должна выглядеть примерно так:
fwrite(&A[0][0], sizeof(teststruct), 54, dogfile);
Вот как я это понимаю, исходя из логики работы функции fwrite().
Однако я видел в Интернете двойной указатель, используемый с fwrite() для печати в файл с одним массивом блоков (A[0]), а не с массивом двойных блоков (A[0][0]). Это выглядело как:
fwrite(A[0], sizeof(teststruct), 54, dogfile);
Может ли кто-нибудь, кто разбирается в этом, объяснить мне логику этого, пожалуйста?
&A[0]
— это то же самое, что и A
в большинстве контекстов. Следовательно, &A[0][0]
совпадает с A[0]
.
Fwrite() хочет указатель на (адрес) записываемых данных.
В этом коде:
int A[size];
A — это массив значений int:
[0] [1] [2] [...]
+---+---+---+-----+
A = | x | x | x | ... |
+---+---+---+-----+
Итак, этот код будет работать:
fwrite(&A[0], sizeof(int), size, dogfile);
Как и этот код:
fwrite(A, sizeof(int), size, dogfile);
Потому что ссылка на фиксированный массив только по его имени даст (т.е. он распадается на) указатель на его 1-й элемент в различных контекстах, например, в параметре функции или присваивании переменной.
[0] [1] [2] [...]
+---+---+---+-----+
A = | x | x | x | ... |
+---+---+---+-----+
^
|
fwrite(buffer, ...)
Теперь в вашем коде:
int ** A;
A = new int* [randomSize];
A[0] = new int [randomSize];
Это создает массив указателей int*, где каждый int* указывает на свой собственный массив значений int:
[0] [1] [2] [...]
+---+---+---+-----+
A = | x | x | x | ... |
+---+---+---+-----+
| | | [0] [1] [2] [...]
| | | +---+---+---+-----+
+---|---|--------> | x | x | x | ... |
| | +---+---+---+-----+
| |
| | [0] [1] [2] [...]
| | +---+---+---+-----+
+---|--------> | x | x | x | ... |
| +---+---+---+-----+
|
| [0] [1] [2] [...]
| +---+---+---+-----+
+--------> | x | x | x | ... |
+---+---+---+-----+
Где:
A[0]
— это 1-й int*
в этом массиве, а &A[0]
— указатель на этот int*
A[0][0]
— это 1-й int
, на который указывает 1-й int*
в A
, а &A[0][0]
— указатель на этот int
fwrite(&A[0], sizeof(int), randomSize, dogfile);
Вы будете писать мусор в файл, потому что A — это массив int* указателей, и вы записываете значения самих этих указателей, а не значения int, на которые они указывают.
[0] [1] [2] [...]
+---+---+---+-----+
A = | x | x | x | ... |
+---+---+---+-----+
^ | [0] [1] [2] [...]
| | +---+---+---+-----+
| +---------------> | x | x | x | ... |
| +---+---+---+-----+
+---+
|
fwrite(buffer, ...)
Однако этот код работает:
fwrite(A[0], sizeof(int), randomSize, dogfile);
Потому что A[0] — это указатель на 1-й int в другом массиве.
[0] [1] [2] [...]
+---+---+---+-----+
A = | x | x | x | ... |
+---+---+---+-----+
| [0] [1] [2] [...]
| +---+---+---+-----+
+---------------> | x | x | x | ... |
+---+---+---+-----+
^
|
+-----------------+
|
fwrite(buffer, ...)
1: то есть взять начальный адрес Array, увеличить его на Index количество элементов (то есть на Index умножить на sizeof(element type) количество байтов), а затем разыменовать его для доступа к элементу по этому адресу.
2: означает взять начальный адрес Array, увеличить его на Index количество элементов, разыменовать его для доступа к элементу, а затем взять адрес этого элемента.
В данном случае A[0] — это указатель, потому что у вас есть массив указателей.