指针的进阶3—试题
1.一维数组
int main()
{
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a));//16
//是第一种特殊情况,计算的是整个数组的大小 4*4 = 16 四个元素,每个元素的是int类型,大小为4字节
printf("%d\n", sizeof(a + 0));//4/8
//不是数组单独在sizeof内部,所以a代表的是数组首元素的地址,a+0代表的也是数组首元素的地址,地址就是4/8字节
printf("%d\n", sizeof(*a));//4
//不是特殊情况,所以a代表的是数组首元素的地址,对地址解引用得到的是首元素1,大小为4个字节
printf("%d\n", sizeof(a + 1));//4/8
//同第二种情况,代表的是第二个元素的地址,地址就是4/8字节
printf("%d\n", sizeof(a[1]));//4
//a[1]就是数组下标为1的元素->2,int类型,大小为4
printf("%d\n", sizeof(&a));//4/8
//是第二种特殊情况,取出的是整个数组的地址,地址就是4/8
printf("%d\n", sizeof(*&a));//16
//先取出整个数组的地址,然后对其进行解引用,得到的是整个数组,(大致可以理解为&和*相抵消)4*4 = 16
printf("%d\n", sizeof(&a + 1));//4/8
//&a取出的是整个数组的地址,+1跳过整个数组,产生的4后边位置的地址,地址就是4/8
printf("%d\n", sizeof(&a[0]));//4/8
//a先和[0]结合,取出的是数组第一个元素1,然后取出1的地址,地址就是4/8
printf("%d\n", sizeof(&a[0] + 1));//4/8
//取出的是数组第二个元素的地址,地址就是4/8
}
总结:
数组名一般代表的是数组首元素的地址
但也有两种特殊情况:
1.sizeof(arr)
求的是整个数组的大小
2.&arr
取出的是整个数组的地址
2.字符数组
1.char arr[] = { 'a','b','c','d','e','f' };
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr));//6
//求整个数组的大小,6*1 = 6
printf("%d\n", sizeof(arr + 0));//4/8
//求的是数组首元素地址的大小,地址就是4/8
printf("%d\n", sizeof(*arr));//1
//*arr得到的是数组首元素'a',类型为char,大小为1
printf("%d\n", sizeof(arr[1]));//1
arr[1]得到的是数组第二个元素'b',大小为1
printf("%d\n", sizeof(&arr));//4/8
//&arr得到的是整个数组的地址,地址大小为4/8
printf("%d\n", sizeof(&arr + 1));//4/8
//&arr取出的是整个数组的地址,+1跳过整个数组,产生的是'f'后边位置的地址,地址就是4/8
printf("%d\n", sizeof(&arr[0] + 1));//4/8
//&arr[0]取出的是数组第一个元素的地址,+1跳过一个char,就是数组第二个元素的地址,地址就是4/8
printf("%d\n", strlen(arr));//随机值
//arr代表的是数组首元素的地址,从该地址开始向后访问字符,统计\0之前出现的字符个数
//但是arr只有六个字符,并没有'\0',所以继续向后找,但是不知道'\0'会出现在那,所以结果为随机值
printf("%d\n", strlen(arr + 0));//随机值
//arr+0也是数组首元素的地址,第一个情况一样,结果也为随机值
printf("%d\n", strlen(*arr));//err
//*arr得到的是数组第一个元素'a','a'的ASCII值为97,strlen会将97看成是地址去访问,
//但是该地址不允许人访问
printf("%d\n", strlen(arr[1]));//err
//与上一种情况一样
printf("%d\n", strlen(&arr));//随机值
//&arr取出的是数组的地址,所以从数组第一个元素开始访问
printf("%d\n", strlen(&arr + 1));//随机值
//跳过该数组,从'f'后面的地址开始访问
printf("%d\n", strlen(&arr[0] + 1));//suijiz
//从数组第二个元素开始访问
}
总结:
sizeof
是一个操作符
sizeof
计算的是对象所占内存的大小-单位是字节,size_t
不在乎内存中存放的是什么,只在乎内存大小
strlen
库函数
求字符串长度,从给定的地址向后访问字符,统计\0
之前出现的字符个数
从例子中总结:strlen
括号总放的必须是一个地址,要不然就会err
2.char arr[] = "abcdef";
int main()
{
char arr[] = "abcdef";
//[a b c d e f \0]
printf("%d\n", sizeof(arr));//7
//包括'\0',7*1 = 7
printf("%d\n", sizeof(arr + 0));//4/8
//arr+0数组首元素的地址,地址大小为4/8
printf("%d\n", sizeof(*arr));//1
//*arr数组第一个元素,大小为1
printf("%d\n", sizeof(arr[1]));//1
//arr[1]数组第二个元素,大小为1
printf("%d\n", sizeof(&arr));//4/8
//&arr整个数组的地址,地址大小4/8
printf("%d\n", sizeof(&arr + 1));//4/8
//'\0'后面的地址
printf("%d\n", sizeof(&arr[0] + 1));//4/8
//第二个元素的地址
printf("%d\n", strlen(arr));//6
//从数组首元素开始,直到读取到'\0',大小为6
printf("%d\n", strlen(arr + 0));//6
//也是从数组首元素开始
printf("%d\n", strlen(*arr));//err
//*arr得到的是数组第一个元素'a','a'的ASCII值为97,strlen会将97看成是地址去访问,
//但是该地址不允许人访问
printf("%d\n", strlen(arr[1]));//err
//同上
printf("%d\n", strlen(&arr));//6
//从数组首元素开始,直到读取到'\0'
printf("%d\n", strlen(&arr + 1));//随机值
//&arr+1 跳过这个数组,从'\0'之后开始,直到读取到'\0'
printf("%d\n", strlen(&arr[0] + 1));//5
//从数组第二个元素开始,直到读取到'\0'
}
3.char* p = "abcdef";
int main()
{
char* p = "abcdef";
//让指针变量p指向a,p中存放a的地址
printf("%d\n", sizeof(p));//4/8
//p是一个指针变量,计算的是指针变量的大小,——>4/8
printf("%d\n", sizeof(p + 1));//4/8
// p+1是'b'的地址
printf("%d\n", sizeof(*p));//1
//*p,得到的是'a',大小为1
printf("%d\n", sizeof(p[0]));//1
//同上
printf("%d\n", sizeof(&p));//4/8
//p是个一级指针,&p就是取出p这个一级指针的地址
printf("%d\n", sizeof(&p + 1));//4/8
//&p+1是跳过p地址之后的地址
printf("%d\n", sizeof(&p[0] + 1));//4/8
//'b'的地址
printf("%d\n", strlen(p));//6
//从字符串a的地址开始,直到读取到'\0'
printf("%d\n", strlen(p + 1));//5
//从字符串b的地址开始,直到读取到'\0'
printf("%d\n", strlen(*p));
//err
printf("%d\n", strlen(p[0]));
//err
printf("%d\n", strlen(&p));//随机值
//从一级指针p的地址开始
printf("%d\n", strlen(&p + 1));//随机值
//从一级指针p的地址后面的地址开始
printf("%d\n", strlen(&p[0] + 1));//5
//从b的地址开始
}
2.二维数组
int main()
{
int a[3][4] = { 0 };
//arr数组3行4列
printf("%d\n", sizeof(a));//48
//计算的是整个数组的大小 3*4*4 = 48
printf("%d\n", sizeof(a[0][0]));//4
//a[0][0]:0行0列的元素,大小为4
printf("%d\n", sizeof(a[0]));//16
//a[0]:第一行的地址,大小为4*4 = 16
printf("%d\n", sizeof(a[0] + 1));//4/8
//a[0]:不是单独挡在sizeof中,也没有被取地址,所以指的是第一行首元素的地址,a[0]+1:第一行第二个元素的地址
printf("%d\n", sizeof(*(a[0] + 1)));//4
//*(a[0]+1):第一行第二个元素 大小为4
printf("%d\n", sizeof(a + 1));//4/8
//a代表数组首元素的地址。在二维数组中就是第一行的地址,a+1:第二行的地址
printf("%d\n", sizeof(*(a + 1)));//16
//*(a+1):第二行的元素 4*4 = 16
printf("%d\n", sizeof(&a[0] + 1));//4/8
//&a[0]:第一行的地址,&a[0]+1:第二行的地址
printf("%d\n", sizeof(*(&a[0] + 1)));//16
//*(&a[0] + 1)):第二行的元素,大小为4*4 = 16
printf("%d\n", sizeof(*a));//16
// a代表的是数组首元素的地址,在二维数组中指的是第一行的地址,*a得到第一行的元素,4*4 = 16
printf("%d\n", sizeof(a[3]));//16
//第4行大小
}
3.试题
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a + 1);
//&a取出的是整个数组的地址,&a+1跳过这个数组之后的地址。
// 本来只想数组的指针应该是 int(*p)[5] ,强转成int*类型
//ptr指向的是5之后的地址
printf("%d,%d", *(a + 1), *(ptr - 1));
//*(a+1):第二个元素->2 *(ptr-1):ptr-1向后减去一个int的大小,然后 *(ptr - 1)得到的是5
// 2 5
return 0;
}
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
//要想表示3行2列的数组应该这样写 int a[3][2] = {{0,1},{2,3}{4,5}};
//而题中表示的是一个逗号表达式,结果为 int a[3][2] = {1,3,5};
int* p;
p = a[0];
//a[0]表示的是第一行第一个元素
printf("%d", p[0]);
//p[0]等价于*(p+0),表示的也是第一行第一个元素->1
return 0;
}
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;
}
//a[0]表示的是第一行第一个元素
printf("%d", p[0]);
//p[0]等价于*(p+0),表示的也是第一行第一个元素->1
return 0;
3.
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;
}