Указатели и адреса в C - PullRequest
       20

Указатели и адреса в C

1 голос
/ 03 июня 2010

Следующий код манипулирует указателями для указания на то же место; Я озадачен тем, почему, когда я запускал код, выходные данные не показывали совпадающее значение.

#include "stdio.h"  
main()  
{  
int i=3,*x;  
float j=1.5,*y;  
char k='c',*z;  

x=&i;  
y=&j;  
z=&k;  

printf("\nAddress of x= %u",x);  
printf("\nAddress of y= %u",y);  
printf("\nAddress of z= %u",z);  

x++;  
y++;y++;y++;y++;  
z++;  

printf("\nNew Address of x= %u",x);  
printf("\nNew Address of y= %u",y);  
printf("\nNew Address of z= %u",z);  

printf("\nNew Value of i= %d",i);  
printf("\nNew Value of j= %f",j);  
printf("\nNew Value of k= %c\n",k);  
}  

Выход:

Адрес x = 3219901868
Адрес у = 3219901860
Адрес z = 3219901875
Новый адрес x = 3219901872
Новый адрес y = 3219901876
Новый адрес z = 3219901876
Новое значение i = 3
Новое значение j = 1.500000
Новое значение k = c

Новые адреса переменных y и z совпадают. Как две переменные могут иметь один и тот же адрес и иметь разные значения? Примечание: я использовал компилятор gcc в Ubuntu 9.04

Ответы [ 5 ]

12 голосов
/ 03 июня 2010

То, что вы печатаете, - это не адрес x / y / z, а адрес, на который они указывают. Затем, изменяя указатели (используя ++), вы в конечном итоге получаете y и z, указывающие на один и тот же адрес в памяти.

Что касается значений - во-первых, вы должны заметить, что вы на самом деле не меняете значения j и k, и к тому времени, когда вы печатаете эти значения, указатели больше не указывают на них.

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

0 голосов
/ 03 июня 2010

Это простой указатель математики. адрес с плавающей запятой увеличивается, по крайней мере, на sizeof (float) (это должно быть 4, но у вас есть приращение 16, это зависит от аппаратного обеспечения и реального размера, используемого для хранения поплавков), в то время как адрес char увеличивается на sizeof (char) (1)

у вас есть y + 16 = z + 1, что неудивительно, если вспомнить, что «следующий float» («вещи», на которые теперь указывает y) не действительно float, а место в памяти после float и т. Д. это для z (это будет указывать не на символ);

это просто означает, что местоположение с плавающей точкой находится в 15 байтах "перед" положением символа. То есть y + 15 = z.

РЕДАКТИРОВАТЬ : с y я всегда имею в виду адрес с плавающей запятой, взятый с &, и так для z ...: т.е. до того, как вы их включили. y + 16 - увеличенное значение y (после того, как вы сделали y ++), а z + 1 - увеличенное значение z после z ++.

РЕДАКТИРОВАТЬ 2: тупой, я не заметил, что вы увеличиваете y в 4 раза! поэтому sizeof (float) равен 4, 4 * 4 = 16 ...! и это 4 на вашем компьютере тоже (как и ожидалось IEEE для чисел fp одинарной точности ...), и это означает, что y + (sizeof (float) * 4 = z + sizeof (char) ... это все еще означает y расположение 15 байтов перед z (адрес символа)

0 голосов
/ 03 июня 2010

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

void display_addr_and_val(const int& var){
  printf("Addr:%p  Val:%d\n",&var, var);
}

void main(){
int x=0;
int y=1;

display_addr_and_val(x);
display_addr_and_val(y);
}

.. теоретически вы можете получить один и тот же адрес для x и y (если вы включите оптимизацию). Поскольку параметр "display_addr_and_val" является константной ссылкой, код можно переписать как:

void display_addr_and_val(const int& var){
  printf("Addr:%p  Val:%d\n",&var, var);
}

void main(){
int x=0;
display_addr_and_val(x);

int y=1;
display_addr_and_val(y);
}

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

Конечно, это случается не очень часто, но не существует теоретической причины, по которой это не должно происходить - поэтому суть в том, что вы должны быть предельно осторожны, когда вы предполагаете что-либо относительно адреса переменные - особенно локальные (например, не предполагайте, что их адреса будут распределены в заданном порядке или что все разные переменные будут использовать разные адреса)

0 голосов
/ 03 июня 2010

Ах. Опасности указателя арифметические.

Так y = &j устанавливает указатель y на текущий адрес с плавающей запятой "j"

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

Однако вы определили только одну плавающую точку «j», поэтому y теперь указывает на то, что было выделено после «j» - в данном случае это адрес переменной «k», которая была определена сразу после - но на практике это может быть что угодно или ничего.

Если бы вы определили «j» как массив чисел с плавающей запятой, он бы указывал на вторую запись в массиве, эквивалентную j [1]. С другой стороны, C также позволит вам использовать j [1], даже если вы не определили j как массив!

0 голосов
/ 03 июня 2010

Инкрементные указатели таким способом полезны, только если ваш указатель указывает на элемент в массиве.

Фактически, приращение указателя просто увеличивает его значение на размер, указанный типом указателя, поэтому:

  • увеличение указателя на символ добавит 1 к адресу
  • увеличение длинного указателя добавит 4 к адресу (в системах с длинным 4 байтом)
  • и т. Д.
...