1.sizeof和strlen的区别
sizeof是操作符,不是函数
strlen是函数,求的是字符串的长度,统计的是字符中\0之前的字符个数,只能针对字符串(字符数组)
2.数组和指针笔试题目解析
2.1 一维数组
数组名都是首元素的地址,但是有两个例外
1.sizeof(数组名)
2.&数组名
int a[] = {1,2,3,4};
printf("%zd\n",sizeof(a));
printf("%zd\n",sizeof(a+0));
printf("%zd\n",sizeof(*a));
printf("%zd\n",sizeof(a+1));
printf("%zd\n",sizeof(a[1]));
printf("%zd\n",sizeof(&a));
printf("%zd\n",sizeof(*&a));
printf("%zd\n",sizeof(&a+1));
printf("%zd\n",sizeof(&a[0]));
printf("%zd\n",sizeof(&a[0]+1));
以上代码结果和解析:
printf("%zd\n",sizeof(a));
数组名a单独放在sizeof里面,表示的是整个元素的大小,单位是字节,有4个int,int类型有4个字节,所以答案为16个字节,输出16.
printf("%zd\n",sizeof(a+0));
a表示首元素地址,a+0也是首元素地址,在32位平台下是4个字节,62位平台下8个字节,输出4/8.
printf("%zd\n",sizeof(*a));
a表示首元素地址,结引用a就是第一个元素,就是a[0],一个int,4个字节,输出4.
printf("%zd\n",sizeof(a+1));
a表示首元素地址,a+1是第2个元素的地址,输出4/8
printf("%zd\n",sizeof(a[1]));
第二个元素大小,4个字节,输出4
printf("%zd\n",sizeof(&a));
&a就是首元素的地址,输出4/8
printf("%zd\n",sizeof(*&a));
这里的*和&相互抵消了,所以等同于sizeof(a),输出16
printf("%zd\n",sizeof(&a+1));
反正是地址,输出为4/8
printf("%zd\n",sizeof(&a[0]));
还是地址,输出4/8
printf("%zd\n",sizeof(&a[0]+1));
这是第二个元素的地址,输出还是4/8
全部结果如下:
2.2字符数组
2.2.1代码1
char arr[] = {'a','b','c','d','e','f'};
printf("%zd\n", sizeof(arr));
printf("%zd\n", sizeof(arr+0));
printf("%zd\n", sizeof(*arr));
printf("%zd\n", sizeof(arr[1]));
printf("%zd\n", sizeof(&arr));
printf("%zd\n", sizeof(&arr+1));
printf("%zd\n", sizeof(&arr[0]+1));
printf("%zd\n", sizeof(arr));
arr单独放在sizeof里面,所以表示整个数组,char为1个字节,输出为6
printf("%zd\n", sizeof(arr+0));
a表示首元素地址,a+0也是首元素地址,在32位平台下是4个字节,62位平台下8个字节,输出4/8.
printf("%zd\n", sizeof(*arr));
第一个元素,输出1
printf("%zd\n", sizeof(arr[1]));
第二个元素,输出1
printf("%zd\n", sizeof(&arr));
指针,输出4/8
printf("%zd\n", sizeof(&arr+1));
一样是指针,不过是跳过整个数组的地址,输出4/8
printf("%zd\n", sizeof(&arr[0]+1));
第二个元素的地址,输出4/8
2.2.2代码2
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));
printf("%d\n", strlen(arr));
strlen计算的是\0之前的值,随机值
printf("%d\n", strlen(arr+0));
strlen计算的是\0之前的值,随机值
printf("%d\n", strlen(*arr));
arr是首元素的地址,*arr是首元素---'a'---97
strlen会认为97是地址,会一直向后访问,会崩溃
printf("%d\n", strlen(arr[1]));
这个也会崩溃
printf("%d\n", strlen(&arr));
随机值
printf("%d\n", strlen(&arr+1));
随机值
printf("%d\n", strlen(&arr[0]+1));
随机值
2.2.3代码3
char arr[] = "abcdef";
printf("%zd\n", sizeof(arr));
printf("%zd\n", sizeof(arr+0));
printf("%zd\n", sizeof(*arr));
printf("%zd\n", sizeof(arr[1]));
printf("%zd\n", sizeof(&arr));
printf("%zd\n", sizeof(&arr+1));
printf("%zd\n", sizeof(&arr[0]+1));
字符串后面还有个'\0'
printf("%zd\n", sizeof(arr));
arr单独放在sizeof里面,表示整个数组的大小,包括\0,所以输出7
printf("%zd\n", sizeof(arr+0));
指针,4/8
printf("%zd\n", sizeof(*arr));
*arr是'a',1
printf("%zd\n", sizeof(arr[1]));
'b',1
printf("%zd\n", sizeof(&arr));
&arr表示的是整个数组的地址,但是一样是地址,4/8
printf("%zd\n", sizeof(&arr+1));
跳过了整个数组的地址,指的是\0后面的地址,4/8
printf("%zd\n", sizeof(&arr[0]+1));
第二个元素的地址,4/8
2.2.4代码4
char arr[] = "abcdef";
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));
字符串后面还有个'\0'
printf("%d\n", strlen(arr));
strlen计算的是\0之前的个数,6
printf("%d\n", strlen(arr+0));
6
printf("%d\n", strlen(*arr));
*arr='a'=97,无法访问,报错·
printf("%d\n", strlen(arr[1]));
报错
printf("%d\n", strlen(&arr));
&arr的地址是首元素的地址,类型为char(*)[7],strlen是size_t strlen(const char*str),会强制转换为char*,6
printf("%d\n", strlen(&arr+1));
跳过了整个数组,随机值
printf("%d\n", strlen(&arr[0]+1));
是第二个元素的地址,5
2.2.5代码5
char *p = "abcdef";
printf("%d\n", strlen(&p));
printf("%d\n", strlen(&p+1));
printf("%d\n", strlen(&p[0]+1));
printf("%d\n", strlen(&p));
这里的&p是二级指针,跟后面的字符串没有关系,随机值
printf("%d\n", strlen(&p+1));
这里和上面一样,+1,跳过了&p的地址,一样是随机值
printf("%d\n", strlen(&p[0]+1));
这里就不同了,取得是p[0]的地址,也就是a的地址,5
答案
这里的6纯属巧合,我们可以加几个字符来验证
如图所示。
2.3二维数组
int a[3][4] = {0};
printf("%zd\n",sizeof(a));
printf("%zd\n",sizeof(a[0][0]));
printf("%zd\n",sizeof(a[0]));
printf("%zd\n",sizeof(a[0]+1));
printf("%zd\n",sizeof(*(a[0]+1)));
printf("%zd\n",sizeof(a+1));
printf("%zd\n",sizeof(*(a+1)));
printf("%zd\n",sizeof(&a[0]+1));
printf("%zd\n",sizeof(*(&a[0]+1)));
printf("%zd\n",sizeof(*a));
printf("%zd\n",sizeof(a[3]));
printf("%zd\n",sizeof(a));
a作为二维数组的数组名,单独放在sizeof内部,a表示的是整个数组,计算的是整个数组的大小,单位是字节,4/8
printf("%zd\n",sizeof(a[0][0]));
第一行第一个元素,4
printf("%zd\n",sizeof(a[0]));
a[0]是第一行的数组名,单独放在sizeof内部,计算的是整个数组的大小,16
printf("%zd\n",sizeof(a[0]+1));
a[0]是第一行数组名,但是没有单独放在sizeof内部,那么只能表示为数组首元素的地址,也就是第一行第一个元素的地址,也就是&a[0][0],+1就是&a[0][1],地址是4/8
printf("%zd\n",sizeof(*(a[0]+1)));
由上一个可知,a[0]+1==&a[0][1],结引用就是a[0][1],就是一个int,4
printf("%zd\n",sizeof(a+1));
a是二维数组数组名,没有单独放在sizeof内部,所以a表示的是首元素的地址,也就是&a[0],第一行的地址,类型为int(*)[4],a+1就是第二行的地址,地址就是4/8.
printf("%zd\n",sizeof(*(a+1)));
1*(a+1)==a[1],a[1]就是第二行的数组名,单独放在sizeof内部,表示整个数组的大小,16
2.a+1是第二行的地址,类型为int (*)[4],结引用访问的是整个数组的大小,16
printf("%zd\n",sizeof(&a[0]+1));
a[0]是第一行的数组名,&a[0]+1是第二行的地址,4/8
printf("%zd\n",sizeof(*(&a[0]+1)));
解引用访问的是第二行的数组,16
printf("%zd\n",sizeof(*a));
1.a没有单独放在sizeof内部,a是首元素的地址,也就是&a[0],解引用相当于访问第一行的数组,16
2.*a==*(a+0)==a[0]
printf("%zd\n",sizeof(a[3]));
不会越界访问,a[3]的类型为int(*)[4],sizeof知道了这个之后就会运算了,16
运行结果
3.指针运算笔试题目解析
3.1代码1
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没有单独放在sizeof内部,表示首元素地址,a+1是第二个元素的地址。
ptr被强转为int*类型,-1就向后倒退一个字节
运行输出
3.2代码2
//x86环境下
//这里告知结构体的大小是20个字节
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}
提示:
指针+1和类型有关,整数+1就是+1
p + 0x1
p是结构体类型指针,一个结构体指针类型20个字节,+1跳过就是20个字节,又因为输出形式是16进制,所以为0x100014
(unsigned long)p + 0x1
强制为整形,整形+1就是+1,0x100001
(unsigned int*)p + 0x1
+1跳过一个unsigned int类型的变量,是4个字节,0x100004
3.3代码3
#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;
}
初始化(0, 1), (2, 3), (4, 5)==(1,3,5),因为这个是逗号表达式,输出的是后一位的结果
a[0]是第一行的地址,没有放在sizeof和&,表示的是首元素的地址,所以p指向的是第一行第一个元素,打印出1.
3.4代码4
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的类型为int(*)[5],p的类型为int(*)[4],p=a把a的地址给了p。
&p[4][2] - &a[4][2]计算的是两个地址之间元素的个数,又因为低地址-高地址,为-4
1000000000000000000000000000100--原码
11111111111111111111111111111111011--反码
11111111111111111111111111111111100--补码
%d打印的是有符号的值,但存的是补码,即打印的是原码,-4
%p在内存中存的是无符号的整数,也就是原反补相同,但此时存的是补码,原码也是补码,
每四个二进制位得一个16进制位,0xFF FF FF FC
3.5代码5
int main()
{
int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int *ptr1 = (int *)(&aa + 1);
int *ptr2 = (int *)(*(aa + 1));
printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));
return 0;
}
aa是首元素的地址,也就是int(*)[5]类型,+1跳过第一行数组,强转为int*。
*(aa+1)==aa[1],aa[1]表示的是首元素的地址,即&aa[1][0]。
3.6代码6
#include <stdio.h>
int main()
{
char *a[] = {"work","at","alibaba"};
char**pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
a中存的是字符串的首元素地址,pa存的是a首元素的地址,pa++指向了a中第二个元素。*pa找到的是a中第二个元素的地址。
3.7代码7
int main()
{
char *c[] = {"ENTER","NEW","POINT","FIRST"};
char**cp[] = {c+3,c+2,c+1,c};
char***cpp = cp;
printf("%s\n", **++cpp);
printf("%s\n", *--*++cpp+3);
printf("%s\n", *cpp[-2]+3);
printf("%s\n", cpp[-1][-1]+1);
return 0;
}
**++cpp
先算++cpp,cpp本来指向的是cp中第一个元素,++之后就指向第二个,**之后访问的是c中第三个元素的地址。POINT
*--*++cpp+3
上一个影响了cpp的指向,此时cpp指向的是cp中第二个元素的地址,+的优先级最低,最后运算,++cpp指向cp中第三个元素,*解引用之后找到了cp中的c+1,再运算--*++cpp,c=(c+1)-1就指向了c中首元素的地址,然后+3指向c中首元素的第三个字符的地址,可能有人疑惑为什么不是c中第四个元素的地址,经过了前面一系列运算,最后就相当于char*c了,要想变成c+3的地址,c的类型为char**,c的类型也要是char**。所以此时+3和下列一样,ER
*cpp[-2]+3
*cpp[-2]+3==**(cpp-2)+3,由于经过上列的运算,此时cpp指向了cp中第三个元素的地址,-2就指向cp中第一个元素的地址,**两次解引用访问的是c中第四个元素的地址,再算+3就是ST
cpp[-1][-1]+1
cpp[-1][-1]+1==*(*(cpp-1)-1)+1,cpp还是指向cp的第三个元素的地址,因为上一步没有改变,cpp-1指向的是cp中第二个元素的地址,*解引用就是c+2,此时-1=c+1,*再解引用,指向的是c中第二个元素的地址,+1又向后1位地址,EW