Похоже, вы не понимаете, как объявлять и использовать массив из string
и массив указателей на string
.
string part1[ARRAY_SIZE] = { "...", "...", ...
Вы объявляете part1
как массив строк . Но тогда, кажется, есть некоторая путаница в том, как передать этот массив в printpart1()
. Вы предоставляете объявление как:
void printpart1(..., string *printpart1[18]) {
Где printpart1
указывает, что параметр printpart1
будет массивом указателей на строку. Затем вы вызываете printpart1()
как:
printpart1(ARRAY_SIZE, &part1PTR);
Где вы объявили part1PTR
как указатель на строку . Передавая адрес из part1PTR
, вы предоставляете printpart1()
указатель для указателя на string
.
Чтобы получить фактическую строку для печати, сначала необходимо разыменовать printpart1
(например, *printpart1
), чтобы получить указатель на строку перед применением любого смещения и разыменования, например, *(*printpart1 + i)
.
Например, следующее обеспечит желаемый вывод строки:
void printpart1(int length, string *printpart1[18]) {
int dummy;
for (int i = 0; i < length; i++)
cout << *(*printpart1 + i) << endl;
cin >> dummy;
}
(примечание : *(*printpart1 + 1)
эквивалентно (*printpart1)[i]
. Все, что имеет больше смысла) Вам обычно используется вторая форма)
Просто передайте массив
Теперь все это чрезмерно усложняет то, что должно быть так просто, как передача самого массива в printpart1()
, например если вы измените свою функцию на:
void printpart1(int length, string *printpart1) {
int dummy;
for (int i = 0; i < length; i++)
cout << printpart1[i] << endl;
cin >> dummy;
}
Вы больше не нуждаетесь в part1PTR
и можете просто вызвать вашу функцию с помощью:
printpart1 (ARRAY_SIZE, part1);
Посмотрите вещи, продумайте их и позвольте мне знать, если у вас есть дополнительные вопросы.
Я не знаю, есть ли какой-нибудь способ ответить на этот вопрос. Это просто то, чему вы научитесь на собственном опыте. Вам просто нужно спросить себя, насколько распространенной может быть потенциальная проблема, и высказать свое мнение. Также учтите, что у вас не обязательно есть , чтобы всегда писать защитный код. Иногда допустимо просто отметить любые потенциальные проблемы в документации по вашему коду.
В конечном счете, я думаю, что это просто то, в чем человек должен следовать своей интуиции. Нет правильного или неправильного способа сделать это.
Все, что пользователь вводит прямо или косвенно, вы должны всегда проверить работоспособность. Кроме того, несколько assert
здесь и там не повредят, но вы все равно ничего не сможете сделать с сумасшедшими программистами, редактирующими и взламывающими ваш код! -)
Я склонен изменять степень защиты, которую я устанавливаю в свой код, в зависимости от языка. Сегодня я в основном работаю на C ++, поэтому мои мысли двигаются в этом направлении.
При работе на C ++ не может быть достаточно защитного программирования. Я отношусь к своему коду так, как будто я охраняю ядерные секреты, и все остальные программисты стремятся их получить. Утверждения, выбросы, взлом шаблонов ошибок времени компилятора, проверка аргументов, устранение указателей, углубленный анализ кода и общая паранойя - все это честная игра. C ++ - злой чудесный язык, который я люблю и очень не доверяю.
Я не поклонник термина «защитное программирование». Мне он предлагает такой код:
void MakePayment( Account * a, const Payment * p ) {
if ( a == 0 || p == 0 ) {
return;
}
// payment logic here
}
Это неправильно, неправильно, неправильно, но я, должно быть, видел это сотни раз. Функция никогда не должна была вызываться с нулевыми указателями, и совершенно неправильно их спокойно принимать.
Правильный подход здесь спорен, но минимальное решение состоит в том, чтобы сработать с шумом либо с помощью утверждения, либо путем выдачи исключения.
Изменить: Я не согласен с некоторыми другими ответами и комментариями здесь - я согласен не думайте, что все функции должны проверять свои параметры (для многих функций это просто невозможно). Вместо этого я считаю, что все функции должны документировать приемлемые значения и указывать, что другие значения приведут к неопределенному поведению.
Если вы работаете над общедоступными API-интерфейсами компонента, то стоит провести тщательную проверку параметров. Это привело меня к привычке проводить валидацию везде. Это ошибка. Весь этот код проверки никогда не тестируется и потенциально делает систему более сложной, чем она должна быть.
Теперь я предпочитаю проверять с помощью модульного тестирования.
Моя личная идеология: защищенность программы должна быть пропорциональна максимальной наивности / незнанию потенциальных пользователей.
Защита от разработчиков, использующих ваш код API, ничем не отличается от защиты от обычных пользователей.
Кроме того, больше нечего делать, кроме как убедиться, что ваше приложение хорошо восстанавливается в случае проблемы, и что вы всегда предоставляете разработчику обширную информацию, чтобы что они понимают, что происходит.
Я думаю, вам следует задать вопрос, создаете ли вы также тесты. Вы должны защищаться в своем коде, но, как указал JaredPar, я также считаю, что это зависит от языка, который вы используете. Если это неуправляемый код, вы должны быть крайне оборонительными. Если это удастся, я думаю, у вас есть немного места для маневра.
Если у вас есть тесты, и какой-то другой разработчик пытается уничтожить ваш код, тесты не пройдут . Но опять же, это зависит от тестового покрытия вашего кода (если оно есть).
Защитное программирование - это только один из способов составления контракта с помощью разработки по контракту способа кодирования.
Два других способа
Конечно, вы не должны защищаться от всех безумных вещей , которые может сделать разработчик, но затем вы должны указать, в каком контексте он будет делать то, что ожидается, используя предварительные условия .
//precondition : par is so and so and so
function doSth(par)
{
debug.assert(par is so and so and so )
//dostuf with par
return result
}
Я пытаюсь написать код, который является более чем оборонительным, но прямо враждебным. Если что-то пойдет не так, и я смогу это исправить, я исправлю. если нет, бросьте или передайте исключение и сделайте его проблемой для кого-то еще. Все, что взаимодействует с физическим устройством - файловая система, соединение с базой данных, сетевое соединение, следует считать ненадежным и подверженным сбоям. предвидеть эти неудачи и улавливать их имеет решающее значение
Когда у вас есть такой образ мышления, ключ к тому, чтобы быть последовательным в своем подходе. вы ожидаете вернуть коды состояния, чтобы сообщить о проблемах в цепочке вызовов, или вам нравятся исключения. смешанные модели убьют вас или, по крайней мере, заставят выпить. сильно. если вы используете чужой API, затем изолируйте эти вещи в механизмы, которые ловят / сообщают в терминах, которые вы используете. используйте эти интерфейсы упаковки.
Системы должны иметь хорошо спроектированные границы, в которых происходит защитная проверка. Должно быть решение о том, где пользовательский ввод проверяется (на какой границе) и где другие потенциальные защитные проблемы требуют проверки (например, сторонние точки интеграции, общедоступные API-интерфейсы, взаимодействие с системой правил или разные блоки, написанные разными командами программистов. ). Более защитная проверка, чем это, во многих случаях нарушает DRY и просто увеличивает стоимость обслуживания с очень небольшой выгодой.
При этом, есть определенные моменты, в которых нельзя быть слишком параноиком. Необходимо очень строго защищать возможность переполнения буфера, повреждения данных и подобных проблем.
Недавно у меня был сценарий, в котором данные, вводимые пользователем, распространялись через интерфейс удаленного фасада, затем через локальный интерфейс фасада, затем через какой-то другой класс, чтобы наконец добраться до метода, в котором они фактически использовались. Я задавал себе вопрос: Когда должно быть проверено значение? Я добавил код проверки только в последний класс, где фактически использовалось значение. Добавление других фрагментов кода проверки в классы, лежащие на пути распространения, было бы для меня слишком защитным программированием. Единственным исключением может быть удаленный фасад, но я его тоже пропустил.
Если здесь речь идет о том, как защищать код от будущих (возможно, злонамеренных или некомпетентных) сопровождающих, то есть предел тому, что вы можете сделать. Обеспечение выполнения контрактов через покрытие тестами и либеральное использование утверждения ваших предположений, вероятно, лучшее, что вы можете сделать, и это должно быть сделано таким образом, чтобы в идеале не загромождать код и не усложнять работу для будущих незлых сопровождающих код. Утверждения легко читать и понимать, и они дают понять, каковы предположения данного фрагмента кода, поэтому они обычно являются отличной идеей.
Защитное кодирование действий пользователя - это совсем другая проблема, и подход, который я использую думать, что пользователь хочет меня достать. Каждый ввод проверяется настолько тщательно, насколько я могу. и я прилагаю все усилия, чтобы мой код был безопасным - стараюсь не сохранять какое-либо состояние, которое не проверено строго, исправляйте там, где можете, выходите изящно, если не можете, и т. д. Если вы просто подумаете обо всех глупостях, которые могут быть совершенное в вашем коде внешними агентами, это поможет вам сформировать правильное мышление.
Кодирование с защитой от другого кода, такого как ваша платформа или другие модули, - это то же самое, что и пользователи: они хотят вас достать. ОС всегда будет менять ваш поток в неподходящее время, сети всегда уходят в неподходящее время, и в целом зло изобилует за каждым углом. Вам не нужно кодировать каждую потенциальную проблему - затраты на обслуживание могут не окупать повышения безопасности - но думать об этом не повредит. И обычно это не так
Хороший вопрос, я колебался между проверкой работоспособности и отказом от нее. Это ситуация 50/50
, я бы, вероятно, выбрал золотую середину, где я бы только "Bullet Proof" любые подпрограммы, которые:
(a) Вызываются из более чем одного места в проекте
( b) имеет логику, которая ВЕРОЯТНО изменится
(c) Вы не можете использовать значения по умолчанию
(d) процедура не может быть корректно «неудачна»
Darknight