Доступ к массиву за пределы не дает ошибки, почему?

Используйте LWP:: UserAgent вместо этого:

use strict;
use warnings;

use LWP::UserAgent;

my %query_hash = (spam => 'eggs', foo => 'bar baz');

my $ua = LWP::UserAgent->new();
my $resp = $ua->get("http://www.foobar.com", %query_hash);

print $resp->content;

Это заботится о кодировании для Вас.

, Если Вы хотите более универсальное решение для кодирования, см. HTML:: Объекты .

РЕДАКТИРОВАНИЕ: URI:: Escape является лучшим выбором.

157
задан Rakete1111 3 August 2017 в 15:23
поделиться

13 ответов

Добро пожаловать в лучший друг каждого программиста C / C ++: Undefined Behavior .

Есть много вещей, которые не указаны в языковом стандарте по разным причинам. Это один из них.

В общем, всякий раз, когда вы сталкиваетесь с неопределенным поведением, может произойти что угодно . Приложение может дать сбой, оно может зависнуть, оно может выбросить ваш привод CD-ROM или заставить демонов вылезти из вашего носа. Он может отформатировать ваш жесткий диск или по электронной почте всех ваших порно к своей бабушке.

Это, возможно, даже, если вы действительно повезли, появляется , чтобы работать правильно.

Язык просто говорит о том, что должно произойти, если вы доступ к элементам в границах массива. Неизвестно, что произойдет, если вы выйдете за пределы поля. Может показаться, что сегодня работает на вашем компиляторе, но это не законный C или C ++, и нет никакой гарантии, что он по-прежнему будет работать при следующем запуске программы. Или что он не перезаписал важные данные даже сейчас, и вы просто не столкнулись с проблемами, которые он вызовет - пока.

Что касается , почему там. Это не проверка границ, есть пара аспектов ответа:

  • Массив - это остаток C. Массивы C настолько примитивны, насколько это возможно. Просто последовательность элементов с непрерывными адресами. Нет проверки границ, потому что это просто раскрывает необработанную память. Реализация надежного механизма проверки границ была бы почти невозможной в C.
  • В C ++ проверка границ возможна для типов классов. Но массив по-прежнему остается обычным C-совместимым. Это не класс. В дальнейшем, C ++ также основан на другом правиле, которое делает проверку границ неидеальной. Руководящий принцип C ++ - «вы не платите за то, что не используете». Если ваш код правильный, вам не нужна проверка границ, и вы не должны быть вынуждены платить за накладные расходы на проверку границ во время выполнения.
  • Итак, C ++ предлагает std :: vector шаблон класса, который позволяет и то, и другое. Оператор [] разработан, чтобы быть эффективным. Стандарт языка не требует, чтобы он выполнял проверку границ (хотя и не запрещает этого). Вектор также имеет функцию-член at () , которая гарантированно выполняет проверку границ. Итак, в C ++ вы получаете лучшее из обоих миров, если используете вектор. Вы получаете производительность, подобную массиву, без проверки границ, и вы получаете возможность использовать доступ с проверкой границ, когда захотите.
326
ответ дан 23 November 2019 в 21:42
поделиться

при объявлении массива int [2]; вы резервируете 2 области памяти по 4 байта каждая (32-битная программа). если вы наберете в коде array [4], он по-прежнему будет соответствовать допустимому вызову, но только во время выполнения он вызовет необработанное исключение. C ++ использует ручное управление памятью. На самом деле это недостаток безопасности, который использовался для взлома программ

. Это может помочь понять:

int * somepointer;

somepointer [0] = somepointer [5];

0
ответ дан 23 November 2019 в 21:42
поделиться

Когда вы инициализируете массив с помощью int array [2] , выделяется место для 2 целых чисел; но массив идентификаторов просто указывает на начало этого пространства. Когда вы затем обращаетесь к массиву [3] и массиву [4] , компилятор просто увеличивает этот адрес на единицу, чтобы указать, где эти значения были бы, если бы массив был достаточно длинным; попробуйте получить доступ к чему-то вроде array [42] без его предварительной инициализации, вы получите любое значение, которое уже было в памяти в этом месте.

