2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




Начать новую тему Ответить на тему На страницу 1, 2, 3, 4  След.
 
 СИ. Указатели. Можно ли к указателю прибавить любое число?
Сообщение28.08.2015, 21:16 


02/10/12
308
С адресной арифметикой я знаком.
Знаю, что допустима такая конструкция:
Используется синтаксис C

 | | | | | | | | |   <- это массив
    |             |
    p1            p2

    char *p1 // -текущая позиция;
    char *p2 // -за край массива;
    char *v1 // -текущая позиция приёмного массива.

    for(; p1 < p2;){
        *v1++ = *p1++;
    }
 

Указатель p1 может на один байт выйти за край массива вправо, и при этом
еще и использоваться в адресных выражениях типа (p1 < p2).
Но мне надо не просто скопировать один массив в другой, а байты из
первого маленького массива перенести во второй массив на определенные
позиции, с промежутками между ними. Так:
код: [ скачать ] [ спрятать ]
Используется синтаксис C

 | | | | |
  | | | |_________________
  | | |___________        |
  | |_____        |       |
  |       |       |       |
 | | | | | | | | | | | | | |

    int dx; // dx больше или равно нулю
      ....
    for(; p1 < p2;){
        *v1++ = *p1++;
        v1 += dx;
    }
 

Здесь указатель v1 при очередном наращивании может выйти на несколько
байт за край массива, но при этом он не используется в адресных выражениях,
и нет обращения к памяти по этому указателю.
Вопрос: допустима ли такая конструкция?
Плохая, неэстетичная, корявая альтернатива:
Используется синтаксис C

    for(;;){
        *v1++ = *p1++;
        if(p1 >= p2) break;
        v1 += dx;
    }
 

Я так понимаю, что если v1 += dx; это всего лишь операция сложения в регистре,
то да, допустима. А если, скажем, компилятор на операцию с указателем вставит
какую-нибудь защиту от переполнения регистра, и если такое переполнение
случится, то плохо.

 Профиль  
                  
 
 Re: СИ. Указатели. Можно ли к указателю прибавить любое число?
Сообщение28.08.2015, 21:24 
Заслуженный участник


04/05/09
4587
Операция
Код:
v1 += dx
гарантированно допустима стандартом, пока v1 не выйдет более, чем на один элемент за пределы массива. Но, если мы говорим о практике, а не гарантиях, то прибавлять можно сколько угодно, а вот обращаться по указателю только в пределах массива.

 Профиль  
                  
 
 Re: СИ. Указатели. Можно ли к указателю прибавить любое число?
Сообщение28.08.2015, 23:24 


02/10/12
308
venco в сообщении #1048850 писал(а):
Но, если мы говорим о практике, а не гарантиях, то прибавлять можно сколько угодно

Какое-то слабое утешение. Кабы чего не вышло. Потом не отловить эту ошибку.
Да, подчеркну, не будет ни обращения к памяти по этому указателю, ни операций
сравнения с ним. Это я гарантирую как программист, на самом деле никакой я не
программист, но гарантирую. Маленький код, который я привел, в этом смысле
точно соответствует.

http://people.toiit.sgu.ru/Sinelnikov/P ... _Ritch.pdf
Керниган и Ритчи
5.4. Адресная арифметика. стр. 101
Цитата:
Если р и q указывают на элементы одного массива, то к ним можно применять
операторы отношения == , !=, <, >= и т. д. Например, отношение вида
q < p
истинно, если p указывает на более ранний элемент массива, чем q.
Любой указатель всегда можно сравнить на равенство и неравенство с нулем.
А вот для указателей, не указывающих на элементы одного массива, результат
арифметических операций или сравнений не определен. (Существует одно исключение:
в арифметике с указателями можно использовать адрес несуществующего "следующего
за массивом" элемента, т. е. адрес того "элемента", который станет последним,
если в массив добавить еще один элемент.).

Во-вторых, как вы уже, наверное, заметили, указатели и целые можно складывать
и вычитать. Конструкция
р + n
означает адрес объекта, занимающего n-е место после объекта, на который
указывает p. Это справедливо безотносительно к типу объекта, на который
указывает p;

Тут нет ответа.

-- 29.08.2015, 00:38 --

Прще говоря, я боюсь, что программа аварийно завершится из-за
простой операции сложения указателя и числа.

 Профиль  
                  
 
 Re: СИ. Указатели. Можно ли к указателю прибавить любое число?
Сообщение28.08.2015, 23:40 
Заслуженный участник


