C Char указатели - PullRequest
       7

C Char указатели

4 голосов
/ 27 марта 2012

Допустим, у нас есть массив указателей:

char *ptr[30];

Теперь это работает отлично, и, кажется, не делает ничего неожиданного!Я могу легко вводить имена.

scanf("%s", ptr[1]);
scanf("%s", ptr[2]);
scanf("%s", ptr[3]);

printf("%s\n", ptr[1]);
printf("%s\n", ptr[2]);
printf("%s\n", ptr[3]);

Мой вопрос: если указатель можно использовать таким образом для хранения конечного числа имен, то почему используется malloc.?и в этом случае ptr [1] указывает не на символ на входе, а на сам новый ввод.например, если в ptr есть mukul, ptr [1] должен указывать на 'u', и если пробел не выделяется, когда указатель объявлен так, каковы ограничения .?

Ответы [ 4 ]

13 голосов
/ 27 марта 2012

Указатель не может использоваться таким образом. Иногда это может «сработать» для вас по чистой случайности, но использование указателя без выделения памяти для него вызовет неопределенное поведение - это означает, что ваша программа может вести себя беспорядочно и давать вам неожиданные результаты .

Помните, только то, что ваша программа компилируется и запускается, не означает, что она правильная. В языке Си существует целый класс ошибок, известных как «неопределенное поведение», многие из которых компилятор с радостью разрешит вам сделать, не жалуясь. К ним относятся перезапись буфера, использование неинициализированной переменной или разыменование указателя, который не указывает на законно выделенный блок памяти. Программы, которые проявляют неопределенное поведение, могут иногда даже работать нормально, но обычно они очень нестабильны и подвержены сбоям.

4 голосов
/ 27 марта 2012

Если вы используете то, что в вашем примере, вы просто пишете другие местоположения, которые идут после вашего ptr массива.Большинство компиляторов на самом деле должны давать хотя бы предупреждение.Ваша программа зависала на большинстве систем, вам просто очень повезло.

3 голосов
/ 27 марта 2012

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

Item       Address       0x00  0x01  0x02  0x03
----       -------       ----  ----  ----  ----
ptr        0xbbc81230    0x??  0x??  0x??  0x??
           0xbbc81234    0x??  0x??  0x??  0x??
           0xbbc81238    0x??  0x??  0x??  0x??
           ...
           0xbbc812a8    0x??  0x??  0x??  0x??           

, где 0x?? представляет случайное значение байта. Для поведения, которое вы описали, каждое из случайных значений просто указывает на доступную для записи память, а перезапись того, что там хранится , просто происходит , чтобы не иметь немедленных вредных последствий.

Bad juju: он выглядит как будто ваш код работает правильно, когда на самом деле он ведет себя очень плохо, и может привести к некоторым неприятным проблемам во время выполнения в других местах вашей программы, которые затрудняют отладку.

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

Предположим, мы добавили следующий код:

ptr[0] = malloc(strlen("foo") + 1);
strcpy(ptr[0], "foo");
ptr[1] = malloc(strlen("bar") + 1);
strcpy(ptr[1], "bar");

Мы динамически выделили некоторую дополнительную память для хранения пары строк и сохранили указатели для этих новых буферов в ptr[0] и ptr[1].

Наша картинка теперь будет выглядеть примерно так:

Item       Address       0x00  0x01  0x02  0x03
----       -------       ----  ----  ----  ----
           0x80ff0000     'f'   'o'   'o'  0x00
           ...
           0x80ffcdc0     'b'   'a'   'r'  0x00
           ...
ptr        0xbbc81230    0x80  0xff  0x00  0x00
           0xbbc81234    0x80  0xff  0xcd  0xc0
           0xbbc81238    0x??  0x??  0x??  0x??
           ...
           0xbbc812a8    0x??  0x??  0x??  0x??           

ptr[0] теперь содержит адрес буфера размером 4 char, и мы копируем строку "foo" в этот буфер. Точно так же ptr[1] содержит адрес другого 4-байтового буфера, который теперь содержит строку «bar».

3 голосов
/ 27 марта 2012

Когда вы определяете указатель как:

char *ptr = 0; // NULL pointer: dereferencing it will crash
puts(ptr);    // crash

Вы просто создаете ссылку на место в памяти:

ptr = "string"; // dereferencing it will show the string
puts(ptr);     // displaying "string"

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

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

...