Пока я передаю B[2][3] 2D Array в указателе функции, ожидается распад.
Однако, похоже, это происходит избирательно только в том, что я передаю в качестве параметра синтаксически.
Вот мой код:
#include<stdio.h>
void printArray(int B[][3])
{
printf("sizeof(B) is : %u\n", sizeof(B)); //When arrays are passed to functions they decay into pointers.
printf("sizeof(B[0]) is : %u\n", sizeof(B[0])); //But here I get an array-pointer.Why there is no array to pointer decay here?See results Below.
}
int main(int argc, char* argv[])
{
int B[2][3] = { {2,3,6},{4,5,8} };
printf("sizeof(B) is : %u\n", sizeof(B));
printf("sizeof(B[0]) is : %u\n", sizeof(B[0]));
printArray(B);
return 0;
}
Вот мои результаты:
sizeof(B) is : 24
sizeof(B[0]) is : 12
sizeof(B) is : 4
sizeof(B[0]) is : 12
C:\Users\Strakizzz\Desktop\C\My Codeschool Projects\Pointers in C?C++\Debug\FunctionsPointersScopeMayhem.exe (process 20660) exited with code 0.
To automatically close the console when debugging stops, enable Tools->Options->Debugging->Automatically close the console when debugging stops.
Press any key to close this window . . .
Я ожидал, что при печати sizeof(B[0]) из вызываемой функции я получу 4 байта, как и когда я вычислил sizeof(B) в той же функции:
void printArray().
.Почему я получаю такие результаты, что происходит под капотом?
Поскольку вы знаете, что массив распадается на указатель на свой первый элемент, когда он передается в качестве аргумента функции, пожалуйста, подумайте о том, какие ваши ожидания от sizeof(B) внутри функции должны быть более глубокими.
Нет, я все еще получаю те же результаты. Кроме того, я программирую в vs2022, и спецификатор беззнакового целого числа — %u согласно msdn.
Я ожидал получить 24 байта для sizeof(B) или 4 байта для sizeof(B[0]) в результате printArray(). Если оба дадут мне 4 байта, это будет распад массива. Однако sizeof(B[0]) вызывается из printArray в результате дает мне 12 байт. Последние два значения, полученные из printArray, сбивают с толку.
sizeof и оператор адреса & принимают l-значения, поэтому затухание указателя не происходит (поскольку затухание указателя происходит, когда l-значения распадаются до r-значений).
sizeof не является функцией. Это оператор, даже если вы используете с ним (). Его операнд — это один из случаев, когда затухание указателя массива не происходит. B в printArray имеет тип указателя на массив из 3 целых чисел. Итак, B[0] имеет тип массива из 3 целых чисел. Так что 12 верно.
Рэймонд, мне нужно время, чтобы подтвердить твой ответ, я ищу значения lvalue и rvalue.
Отвечает ли это на ваш вопрос? Размер C переданного массива
@Sotiris Re: «Я ожидал получить 24 байта для sizeof(B)» - подумайте об этом: какой размер имеет указатель после распада на указатель на первый элемент?
12 действительно правильный Ави. Но я указал параметр int B[][3] в printArray().
Тед, я согласен, странно то, что это происходит только с параметром B[][3], тогда как B[0] в той же функции дает правильный результат 12.
@Sotiris Почему это удивительно? Какой тип B[0]? Расчет: это int[3]. Учитывая, что sizeof(int) есть 4 и у вас их 3, 12 кажется разумным.
Рэймонд, я думаю, что и B, и B[0] являются lvalue. Оба указывают местоположение в памяти, поскольку у меня есть 2d-массив.
Хорошо, тогда что такое B Ted? Мой массив — B[2][3].
? Таким образом, параметр B функции printArray() является указателем на массив из трех целых чисел. Это делает B[0] массивом из 3 целых чисел, который не превращается обратно в указатель при использовании с sizeof. Я не понимаю "Но" в вашем комментарии. См. Преобразование массива в указатель примерно на 3/4 пути вниз. И обратите внимание: «Когда тип массива используется в списке параметров функции, он преобразуется в соответствующий тип указателя... фактический тип параметра функции — указатель». тип"
@Sotiris Re: «Что такое Би Тед?» - Вы объявили B как int B[][3], так что мне не совсем понятен вопрос, понимаете ли вы распад. Объявление с таким же успехом можно было бы записать int(*B)[3], что является тем же объявлением - следовательно, разыменование, как в B[0], даст вам int[3].
void printArray(int B[][3]) — это обозначение void printArray(int (*B)[3]) в C. B в main() — это int B[2][3], но параметр B в printArray это другая переменная, имеющая то же имя. это int (*B)[3]. Указатель, а не массив. Вот почему sizeof B функции printArray() — это размер указателя, а не массива каких-либо размеров.
Хорошо, это имеет больше смысла: создается НОВАЯ переменная для передачи массива по ссылке. Верно?
@Sotiris Re: «создается НОВАЯ переменная» - переменные не создаются. Переменные — это абстракции, которые мы используем в языках «высокого» уровня, чтобы упростить создание программ.
Вы имеете в виду: sizeof( int )?
Так что же тогда происходит?? переменная-указатель int (*B)[3] имеет тот же адрес, что и...?
@Джеймс sizeof(int[3])
@Sotiris Поскольку B является указателем на int[3], размер, который вы получите при разыменовании B, будет равен размеру одного элемента, умноженному на количество элементов. sizeof(int) * 3
Мне нужно время, чтобы изучить это... но мой вопрос не о результатах main(), а только о результатах printArray(), которые являются двумя последними.
@Sotiris Эти результаты станут ясны, если вы просто подумаете о затухании указателя. int[3][4] распадается на int(*][4] и т. д.
@Джеймс Вы обращаетесь к чему-то конкретному?
@TedLyngmo sizeof(int[3]) == 12, он хочет получить номер =4.
@James То, что ОП хочет от испорченного указателя, и то, что возможно, - это две совершенно разные вещи. Вы согласны? Я до сих пор не понимаю, о чем был ваш комментарий warning: 'sizeof'... и кто это был предполагаемый читатель.
Параметр int (*B)[3] имеет собственный адрес, отличный от других (если только он не оптимизирован для хранения в регистре процессора). Параметры функции по сути являются локальными переменными, которые возникают во время вызова функции в модели языка C. Мне не ясно, в чем заключается возражение против «создания НОВОЙ переменной». Для общего понимания это звучит хорошо, хотя я допускаю, что может быть более точный и правильный способ выразить это.
@Sotiris Вы можете получить это предупреждающее сообщение, если заметили его warning: 'sizeof' on array function parameter 'B' will return size of 'int (*)[3]' [-Wsizeof-array-argument] .
Всем спасибо за помощь, на мой вопрос получен ответ.
См. также мои ответы здесь, где я говорю об этом: Как передать многомерный массив функции в C и C++ и Передача массива в качестве аргумента функции в C.