Изменить:

Подробнее об указателях / массивы: http://home.netcom.com/~tjensen/ptr/pointers.htm

1
ответ дан 23 November 2019 в 21:42
поделиться

Запустите это через Valgrind , и вы можете увидеть ошибку.

Как указала Фалаина, valgrind не обнаруживает многих случаев повреждения стека. Я только что попробовал образец под valgrind, и он действительно не сообщает об ошибках. Тем не менее, Valgrind может помочь в поиске многих других типов проблем с памятью, но в этом случае он не особенно полезен, если вы не измените свой Bulid, включив параметр --stack-check. Если вы соберете и запустите образец как

g++ --stack-check -W -Wall errorRange.cpp -o errorRange
valgrind ./errorRange

valgrind , сообщит об ошибке.

3
ответ дан 23 November 2019 в 21:42
поделиться

C или C ++ не будут проверять границы доступа к массиву.

Вы размещаете массив в стеке. Индексирование массива через array [3] эквивалентно * (array + 3) , где array является указателем на & array [0]. Это приведет к неопределенному поведению.

Один из способов поймать это иногда в C - использовать статическую проверку, такую ​​как splint . Если вы запустите:

splint +bounds array.c

on,

int main(void)
{
    int array[1];

    array[1] = 1;

    return 0;
}

, вы получите предупреждение:

array.c: (в функции main) Индексирование массива через array [3] эквивалентно * (array + 3) , где array является указателем на & array [0]. Это приведет к неопределенному поведению.

Один из способов поймать это иногда в C - использовать статическую проверку, такую ​​как splint . Если вы запустите:

splint +bounds array.c

on,

int main(void)
{
    int array[1];

    array[1] = 1;

    return 0;
}

, вы получите предупреждение:

array.c: (в функции main) Индексирование массива через array [3] эквивалентно * (array + 3) , где array является указателем на & array [0]. Это приведет к неопределенному поведению.

Один из способов поймать это иногда в C - использовать статическую проверку, такую ​​как splint . Если вы запустите:

splint +bounds array.c

on,

int main(void)
{
    int array[1];

    array[1] = 1;

    return 0;
}

, вы получите предупреждение:

array.c: (в функции main) array.c: 5: 9: Вероятно, за пределами поля магазин: массив [1] Невозможно разрешить ограничение: требуется 0> = 1 необходимо для выполнения предварительного условия: требует maxSet (array @ array.c: 5: 9)> = 1 Запись в память может напишите по адресу за пределами выделенный буфер.

3
ответ дан 23 November 2019 в 21:42
поделиться

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

3
ответ дан 23 November 2019 в 21:42
поделиться

Подсказка

Если вы хотите иметь быстрые массивы ограничения размера с проверкой ошибок диапазона, попробуйте использовать boost :: array , (также std :: tr1 :: array из это будет стандартный контейнер в следующей спецификации C ++). Это намного быстрее, чем std :: vector. Он резервирует память в куче или внутри экземпляра класса, как int array [].
Это простой пример кода:

#include <iostream>
#include <boost/array.hpp>
int main()
{
    boost::array<int,2> array;
    array.at(0) = 1; // checking index is inside range
    array[1] = 2;    // no error check, as fast as int array[2];
    try
    {
       // index is inside range
       std::cout << "array.at(0) = " << array.at(0) << std::endl;

       // index is outside range, throwing exception
       std::cout << "array.at(2) = " << array.at(2) << std::endl; 

       // never comes here
       std::cout << "array.at(1) = " << array.at(1) << std::endl;  
    }
    catch(const std::out_of_range& r)
    {
        std::cout << "Something goes wrong: " << r.what() << std::endl;
    }
    return 0;
}

Эта программа напечатает:

