Вот что я обнаружил в период обучения:
#include<iostream>
using namespace std;
int dis(char a[1])
{
int length = strlen(a);
char c = a[2];
return length;
}
int main()
{
char b[4] = "abc";
int c = dis(b);
cout << c;
return 0;
}
Таким образом , в переменной int dis(char a[1])
, то , [1]
кажется , не делать ничего и не работает на
всех, потому что я могу использовать a[2]
. Также как int a[]
или char *a
. Я знаю, что имя массива - это указатель и как передать массив, поэтому моя загадка не в этой части.
Я хочу знать, почему компиляторы допускают такое поведение ( int a[1]
). Или у него есть другие значения, о которых я не знаю?
typedef
с типом массива. Так что «распад на указатель» в типах аргументов не просто синтаксический сахар заменить[]
с*
, это действительно происходит через систему типа. Это имеет реальные последствия для некоторых стандартных типов, подобных тому,va_list
которые могут быть определены с типом массива или не-массивом.int dis(char (*a)[1])
. Затем передать указатель на массив:dis(&b)
. Если вы хотите использовать функции C, которых нет в C ++, вы также можете говорить такие вещи, какvoid foo(int data[static 256])
иint bar(double matrix[*][*])
, но это совсем другая баня червей.Ответы:
Это причуда синтаксиса для передачи массивов функциям.
На самом деле невозможно передать массив в C. Если вы напишете синтаксис, который выглядит так, как будто он должен передавать массив, на самом деле вместо него передается указатель на первый элемент массива.
Поскольку указатель не содержит информации о длине, содержимое вашего
[]
в списке формальных параметров функции фактически игнорируется.Решение разрешить этот синтаксис было принято в 1970-х годах и с тех пор вызвало много путаницы ...
источник
void foo(int (*somearray)[20])
синтаксис. в этом случае на вызывающих сайтах принудительно устанавливается 20.[]
не игнорируются в многомерных массивах, как показано в ответе pat. Поэтому необходимо было включить синтаксис массива. Кроме того, ничто не мешает компилятору выдавать предупреждения даже для одномерных массивов.void foo(int (*args)[20]);
Кроме того, строго говоря, C не имеет многомерных массивов; но у него есть массивы, элементами которых могут быть другие массивы. Это ничего не меняет.Длина первого измерения игнорируется, но длина дополнительных измерений необходима, чтобы компилятор мог правильно вычислить смещения. В следующем примере
foo
функции передается указатель на двумерный массив.Размер первого измерения
[10]
игнорируется; компилятор не помешает вам выполнить индексацию с конца (обратите внимание, что формальный элемент требует 10 элементов, а фактический предоставляет только 2). Однако размер второго измерения[20]
используется для определения шага каждого ряда, и здесь формальное значение должно совпадать с фактическим. Опять же, компилятор не помешает вам индексировать конец второго измерения.Смещение байта от основания массива до элемента
args[row][col]
определяется:Обратите внимание, что если
col >= 20
, то вы фактически индексируете следующую строку (или конец всего массива).sizeof(args[0])
, возвращается80
на мою машину гдеsizeof(int) == 4
. Однако, если я попытаюсь принятьsizeof(args)
, я получаю следующее предупреждение компилятора:Здесь компилятор предупреждает, что он даст только размер указателя, на который распался массив, а не размер самого массива.
источник
args
. В этом случае первый элемент args - это «массив из 20 целых чисел». Указатели включают информацию о типе; передается «указатель на массив из 20 целых чисел».int (*)[20]
тип; "указатель на массив из 20 int".Проблема и как ее решить в C ++
Проблема была объяснена широко по погладить и Мэтта . Компилятор в основном игнорирует первое измерение размера массива, фактически игнорируя размер переданного аргумента.
С другой стороны, в C ++ вы можете легко преодолеть это ограничение двумя способами:
std::array
(начиная с C ++ 11)Ссылки
Если ваша функция пытается только прочитать или изменить существующий массив (не копируя его), вы можете легко использовать ссылки.
Например, предположим, что вы хотите иметь функцию, которая сбрасывает массив из десяти,
int
устанавливая для каждого элемента значение0
. Вы можете легко сделать это, используя следующую сигнатуру функции:Это не только будет работать нормально , но также обеспечит соблюдение размеров массива .
Вы также можете использовать шаблоны, чтобы сделать приведенный выше код универсальным :
И наконец, вы можете воспользоваться
const
правильностью. Рассмотрим функцию, которая печатает массив из 10 элементов:Применяя
const
квалификатор, мы предотвращаем возможные модификации .Стандартный библиотечный класс для массивов
Если вы считаете приведенный выше синтаксис некрасивым и ненужным, как и я, мы можем выбросить его и использовать
std::array
вместо него (начиная с C ++ 11).Вот код после рефакторинга:
Разве это не прекрасно? Не говоря уже о том, что трюк с общим кодом, который я научил вас ранее, все еще работает:
Не только это, но вы также получаете копирование и перемещение семантики бесплатно. :)
Чего же ты ждешь? Иди и используй
std::array
.источник
Это забавная особенность C, которая позволяет вам эффективно стрелять себе в ногу, если вы так склонны.
Я думаю, причина в том, что C - всего лишь на шаг выше ассемблера. Проверка размера и аналогичные функции безопасности были удалены, чтобы обеспечить максимальную производительность, что неплохо, если программист очень прилежен.
Кроме того, назначение размера аргументу функции имеет то преимущество, что, когда функция используется другим программистом, есть вероятность, что он заметит ограничение размера. Простое использование указателя не передает эту информацию следующему программисту.
источник
Во-первых, C никогда не проверяет границы массива. Не имеет значения, являются ли они локальными, глобальными, статическими, параметрами или чем угодно. Проверка границ массива означает больше обработки, и C должен быть очень эффективным, поэтому проверка границ массива выполняется программистом при необходимости.
Во-вторых, есть уловка, которая позволяет передавать массив по значению функции. Также возможно возвращать массив по значению из функции. Вам просто нужно создать новый тип данных с помощью struct. Например:
Вы должны получить доступ к таким элементам: foo.a [1]. Дополнительный ".a" может выглядеть странно, но этот трюк добавляет отличную функциональность языку C.
источник
Чтобы сообщить компилятору, что myArray указывает на массив не менее 10 целых чисел:
Хороший компилятор должен предупреждать вас, если вы обращаетесь к myArray [10]. Без ключевого слова static число 10 вообще ничего не значило бы.
источник
[static]
позволяет компилятору предупреждать, если вы вызываетеbar
с расширениемint[5]
. Она не диктует , что вы можете получить доступ вbar
. Бремя ответственности полностью ложится на вызывающего абонента.error: expected primary-expression before 'static'
никогда не видел такого синтаксиса. это вряд ли будет стандартный C или C ++.Это хорошо известная «особенность» C, переданная в C ++, потому что C ++ должен правильно компилировать код C.
Проблема возникает по нескольким причинам:
Вы могли бы сказать, что массивы на самом деле не поддерживаются в C (это не совсем так, как я говорил ранее, но это хорошее приближение); массив действительно рассматривается как указатель на блок данных и доступен с использованием арифметики указателей. Поскольку C НЕ имеет какой-либо формы RTTI, вы должны объявить размер элемента массива в прототипе функции (для поддержки арифметики указателей). Это даже «вернее» для многомерных массивов.
В любом случае все вышесказанное больше не соответствует действительности: p
Большинство компиляторов современного C / C ++ сделать поддержку проверки границ, но стандарты требуют, чтобы быть отключена по умолчанию (для обратной совместимости). Например, достаточно свежие версии gcc выполняют проверку диапазона времени компиляции с помощью «-O3 -Wall -Wextra» и полную проверку границ времени выполнения с помощью «-fbounds-check».
источник
struct MyStruct s = { .field1 = 1, .field2 = 2 };
) для инициализации структур, потому что это гораздо более ясный способ инициализации структуры. В результате самый последний код C будет отклонен стандартными компиляторами C ++, потому что большая часть кода C будет инициализировать структуры.C не только преобразует параметр типа
int[5]
в*int
; учитывая объявлениеtypedef int intArray5[5];
, он также преобразует параметр типаintArray5
в*int
. В некоторых ситуациях такое поведение, хотя и нечетное, полезно (особенно с такими вещами, какva_list
определенное вstdargs.h
, которое некоторые реализации определяют как массив). Было бы нелогично разрешать в качестве параметра тип, определенный какint[5]
(игнорируя размер), но не позволяющийint[5]
указывать напрямую.Я считаю, что C обработка параметров типа массива абсурдна, но это следствие усилий взять специальный язык, большие части которого не были особенно четко определены или продуманы, и попытаться придумать поведенческие спецификации, которые согласуются с тем, что существующие реализации сделали для существующих программ. Многие из причуд C имеют смысл, если рассматривать их в этом свете, особенно если учесть, что, когда многие из них были изобретены, большая часть языка, который мы знаем сегодня, еще не существовала. Насколько я понимаю, в предшественнике C, называемом BCPL, компиляторы не очень хорошо отслеживали типы переменных. Объявление
int arr[5];
было эквивалентноint anonymousAllocation[5],*arr = anonymousAllocation;
; после того, как распределение было отложено. компилятор не знал и не заботился о том,arr
был указателем или массивом. При обращении к нему какarr[x]
или к*arr
нему он будет рассматриваться как указатель независимо от того, как он был объявлен.источник
Единственный вопрос, на который еще нет ответа, - это актуальный вопрос.
В уже приведенных ответах объясняется, что массивы не могут быть переданы по значению функции ни в C, ни в C ++. Они также объясняют, что параметр, объявленный как
int[]
, рассматривается как имеющий типint *
, и что такой функцииint[]
можно передать переменную типа .Но они не объясняют, почему никогда не было сделано ошибки явным образом указать длину массива.
Почему последнее из них не является ошибкой?
Причина в том, что это вызывает проблемы с определениями типов.
Если бы было ошибкой указать длину массива в параметрах функции, вы не смогли бы использовать это
myarray
имя в параметре функции. И поскольку некоторые реализации используют типы массивов для стандартных типов библиотек, таких какva_list
, и все реализации требуются для созданияjmp_buf
типа массива, было бы очень проблематично, если бы не было стандартного способа объявления параметров функции с использованием этих имен: без этой возможности не было бы не быть переносимой реализацией таких функций, какvprintf
.источник
Компиляторам разрешено проверять, совпадает ли размер переданного массива с ожидаемым. Компиляторы могут предупредить о проблеме, если это не так.
источник