«Распад» массива, который происходит во многих контекстах, в которых используется тип массива, заменяет тип массива указателем на первый элемент массива. Таким образом, int a[5] распадается на int *.
Для двумерного массива, то есть массива массивов, это работает следующим образом: int B[2][3] распадается на int (*)[3], который является указателем на массив из трех int. Это потому, что B — это массив из двух элементов, каждый из которых представляет собой массив из трех int.
В printArray компилятор автоматически настраивает параметр B на тип int (*B)[3]. B — это указатель, который на вашей машине имеет размер 4, а B[0] имеет тип int [3], поэтому он имеет размер sizeof(int) * 3, который на вашей машине равен 12. Итак, printArray печатает 4 и 12.
Примечание о переносимости: использование %u для печати значения sizeof, имеющего тип size_t, не является переносимым. Вместо этого используйте %zu, который является переносимым.
Таким образом, в B[0 не происходит затухания, только в параметре функции? Спасибо за примечание о переносимости.
Да, распад происходит только на верхнем уровне.
Спасибо, Том, это именно то, что мне нужно было подтвердить.
За исключением случаев, когда это операнд операторов sizeof, _Alignof или унарный & или строковый литерал, используемый для инициализации массива символов в объявлении, выражение типа «N-элемент T» будет преобразовано или «распадом». ", к выражению типа "указатель на T", и значением выражения будет адрес первого элемента массива.
Учитывая декларацию
int B[2][3] = { {2,3,6},{4,5,8} };
в вызове функции
printArray(B);
выражение B превращается из типа «2-элементный массив из 3-элементного массива int» (int [2][3]) в «указатель на 3-элементный массив int» (int (*)[3]), и на самом деле printArray получает значение указателя, эквивалентное &B[0].
В контексте объявления параметра функции T a[N] и T a[] «приспосабливаются» к T *a, поэтому прототип
void printArray(int B[][3])
эквивалентно
void printArray(int (*B)[3])
Как отмечалось выше, выражение массива не будет распадаться, если оно является операндом sizeof, поэтому sizeof B в main даст вам размер всего массива (2 * 3 * sizeof (int)), а sizeof B[0] даст вам размер первого подмассива (3 * sizeof (int)).
Однако в функции printArrayB на самом деле является указателем, а не массивом, поэтому sizeof B дает вам размер указателя. Однако sizeof B[0] по-прежнему дает размер первого подмассива, поскольку тип выражения B[0] — int [3].
Спасибо, что нашли время ответить. Ответ на мой вопрос заключался в том, что распад происходит только тогда, когда массив передается как параметр функции, поэтому B[0] остается int [3], а B[][3] становится int ( *Б)[3].
Несвязано:
%u— неверный спецификатор преобразования дляsizeof. Должно быть%zu