Что мотивирует понятие «указатели» в C, когда достаточно только понятия чисел? - PullRequest
0 голосов
/ 04 апреля 2019

Почему мы используем указатели на языке C для хранения адресов переменных? Казалось бы, беззнаковый int может быть просто объявлен для хранения адреса.

Разница между моим вопросом и теми, которые приведены ниже, заключается в том, что они задают причину наличия разных типов указателей, а я спрашиваю причину, по которой в языке существует простая концепция указателей.

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

Связанные, но разные вопросы:

Ответы [ 4 ]

6 голосов
/ 04 апреля 2019

TL; DR

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

Длинная история

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

int x;
int *p;
// Init x and p somehow
int y = p + x; // Legal, but you probably meant y = *p + x

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

Рассмотрим этот код C:

int i=5;
float f=3.5;
float x=f+i;

В последнем выражении i будет преобразован в float перед плавающей точкой.сложение выполняется.Это было бы невозможно без информации о типе.Теперь x будет иметь значение 8.5 (ошибки округления игнорируются), но теперь рассмотрим этот псевдокод, иллюстрирующий проблему, с которой вы столкнетесь в языке типа B:

word i, f, x;
i = 5;
f = 3.5;
x = i + f;

Что вы ожидаете от переменной x содержать?И какой тип должен представлять этот битовый шаблон?

Когда дело доходит до указателей, это очень ценно.Учтите, что у вас есть два указателя, char *cptr и int *iptr.Теперь давайте посмотрим на некоторую арифметику указателей.При добавлении целого числа к указателю мы сместим указатель относительно размера типа .Таким образом, *(cptr + 1) вернет значение, которое составляет один байт после cptr, тогда как *(iptr + 1) (обычно) вернет значение, которое находится по адресу 4 или 8 байтов после iptr.

Вот пример, очень близкий к вашему вопросу.Рассмотрим этот код C:

float *p = malloc(10 * sizeof (*p));

for(int i=0; i<10; i++)
    p[i]= i * 1.2;

for(int i=0; i<10; i++)
    printf("%f\n", p[i]);

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

unsigned p = malloc(10 * sizeof(float));

for(int i=0; i<10; i++)
    *(p + i*sizeof(float)) = i * 1.2;

for(int i=0; i<10; i++)
    printf("%f\n", (float) *(p + i*sizeof(float)));

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

unsigned p = malloc(10 * sizeof(float));

for(int i=0; i<10; i++)
    *((float*)p + i*sizeof(float)) = i * 1.2;

for(int i=0; i<10; i++)
    printf("%f\n", *((float*)p + i*sizeof(float)));
1 голос
/ 04 апреля 2019

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

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

Одним примечательным примером является процессор x86 в реальном режиме.Когда один из этих процессоров запускается, он запускается в реальном режиме.Этот режим полностью восходит к оригинальным 16-битным процессорам 8086.Память в этом режиме адресуется с 16-битным сегментом и 16-битным смещением, которые вместе составляют 20-битный адрес.Они объединяются путем сдвига сегмента влево на 4 бита и добавления смещения.Например:

segment: 0x1111
offset:   0x2222
address: 0x13332

В таком макете также возможно иметь два указателя с разными числовыми значениями, которые ссылаются на один и тот же адрес:

segment: 0x1110
offset:   0x2232
address: 0x13332

Таким образом, один указатель со значением0x1111: 0x2222, а другой со значением 0x1110: 0x2232 указывают на один и тот же адрес.

Эта модель адресации также имеет концепцию ближних и дальних указателей, где ближний указатель содержит только смещение (и предполагает, что текущий сегмент врегистр CS), в то время как дальний указатель содержит как сегмент, так и смещение.

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

1 голос
/ 04 апреля 2019

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

Компьютер не волнует.

0 голосов
/ 08 апреля 2019

Потому что полезно представлять их по-разному. Вы бы также предпочли указать свои строки, записав шестнадцатеричные значения символов?

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