C Указатель Вопрос - PullRequest
       1

C Указатель Вопрос

3 голосов
/ 01 сентября 2010

Я действительно запутался в том, как работают указатели.Я пытаюсь написать короткие небольшие программы, которые будут освещать, как именно они работают, и у меня возникли некоторые проблемы.Например:

char c[3]; //Creates an array of 3 bytes - the first 2 bytes can be used for characters and the 3rd would need to be used for the terminating zero

*c = 'a'; //sets c[0] to 'a'
*c++; //moves the pointer to c[1]
*c = 'b'; //sets c[1] to 'b'
*c++; //moves the pointer to c[2]
*c = '\0' //sets c[2] to the terminating zero

Очевидно, этот код неверен, иначе я бы не стал опрашивать форум:)

У меня просто некоторые проблемы с пониманием из книги, можетКто-нибудь вкратце объяснит концепцию?

Ответы [ 6 ]

15 голосов
/ 01 сентября 2010

c это не указатель, это массив. Хотя имя массива уменьшается до указателя в большинстве контекстов массива, вы не можете рассматривать имя массива как изменяемый указатель. Результат распада является лишь временным (технически rvalue ).

По этой причине вы не можете применить ++ к имени массива. Если вы хотите увеличить указатель, используйте указатель:

char *d = c;
6 голосов
/ 01 сентября 2010

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

Во-вторых, вы не понимаете, что означает *. Это не просто украшение, которое вы используете при использовании указателей. Как оператор, это означает «разыменование», то есть дает мне доступ к тому, на что указывают. Поэтому, когда вы манипулируете самим указателем (например, увеличивая его) и не манипулируете указанными данными, вам не нужно его использовать.

Вот что вы, вероятно, хотели:

char c[3]; // Creates an array of 3 bytes - the first 2 bytes can be used for characters 
           // and the 3rd would need to be used for the terminating zero
char* p_c; // Creates a character pointer that we will use to refer into the c array

p_c = &c[0]; // Assign the address of the first element of the c array to the p_c pointer.
             // This could also be "p_c = c", taking advantage of the fact that in this
             // is one of the circumstances in which an array can be treated as if it were 
             // a pointer to its first element

*p_c = 'a'; //sets c[0] to 'a'
p_c++;      //moves the pointer to c[1] (note no *)
*p_c = 'b'; //sets c[1] to 'b'
p_c++;      //moves the pointer to c[2] (note no *)
*p_c = '\0' //sets c[2] to the terminating zero
3 голосов
/ 01 сентября 2010

Указатели и массивы - это разные вещи в C. Источником путаницы является то, что массивы преобразуются ( decay , как называет его Стандарт) в указатели при малейшей провокации. Это называется "распад", потому что он теряет некоторую информацию о типе массива (а именно, его размер).

Посмотрим ...

void f( char* p );

char c_array [3];       // define an array
char *c_ptr = c_array;  // define a pointer and set it to point at the beginning of the array
                        // here array "decays" to pointer

*c_ptr = '1';
assert(c_array[0] == '1');
assert(c_ptr[0] == '1');   // this works too... in fact, operator [] is defined
                           // for pointers, not arrays, so in the line above array
                           // decays to pointer too.

++c_ptr;                   // move the pointer
//++c_array;               // -- this won't compile, you can't move the array

*c_ptr++ = '2';
*c_ptr   = '\0';
assert(c_array[1] == '2');
assert(c_array[2] == 0);

assert(sizeof(c_array) == 3);  // no decay here!

assert(sizeof(c_ptr) == sizeof(void*));  // a pointer is just a pointer

f(c_array);                // array-to-pointer decay, again


// now, what happens here?
void g( char param [100] )
{
    ++param;  // it works!
              // you can't pass an array as a parameter by value.
              // The size in the parameter declaration is ignored; it's just a comment.
              // param is a pointer.

    assert(sizeof(param) == sizeof(void*));
              // yes, it's just a pointer

    assert(*param == '2'); // in the call below
}


