在我的上一篇博客中我们对指针有了更近一步的认识。
这篇博客我们来举一些例子更深入理解C语言指针!
例子1:
int main()
{
short s = 3;
int a = 10;
printf("%d\n", sizeof(s = a + 2));//2
printf("%d\n", s);
return 0;
}
我们发现sizeof()括号中的表达式并没有参与运算,但是打印出来的确实是s的大小,这是因为sizeof()的结果是在编译链接的时候就决定好的,并没有参与运算的过程。所以s的值并没有发生变化,但是由于s是short类型,所以sizeof()会打印出short的大小,即为两个字节。
例子2:
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a + 1);
printf("%d,%d", *(a + 1), *(ptr - 1));
return 0;
}
首先我们明确一点&a取的是整个数组的地址,&a+1,+1跳过多少字节是由指针的大小决定的,&a可以理解为数组指针,类型为int (*)[5],所以&a+1跳过整个数组即跳到如图所在位置,单独一个数组名相当于数组首元素地址,所以*(a+1)就是a[1],ptr是一个int*的指针,所以ptr-1往前走一个int类型的大小即四个字节,即指向a[4]。
例子3:
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}* p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
p = (struct Test*)0x100000;
printf("%p\n", p + 0x1);
//0x00100014
printf("%p\n", (unsigned long)p + 0x1);
//0x00100001
printf("%p\n", (unsigned int*)p + 0x1);
//0x00100004
return 0;
}
让我们来看一下这道题目,首先p是一个结构体指针,指针加一,指向的是往后一个结构体的地址,所以p+0x1指向的是往后一个结构体的大小的地址,即为0x00 10 00 14,当p强制转化为无符号长整形,一个整形加一,就是相当于整个数字加一,比如5加上1,就是6,再来看最后一个,强转为int*类型,指针加1相当于指向一个int类型后的地址,即往后挪四个字节。
例子4:
//小端x86环境
int main()
{
int a[4] = { 1, 2, 3, 4 };
int* ptr1 = (int*)(&a + 1);
int* ptr2 = (int*)((int)a + 1);
printf("%x,%x", ptr1[-1], *ptr2);
return 0;
}
老生常谈,&a代表整个数组的地址,+1即为跳过整个数组,*(a+i)=a[i],所以ptr1[-1]等价于*(ptr-1),即指向a[3],让我们来看下一个首先a大家要明确是数组首元素的地址,把a强制类型转化为int,再加1相当于把a的地址往后挪动了一个字节的大小,如图所示,所以往后打印一个int类型是4个字节02 00 00 00
例子5:
#include <stdio.h>
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int* p;
p = a[0];
printf("%d", p[0]);
return 0;
}
这道题就是一个陷阱题了,数组里是逗号表达式,逗号表达式取的是从左到右最后一个表达式的值,所以这个数组,本质上存储的是这些内容,p指向的是a[0]即第一行,所以p[0]即为第一行第一个元素,所以打印出来为1。
例子6:
int main()
{
int a[5][5];
int(*p)[4];
p = a;
printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
return 0;
}
因为p是一个指向有四个元素的int类型的数组的数组指针,所以如果强行用P去指向a的话,P会以四个元素为单位往后指向,如图所示,FFFFFFFC是-4在计算机中的表示型式,详情可见我的博客,数据在内存中的存储,里面有详细的介绍请点击这里