04/05/09
4587
Если очень уж хочется полностью избежать undefined behavior, то не меняйте указатель, а используйте беззнаковое смещение - size_t. У беззнаковых чисел переполнение однозначно определено и не приводит к undefined behavior:
Используется синтаксис C
    char *p1; // -текущая позиция
    char *p2; // -за край массива
    char *v1; // -приёмный массив
    size_t i1; // -смещение в приёмном массиве
    for(;;){
        v1[i1] = *p1++;
        if(p1 >= p2) break;
        i1 += 1+dx;
    }
 


-- Пт авг 28, 2015 16:43:25 --

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

 Профиль  
                  
 
 Re: СИ. Указатели. Можно ли к указателю прибавить любое число?
Сообщение28.08.2015, 23:51 
Заслуженный участник


27/04/09
28128
Даже в версии с возможным переполнением его можно гарантированно отловить постфактум, если dx * sizeof(char) не занимает $\geqslant$ места, чем вообще адресуемо. :mrgreen: (Потому что только тогда по сравнению предыдущего указателя с новым уже нельзя будет однозначно определить, было ли переполнение. Хотя можно ещё и в длинную арифметику залезть и проверить точно, но это уже точно плохой совет.) Если я не ошибаюсь.

 Профиль  
                  
 
 Re: СИ. Указатели. Можно ли к указателю прибавить любое число?
Сообщение29.08.2015, 00:01 
Заслуженный участник


04/05/09
4587
arseniiv в сообщении #1048904 писал(а):
Даже в версии с возможным переполнением его можно гарантированно отловить постфактум, если dx * sizeof(char) не занимает $\geqslant$ места, чем вообще адресуемо. :mrgreen: (Потому что только тогда по сравнению предыдущего указателя с новым уже нельзя будет однозначно определить, было ли переполнение. Хотя можно ещё и в длинную арифметику залезть и проверить точно, но это уже точно плохой совет.) Если я не ошибаюсь.
Если быть параноиком, то по стандарту указатель может указывать только на элементы реального массива или непосредственно за ним. Компилятор имеет право вставить проверку при каждом изменении указателя, даже если вы его более не используете.
Но это именно паранойя. Если всё сделать в духе того, как декларировано в стандарте, то даже удалить память без undefined behavior невозможно, т.к. по возвращении из free() будет подвисший указатель, который, по идее, должен сразу привести к крашу. :-)

 Профиль  
                  
 
 Re: СИ. Указатели. Можно ли к указателю прибавить любое число?
Сообщение29.08.2015, 00:06 
Заслуженный участник


27/04/09
28128

(Оффтоп)

Ого! (Это даже в новом стандарте, в котором добавили константы?)

 Профиль  
                  
 
 Re: СИ. Указатели. Можно ли к указателю прибавить любое число?
Сообщение29.08.2015, 00:22 
Заслуженный участник


04/05/09
4587
arseniiv в сообщении #1048914 писал(а):
Ого! (Это даже в новом стандарте, в котором добавили константы?)
Это даже в наиновейшем стандарте C++. Про это мало кто знает, а из тех, кто знает, мало задумываются, т.к. паранойя и есть. Я уверен, что как только это правило начнут энфорсить, сломается всё, включая самые стандартные библиотеки. Зачем это правило присутствует в стандарте - я не знаю. Т.е. я могу представить такую архитектуру, где это правило будет энфорситься прямо процессором, но это будет такая экзотика, что с ней никто возиться не захочет.

 Профиль  
                  
 
 Re: СИ. Указатели. Можно ли к указателю прибавить любое число?
Сообщение29.08.2015, 00:30 
Заслуженный участник
Аватара пользователя


01/09/13
4656
venco в сообщении #1048917 писал(а):
Это даже в наиновейшем стандарте C++.

Прошу прощения, а можно какую-нибудь ссылку? (а то уж очень неожиданное положение)

 Профиль  
                  
 
 Re: СИ. Указатели. Можно ли к указателю прибавить любое число?
Сообщение29.08.2015, 00:59 
Заслуженный участник