array.at(0) = 1
Something goes wrong: array<>: index out of range
5
ответ дан 23 November 2019 в 21:42
поделиться

Насколько я знаю, это неопределенное поведение. Запустите большую программу с этим, и она выйдет из строя где-нибудь по пути. Проверка границ не является частью необработанных массивов (или даже std :: vector).

Используйте std :: vector с std :: vector :: iterator вместо этого, чтобы вам не приходилось беспокоиться об этом.

Edit:

Просто ради удовольствия, запустите это и посмотрите, сколько времени до сбоя:

int main()
{
   int array[1];

   for (int i = 0; i != 100000; i++)
   {
       array[i] = i;
   }

   return 0; //will be lucky to ever reach this
}

Edit2:

Не запускайте это.

Edit3:

Хорошо, вот краткий урок о массивах и их отношениях с указателями:

Когда вы используете индексацию массива, вы действительно используете замаскированный указатель (называемый «ссылкой»), который автоматически разыменовывается. Вот почему вместо * (array [1]) array [1] автоматически возвращает значение с этим значением.

Если у вас есть указатель на массив, например:

int array[5];
int *ptr = array;

Затем "

int main()
{
   int array[1];
   int *ptr = array;

   for (int i = 0; i != 100000; i++, ptr++)
   {
       *ptr++ = i;
   }

   return 0; //will be lucky to ever reach this
}

Компилятор не будет жаловаться, потому что при программировании вам часто приходится взаимодействовать с другими программами, особенно с операционной системой. Это довольно часто делается с помощью указателей.

int main()
{
   int array[1];
   int *ptr = array;

   for (int i = 0; i != 100000; i++, ptr++)
   {
       *ptr++ = i;
   }

   return 0; //will be lucky to ever reach this
}

Компилятор не будет жаловаться, потому что при программировании вам часто приходится взаимодействовать с другими программами, особенно с операционной системой. Это довольно часто делается с помощью указателей.

7
ответ дан 23 November 2019 в 21:42
поделиться

g ++ не проверяет границы массива, и вы можете перезаписать что-то с 3,4, но ничего особо важного, если вы попробуете с более высокими числами, вы получите сбой.

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

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

12
ответ дан 23 November 2019 в 21:42
поделиться

Используя g ++, вы можете добавить параметр командной строки: -fstack-protector-all .

В вашем примере это привело к следующему:

> g++ -o t -fstack-protector-all t.cc
> ./t
3
4
/bin/bash: line 1: 15450 Segmentation fault      ./t

Это на самом деле не поможет вам найти или решить проблему, но по крайней мере segfault сообщит вам, что что-то не так.

28
ответ дан 23 November 2019 в 21:42
поделиться

Неопределенное поведение работает в вашу пользу. Какое бы воспоминание вы ни забили, очевидно, ничего важного не содержится. Обратите внимание, что C и C ++ не проверяют границы массивов, поэтому подобные вещи не будут обнаружены во время компиляции или выполнения.

2
ответ дан 23 November 2019 в 21:42
поделиться

Насколько я понимаю, локальные переменные выделяются в стеке, поэтому выход за пределы вашего собственного стека может перезаписать только некоторые другие локальные переменные, если вы не перейдете слишком далеко и не превысите размер своего стека. Поскольку у вас нет других переменных, объявленных в вашей функции - это не вызывает никаких побочных эффектов. Попробуйте объявить другую переменную / массив сразу после первой и посмотрите, что с ней произойдет.

0
ответ дан 23 November 2019 в 21:42
поделиться

Когда вы пишете 'array [index]' на C, он переводит его в машинные инструкции.

Перевод выглядит примерно так:

  1. 'получить адрес массива'
  2. 'получить размер типа объекта, состоящего из массива'
  3. ', умножить размер типа на индекс'
  4. 'добавить результат к адресу массива'
  5. 'прочитать, что находится в result address '

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

0
ответ дан 23 November 2019 в 21:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: