Что приводит к сбою этого переназначения целочисленного указателя? - PullRequest
3 голосов
/ 16 октября 2008

Я новичок в C, и у меня есть этот вопрос. почему происходит сбой следующего кода:

int *a = 10;
*a = 100;

Ответы [ 10 ]

20 голосов
/ 16 октября 2008

Поскольку вы пытаетесь записать 100 в ячейку памяти 0x0000000A, которая, вероятно, не выделена вашей программе. То есть

int *a = 10;

не означает, что указатель «а» будет указывать на местоположение в памяти, имеющее значение 10. Это означает, что он указывает на адрес 10 (0x0000000A) в памяти. Затем вы хотите записать что-то в этот адрес, но у вас нет «прав» на это, поскольку он не выделен

Вы можете попробовать следующее:

int *a = malloc(sizeof(int));
*a = 100;

Это сработало бы, хотя ужасно неэффективно. Если вам нужен только один int, вы должны просто поместить его в стек , а не в кучу . В 32-битной архитектуре указатель имеет длину 32 бита, а int также имеет длину 32 бита, поэтому ваша структура указатель на int занимает ( не менее ) 8 байт пространства памяти таким образом вместо 4. И мы даже не упоминали о проблемах кэширования.

12 голосов
/ 16 октября 2008

Вам необходимо назначить указатель для ячейки памяти , а не произвольного значения (10).

int cell = 10;
int *a = &cell; // a points to address of cell
*a = 100;       // content of cell changed

См. мой ответ на другой вопрос, о том, как быть осторожным с C .

7 голосов
/ 16 октября 2008

Я хотел бы предложить небольшое изменение в использовании malloc () для всех ответов, которые предлагают использовать его для выделения памяти для int. Вместо:

a = malloc(sizeof(int));

Я бы предложил не повторять тип переменной, поскольку она известна компилятору, и повторение ее вручную делает код более плотным и создает риск ошибки. Если позже вы измените объявление, например,

long *a;

Без изменения распределения вы в конечном итоге выделите неправильный объем памяти (в общем случае на 32-разрядных компьютерах int и long часто имеют одинаковый размер). Это, IMO, лучше использовать:

a = malloc(sizeof *a);

Это просто означает «размер типа, на который указывает символ», в данном случае int, что, конечно, совершенно верно. Если вы измените тип в объявлении, как указано выше, эта строка по-прежнему верна. Существует риск, если вы измените имя переменной в левой части назначения, но, по крайней мере, вы больше не будете повторять информацию без необходимости.

Также обратите внимание, что скобки с sizeof не нужны при использовании его на реальных объектах (т.е. переменных), только с именами типов, которые выглядят как выражения приведения. sizeof это не функция, это оператор.

2 голосов
/ 16 октября 2008

Следующая строка,

int *a = 10;

определяет указатель на целое число a . Затем вы указываете указатель на ячейку памяти 10.

Следующая строка,

*a = 100;

Помещает значение 100 в ячейку памяти, на которую указывает a.

Проблема:

  1. Вы не знаете, куда указывает. (Вы не знаете значение ячейки памяти 10)
  2. Где бы ни указывал, вы, вероятно, не имеете права изменять это значение. Вероятно, это память какой-то другой программы / процесса. Ты вор!
2 голосов
/ 16 октября 2008

Потому что вы никогда не выделяли памяти для. Вы только что выделили некоторое пространство стека для указателя на.

int *a = NULL;

a = malloc (sizeof (int));

if (a != NULL)
{
*a =10;
}

Будет работать.

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

т.е.

int a* = NULL;
int b = 10;

a = &b;

Теперь это будет означать, что делать что-то вроде

*a = 100;

также установит b == 100

Проверьте это: http://home.netcom.com/~tjensen/ptr/pointers.pdf

1 голос
/ 16 октября 2008

Этот код даже компилируется? 10 не конвертируется в int *, если вы не разыгрываете его так:

int *a = (int *) 10;
*a = 100;

В этом случае вы пытаетесь записать 100 в адрес памяти на 10. Обычно это не правильный адрес памяти, поэтому ваша программа падает.

1 голос
/ 16 октября 2008

Поскольку вы объявляете указатель на int, инициализируйте указатель до 10 (адрес), а затем попытаетесь присвоить значение int по этому адресу. Поскольку память по адресу 10 не принадлежит вашему процессу, вы получаете сбой. Это должно работать:

int *a;
a = malloc(sizeof(int));
*a = 10;
printf("a=%i\n", *a);
free(a);
0 голосов
/ 16 октября 2008

Вы также можете написать это как:

int* a = 10;
*a = 100;

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

Затем прочитайте вслух:

"Pointer-to-int 'a' becomes 10"
"Value-pointed-to-by 'a' becomes 100"

Подставляя фактическое значение:

"Value-pointed-to-by 10 becomes 100"

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

Вы бы почти никогда не назначали указатель с литералом:

int* ptr = (int*)10;  // You've guessed at a memory address, and probably got it wrong
int* ptr = malloc(sizeof(int)); // OS gives you a memory address at runtime  

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

0 голосов
/ 16 октября 2008

Хорошо, пытаясь дать самое простое объяснение сегодня, пытаясь дать вам более подробную картину обо всем этом. Добавим несколько скобок?

(int*) a = 10;
(*a) = 100;

Вы пытаетесь записать четыре байта в диапазон адресов [10-13]. Структура памяти вашей программы обычно начинается выше, поэтому ваше приложение не перезаписывает что-либо случайно, откуда оно могло бы функционировать (например, из .data, .bss и стека). Таким образом, вместо этого просто происходит сбой, поскольку диапазон адресов не был выделен.

Указатель указывает на область памяти, а статическая типизация C определяет тип указателя. Хотя вы можете легко переопределить указатель. Просто:

(void*) v = NULL;

Здесь мы идем дальше к вещам. Что такое нулевой указатель? Это просто указатель, который указывает на адрес 0.

Вы также можете указать тип структуры для вашего указателя:

struct Hello {
    int id;
    char* name;
};

...

struct Hello* hello_ptr = malloc(sizeof Hello);
hello_ptr->id = 5;
hello_ptr->name = "Cheery";

Хорошо, что такое malloc? Malloc выделяет память и возвращает указатель на выделенную память. Он имеет следующую подпись типа:

void* malloc(size_t size);

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

free(hello_ptr);

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

Хорошо, еще одно, как выглядит строка символов в памяти? Например, похожий на «Веселый». Простой ответ Это массив байтов с нулевым символом в конце.

0.1.2.3.4.5. 6
C h e e r y \0
0 голосов
/ 16 октября 2008

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

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