04/05/09
4587
http://en.cppreference.com/w/cpp/language/operator_arithmetic:
Цитата:
If the pointer P points to the ith element of an array, then the expressions P+n, n+P, and P-n are pointers of the same type that point to the i+nth, i+nth, and i-nth element of the same array, respectively. The result of pointer addition may also be a one-past-the-end pointer (that is, pointer P such that the expression P-1 points to the last element of the array). Any other situations (that is, attempts to generate a pointer that isn't pointing at an element of the same array or one past the end) invoke undefined behavior.

 Профиль  
                  
 
 Re: СИ. Указатели. Можно ли к указателю прибавить любое число?
Сообщение29.08.2015, 01:17 
Заслуженный участник
Аватара пользователя


01/09/13
4656
Так вроде бы 'undefined behavior' не тоже самое, что и крэш... (но, кажется, я понял, что Вы имели ввиду)

За ссылку спасибо (совсем забыл этот сайт).

 Профиль  
                  
 
 Re: СИ. Указатели. Можно ли к указателю прибавить любое число?
Сообщение29.08.2015, 01:26 
Заслуженный участник


04/05/09
4587
Увы, undefined behavior - что угодно, включая крэш или даже форматирование диска.

-- Пт авг 28, 2015 18:37:44 --

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

 Профиль  
                  
 
 Re: СИ. Указатели. Можно ли к указателю прибавить любое число?
Сообщение29.08.2015, 01:39 
Заслуженный участник
Аватара пользователя


01/09/13
4656

(Оффтоп)

venco в сообщении #1048931 писал(а):
Увы, undefined behavior - что угодно, включая крэш или даже форматирование диска.

Чаще всего просто день-два мучительного вылавливания баги (если код чужой) :-)

 Профиль  
                  
 
 Re: СИ. Указатели. Можно ли к указателю прибавить любое число?
Сообщение29.08.2015, 02:30 


02/10/12
308
arseniiv в сообщении #1048904 писал(а):
можно ещё и в длинную арифметику залезть

venco, arseniiv, не смейтесь, я же за помощью пришел.
И я привел альтернативный вариант, простой, ничему не противоречащий. Понятно же,
что если указатель плохой, то существует другая зацепка для условия цикла. Обсмеяли,
ну пусть, всё равно - спасибо.

Geen в сообщении #1048929 писал(а):
Так вроде бы 'undefined behavior' не тоже самое, что и крэш... (но, кажется, я понял, что Вы имели ввиду).

Я не понял, Geen, объяснили бы. Я по-английски читаю с яндекс-переводчиком, плохо.
А Вы, venco, мне ответили: "Всё хорошо, всё нормально",
и сами же приводите такие ужасы, крэш и форматирование диска.

Специально подождал, пока Вы с Geen-ом пообсуждаете, и теперь говорю Вам
и Geen-у большое спасибо. Но всё же я так понял, что делать такие конструкции
можно, на свой страх и риск.

Еще есть вопросы. Они не столь важные, как первый вопрос.

Второй вопрос.
В функцию передается в качестве аргумента указатель на структуру. Структура
содержит указатели на данные. Функция перелопачивает за один вызов десятки
килобайт данных. Привожу два варианта обработки этих данных.
код: [ скачать ] [ спрятать ]
Используется синтаксис C

struct abc {
    char *p1;
    char *p2;
    char *v1;
};

int foo(struct abc *s)
{
      ....
    char *p1, *p2, *v1;

    // Вариант-1
    p1 = s->p1;
    p2 = s->p2;
    v1 = s->v1;
    while(p1 < p2){
        *v1++ = *p1++;
    }
    s->p1 = p1;
    s->p2 = p2;
    s->v1 = v1;

    // Вариант-2
    while(s->p1 < s->p2){
        *s->v1++ = *s->p1++;
    }
      ....
}
 

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

 Профиль  
                  
 
 Re: СИ. Указатели. Можно ли к указателю прибавить любое число?
Сообщение29.08.2015, 02:39 
Заслуженный участник
Аватара пользователя


01/09/13
4656
venco в сообщении #1048931 писал(а):
Например, компилятор, определив, что в каком-то коде точно есть undefined behavior

Обычно это, всё-таки, трудно сделать - размер массива чаще всего не является константой...

-- 29.08.2015, 02:51 --

oleg_2 в сообщении #1048947 писал(а):
Я по-английски читаю с яндекс-переводчиком, плохо.

Да, это плохо. Английский стоит подтянуть до этого уровня - большинство стандартов именно на английском...

oleg_2 в сообщении #1048947 писал(а):
сами же приводите такие ужасы, крэш и форматирование диска

Ну, это всё-таки из разряда просто ужасов :-)
А самый ужас - вылавливать подобные ошибки в чужом коде...
Так что лучше сразу взять за правило такие места подробно комментировать почему не может быть выхода за пределы.

oleg_2 в сообщении #1048947 писал(а):
Верна ли моя догадка о быстроте первого варианта?

Это стоит проверить на Вашем конкретном компиляторе ;-)

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 60 ]  На страницу 1, 2, 3, 4  След.

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group