2015-12-29 2 views
2

Я читал, как указатель работает на C++, и я попытался немного поработать с ним, но программа перестала работать.Как увеличить значение указателя указателя в C++

#include <iostream> 
using namespace std; 

int main() 
{ 
    char a[] = "Hello"; 
    char * b = a; 
    char ** c = &b; 

    cout << *(b+1) << endl; 
    //cout << **(c+2) << endl; 
    return 0; 
} 

Если прокомментированная строка не прокомментирована, программа перестает работать с кодом ошибки: 0xC0000005. Я что-то делаю неправильно или это какая-то ошибка?

+0

Что вы пытаетесь достичь? – martijnn2008

+2

'cout << * (* c + 2) << endl;' вы имеете в виду ... – StoryTeller

+0

@StoryTeller Да, это и было. Благодарю. – user3757605

ответ

3

Здесь:

char a[] = "Hello"; 
char * b = a; 

Вы принимаете преимущество массива гниению указателя. Итак, теперь b указывает на a[0]. То есть, b содержит адрес a[0]

, а затем

char ** c = &b; 

c теперь указывает на адрес из b, который сам по себе является указателем. c имеет адрес b, который содержит адрес a (см., почему люди ненавидят указатели сейчас?)

Если вы хотите получить доступ к a[0] от c, в первую очередь необходимо разыменования ему:

*c 

который дает b, а затем мы должны разыменованием что вернуться к a[0]:

*(*c) 

Если вы хотите использовать арифметику указателей для доступа a[1], вы хотите увеличить b, так:

* с +-

и теперь у нас есть адрес a[1], и если мы хотим напечатать, что нам снова нужны разыменование:

*(*c+1) 

Вы сделали ошибку приращения адреса из b вместо адреса a[0], когда вы сказали:

**(c+2) 

c провел адрес b, а не a, поэтому приращение этого приведет к неопределенному поведению.

0

Хорошо, давайте попробуем еще раз.

a является указателем на первый элемент массива, в данном случае «H». c является указателем на адрес b, который также является указателем на первый элемент.

Когда вы увеличиваете c на 2, вы перемещаете этот указатель вперед на два. Таким образом, адрес памяти продвигается двумя, но c - это просто указатель на a, а не сам, поэтому вы находитесь на неизвестной территории. Вместо того, что, скорее всего, работать (непроверенные):

cout<<*(*c+1)<<endl; 

Это разыменовывает с, так что вы получите б (или, то же самое), вы увеличиваем этот указатель на 1, который остается в массиве, а затем разыменования снова, чтобы получить доступ к значению.!

+0

будет обновляться тогда :) – Untitled123

3

c указывает на адрес b на текущий стек кадров.

c + 2 указывает на какое-то место на фрейме стека, благодаря арифметике указателя.

*(c + 2) Затем вы получаете доступ к этому местоположению, беря здесь не заданные байты в качестве адреса.

**(c + 2) Теперь вы пытаетесь получить доступ к указанному неуказанному адресу, и к счастью для вас он сработает.

0

&b + 2 не действительный указатель.

Для простоты предположим, что указатель всего один char широкий (наша память очень, очень маленькая).

Предположим, что a хранится по адресу 10, b на 20, а на c 21.

char* b = a; такое же, как char* b = &a[0]; - он хранит расположение первого элемента массива (10) в b.
char**c = &b хранит местонахождение b (20) в c.

Это выглядит следующим образом:

a          b c 
    10 11 12 13 14 15 16 17 18 19 20 21 22... 
| H | e | l | l | o | 0 | | | | | 10 | 20 | 

Это должно быть ясно из этого образа, c + 2 22, которая не является действительным адресом.
Выражение разыменования не определено, и все может случиться - ваш сбой был просто удачей.

Если вы ожидали в "Hello" первый l, он находится в a[2], который b[2] (или *(b + 2)), который является (*c)[2] (или *(*c + 2)).

0
char a[] = "Hello"; 
char * b = a; 
char ** c = &b; 

a и b указывают на начало массива символов, имеющего «Hello» в качестве его символов.

Итак, давайте начнем с места памяти 0x100. Hello хранится.

a является указателем, поэтому он хранит адрес.

a и b оба сохраняют эти значения 0x100. Но их адреса - это нечто другое. Допустим, что адрес a равен 0x200, а адрес b - 0x300.

c также указатель. Таким образом, он хранит адрес b. Итак, c хранит адрес 0x300 в качестве значений.

с + 1 = 0x304

с + 2 = 0x308

* C: Вы хотите получить доступ значение, хранящееся в 0x300, которая является адресом Ь.

* (с + 1): Вы хотите получить доступ адреса с 0x304

* (с + 2): Затем Вы получаете доступ к 0x308, принимая неопределенные байты есть в качестве адреса.

** (c + 2): разыменовать адрес 0x308. Он может быть или не быть адресом переменной указателя. Таким образом, разыменование может быть неопределенной операцией.

Смежные вопросы