指针
什么是指针?
指针是一个值为内存地址的变量。就像char类型变量的值是字符,int类型变量的值是整数,指针变量的值是地址。
int main()
{
int a=0;//声明一个变量,在内存中分配空间
int* pa=&a;//&是取地址操作符,表示取出a的地址
//pa是一个指针变量,来存放地址。
//*表示pa是一个地址,int表示指向的对象是整型
return 0;
}
指针的解引用
如何通过指针来获取指针指向的内容呢?答案是解引用操作符*
int main()
{
int a = 10;
int* pa = &a;
printf("%d\n", *pa);//*pa表示通过指针找到指向的内容
return 0;
}
那如何改变变量的值呢?也可以通过解引用来改变
int main()
{
int a = 10;
int* pa = &a;
*pa = 20;//*pa就是a
printf("a=%d\n", a);
return 0;
}
需要注意的是:指针解引用作为左值和右值时怎么理解?
int main()
{
int a = 10;
int b = 100;
int* pa = &a;
int* pb = &b;
*pa = *pb; // 指针是左值和右值时不一样
printf("a=%d\n", a);
printf("b=%d\n", b);
return 0;
}
结论:
当指针作为左值时,p的值是内存中某个特定位置的地址。*p操作符使机器指向那个位置,表示指定需要修改的位置。当它作为右值使用时,它就提取当前存储于这个位置的值。
不能说变量可以作为左值而表达式不能作为左值。
例如:
int main()
{
int arr[10] = { 0 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < sz; i++)
{
arr[i + 3] = i;// 左值就是表达式
}
return 0;
}
int main()
{
int a=1;
int* pa=&a;
*pa=2;//左值也是表达式
return 0;
}
指针的大小
既然指针是存放地址的,那么地址就跟机器本身有关了。
在32位机器上,有32根地址线,所以能存放32位0或1组成的二进制序列。但一个字节是8位,所以需要4个字节来存放地址。
在64位机器上,有64根地址线,所以能存放64位0或1组成的二进制序列。因此需要8个字节来存放地址。
总结:
32位机器上:sizeof(p)==4
64位机器上:sizeof(p)==8
指针的类型
在整形家族中,有被分为字符型,短整型,长整型等。
在浮点型家族中,有被分为单精度、双精度型。
那么指针作为一种特殊的类型,是否有被分为类型?看下边代码:
int main()
{
int arr[] = { 1,2,3,4,5 };
int* pa = arr;
pa++;
return 0;
}
我们可以分析一下调试的过程:
当代码走到第9行时,指针变量pa接受数组首元素的地址,在监视和内存窗口知道pa的地址是 0x010ff87c.
当代码走过第9行时,执行完pa++操作后,此时的pa是0x010ff880,也就是跳过了数组中的一个元素指向了元素2。我们知道地址在内存中以16进制形式表示,执行代码第9行前后的指针差正好差了80-7c=4个字节。
再来看下边代码:
int main()
{
int arr[] = { 1,2,3,4,5 };
char* pc = (char*)arr;
pc++;
return 0;
}
当我们用字符指针操作时:
当代码走到第9行还未执行时,pc的值是0x00effe50.
当代码执行完第9行时,pc的值是0x00effe51,并没有访问到数组第二个元素。此时的指针差是1个字节。
这就说明了虽然指针(任意类型)的大小在同一机器下都是相同的,但是指针向前或者向后走的步长是不一样的。
总结:指针的类型决定了走一步走多长。因此可以得出另一个结论:当我们解引用操作时,指针的类型决定了我们所访问的权限。
char*的指针解引用只能访问一个字节。
int*的指针解引用能访问4个字节。
指针的运算
指针+-整数:一般就是指针自增自减
指针+-指针:运算结果是两指针之间的元素个数
指针的关系运算:指针可以比大小
指针与数组的关系
首先说下数组名的特点:
数组名就是数组首元素地址。但是在两种情况下不是:
1、sizeof(数组名):这是整个数组所占的空间大小。
2、&数组名,这是取出整个数组的地址。
举例:
int main()
{
int arr[] = { 1,2,3,4,5 };
int sz = sizeof(arr);
printf("数组所占的空间大小是%d个字节.\n", sz);
printf("数组名地址是:%p\n", arr);
printf("数组名+1地址是:%p\n", arr+1);
printf("\n");
printf("数组首元素的地址是:%p\n", &arr[0]);
printf("数组首元素+1的地址是:%p\n", &arr[0] + 1);
printf("\n");
printf("&数组名的地址是:%p\n", &arr);
printf("&数组名+1的地址是:%p\n", &arr+1);
return 0;
}
可以看出:
数组名就是首元素地址。当数组名+1时只是跳过数组内一个元素。因为08->0C正好跳过了4个字节,即一个整型元素。
&数组名看起来也是首元素地址,但是+1后,从08->1C,跳过了14个字节。注意的是,这是以16进制下的表示,转化为10进制是1*16^1 + 4*16^0=20个字节,刚好是整个数组的大小。所以数组名+1是跳过整个数组。
sizeof(数组名)是计算整个数组大小,即20个字节。
知道了上述以后:是不是就可以用指针访问数组内元素?答案是肯定的
int main()
{
int arr[] = { 1,2,3,4,5 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = arr;
for (i = 0; i < sz; i++)
{
printf("%d ", *(p + i));//p是首元素地址,每次加1都是跳过一个元素得到 //地址,然后解引用。
}
return 0;
}
所以现在访问数组除了用[]下标访问,还可以通过指着访问。
arr[i] == *(p+i) 这两者是等价的!!!