Доступ к элементу за концом массива в C

Здесь вы:

public static class UnixDateTime
    {
        public static DateTimeOffset FromUnixTimeSeconds(long seconds)
        {
            if (seconds < -62135596800L || seconds > 253402300799L)
                throw new ArgumentOutOfRangeException("seconds", seconds, "");

            return new DateTimeOffset(seconds * 10000000L + 621355968000000000L, TimeSpan.Zero);
        }

        public static DateTimeOffset FromUnixTimeMilliseconds(long milliseconds)
        {
            if (milliseconds < -62135596800000L || milliseconds > 253402300799999L)
                throw new ArgumentOutOfRangeException("milliseconds", milliseconds, "");

            return new DateTimeOffset(milliseconds * 10000L + 621355968000000000L, TimeSpan.Zero);
        }

        public static long ToUnixTimeSeconds(this DateTimeOffset utcDateTime)
        {
            return utcDateTime.Ticks / 10000000L - 62135596800L;
        }

        public static long ToUnixTimeMilliseconds(this DateTimeOffset utcDateTime)
        {
            return utcDateTime.Ticks / 10000L - 62135596800000L;
        }

        [Test]
        public void UnixSeconds()
        {
            DateTime utcNow = DateTime.UtcNow;
            DateTimeOffset utcNowOffset = new DateTimeOffset(utcNow);

            long unixTimestampInSeconds = utcNowOffset.ToUnixTimeSeconds();

            DateTimeOffset utcNowOffsetTest = UnixDateTime.FromUnixTimeSeconds(unixTimestampInSeconds);

            Assert.AreEqual(utcNowOffset.Year, utcNowOffsetTest.Year);
            Assert.AreEqual(utcNowOffset.Month, utcNowOffsetTest.Month);
            Assert.AreEqual(utcNowOffset.Date, utcNowOffsetTest.Date);
            Assert.AreEqual(utcNowOffset.Hour, utcNowOffsetTest.Hour);
            Assert.AreEqual(utcNowOffset.Minute, utcNowOffsetTest.Minute);
            Assert.AreEqual(utcNowOffset.Second, utcNowOffsetTest.Second);
        }

        [Test]
        public void UnixMilliseconds()
        {
            DateTime utcNow = DateTime.UtcNow;
            DateTimeOffset utcNowOffset = new DateTimeOffset(utcNow);

            long unixTimestampInMilliseconds = utcNowOffset.ToUnixTimeMilliseconds();

            DateTimeOffset utcNowOffsetTest = UnixDateTime.FromUnixTimeMilliseconds(unixTimestampInMilliseconds);

            Assert.AreEqual(utcNowOffset.Year, utcNowOffsetTest.Year);
            Assert.AreEqual(utcNowOffset.Month, utcNowOffsetTest.Month);
            Assert.AreEqual(utcNowOffset.Date, utcNowOffsetTest.Date);
            Assert.AreEqual(utcNowOffset.Hour, utcNowOffsetTest.Hour);
            Assert.AreEqual(utcNowOffset.Minute, utcNowOffsetTest.Minute);
            Assert.AreEqual(utcNowOffset.Second, utcNowOffsetTest.Second);
            Assert.AreEqual(utcNowOffset.Millisecond, utcNowOffsetTest.Millisecond);
        }
    }
13
задан jww 8 June 2018 в 08:15
поделиться

3 ответа

C не разрешает доступ к памяти за пределами конца массива. Однако он позволяет указателю указывать на один элемент за пределами конца массива. Различие важно.

Таким образом, это нормально:

char array[N];
char *p;
char *end;

for (p = array, end = array + N; p < end; ++p)
    do_something(p);

(Выполнение * end было бы ошибкой.)

И это показывает причину, по которой эта функция полезна: указатель, указывающий в (несуществующем) элементе после конца массива полезен для сравнений, например, в циклах.

С технической точки зрения, это все, что позволяет стандарт C. Однако на практике реализация C (компилятор и среда выполнения) не проверяет, получаете ли вы доступ к памяти за пределами конца массива, является ли это одним элементом или несколькими. Необходима проверка границ, что замедлит выполнение программы.

21
ответ дан 1 December 2019 в 19:31
поделиться

вы можете выйти далеко за пределы 1 после массива например, `

int main()
{
        char *string = "string";
        int i = 0;
        for(i=0; i< 10;i++)
        {
                printf("%c\n", string[i]);
        }
        return 0;
}

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

-2
ответ дан 1 December 2019 в 19:31
поделиться

Часто бывает полезно обозначить "конечную" позицию, которая находится на одну позицию после фактического выделения, поэтому вы можете написать такой код:

 char * end = begin + size;
 for (char * curr = begin; curr < /* or != */ end ; ++curr) {
    /* do something in the loop */
 }

Стандарт C явно говорит, что этот элемент является действительный адрес памяти, но разыменовать его по-прежнему не рекомендуется.

Почему у него есть эта гарантия? Допустим, у вас есть машина с 2 ^ 16 байтами памяти, адресами 0000-FFFF, 16-битными указателями. Допустим, вы создали 16-байтовый массив. Может ли память быть выделена в FFF0?

Непрерывно свободно 16 байтов, но:

begin + size == FFF0 + 10 (16 in hex) == 10000

сбрасывается на 0000 из-за размера указателя. Теперь условие цикла:

curr < end == FFF0 < 0000 == false

Вместо итерации по массиву цикл ничего не сделает. Это приведет к поломке большого количества кода, поэтому в стандарте C указано, что распределение недопустимо.

16
ответ дан 1 December 2019 в 19:31
поделиться
Другие вопросы по тегам:

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