g(c_array);   // array-to-pointer decay, again

Надеюсь, это немного поможет.

(Обратите внимание, что для иллюстрации я смешал объявления и операторы. Вам придется немного переставить вещи, чтобы сделать ее действительной программой на C) .

РЕДАКТИРОВАТЬ : добавлен размер примеров

1 голос
/ 02 сентября 2010

Имя массива можно рассматривать как указатель на его первый элемент, хотя он является постоянным указателем, поэтому его нельзя указывать на любое другое местоположение.Так что c++ не допускается.

1 голос
/ 01 сентября 2010

Работа с программами в отладчике и проверка значений всего, что помогло мне понять указатели. Также нарисуйте много картинок на доске, чтобы укрепить ваше понимание. То, что по-настоящему закрепило это для меня, это изучение ассемблера и создание MIPS с нуля ...

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

#include <stdio.h>

int main()
{
    char c_arr[3] = {'a', 'b', '\0'};       // Array of 3 chars.
    char* c_ptr = c_arr; // Now c_ptr contains the address of c_arr.

    // What does it mean that c_ptr "contains the address of c_arr"?
    // Underneath all this talk of "pointers" and "arrays", it's all
    // just numbers stored in memory or registers. So right now, c_ptr is
    // just a number stored somewhere in your computer.

    printf("%p\n", c_ptr);
    // I got `0xbf94393d`. You'll get something different each time you run it.

    // That number (0xbf94393d) is a particular memory location. If you
    // want to use the contents of that memory location, you use the *
    // operator.
    char ch = *c_ptr;
    // Now ch holds the contents of whatever was in memory location 0xbf94393d.
    // You can print it.

    printf("%c\n", ch);
    // You should see `a`.

    // Let's say you want to work with the next memory location. Since
    // the pointer is just a number, you can increment it with the ++ operator.
    c_ptr++;
    // Let's print it to see what it contains.

    printf("%p\n", c_ptr);
    // I got 0xbf94393e. No surprises here, it's just a number -- the
    // next memory location after what was printed above.

    // Again, if we want to work with the value we can use the *
    // operator. You can put this on the left side of an assignment
    // to modify the memory location.
    *c_ptr = 'z';

    // Since c_ptr was pointing to the middle of our array when we
    // performed that assignment, we can inspect the array to see
    // the change.
    printf("%c\n", c_arr[1]);

    // Again, c_ptr is just a number, so we can point it back to
    // where it was. You could use -- for this, but I'll show -=.
    c_ptr -= 1;

    // We can also move by more than one. This will make the pointer
    // contain the address of the last memory location in the array.
    c_ptr = c_ptr + 2;

    return 0;
}

Вот моя попытка сделать снимок. Эта коробка - память вашего компьютера. Каждому месту в памяти присваивается номер, мы называем этот номер адресом.

++++++++++++++++++++++++++++++++++++++++++++
|  NAME   |   ADDRESS    |   VALUE         |
+=========+==============+=================+
|  c_arr  |  0xbf94393d  |   'a'           |
|         |  0xbf94393e  |   'b'           |
|         |  0xbf94393f  |   '\0'          |
+---------+--------------+-----------------+
|  c_ptr  +  <someaddr>  |   0xbf94393d    |
+------------------------------------------+

Когда вы открываете, скажем, c_arr[0], вы работаете с верхней строкой в ​​таблице. Обратите внимание, что c_ptr имеет в качестве значения адрес верхней строки в таблице. Когда вы говорите *c_ptr, вы указываете ЦПУ использовать 0xbf94393d в качестве адреса для работы. Так что *c_ptr = 'z' немного похоже на высказывание «Эй, иди на 0xbf94393d и оставь там« z »» - на этой улице адреса действительно большие.

0 голосов
/ 01 сентября 2010

Попробуйте

c++;
*c = 'b';
c++;
*c = '\0';

Оператор * пытается разыменовать указатель.Все, что вам нужно сделать, это переместить указатель, а затем выполнить задание.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...