目录
一、数组
1.一维数组
2.字符数组
3.二维数组
二、指针
笔试题1
笔试题2
笔试题3
笔试题4
笔试题5
笔试题6
笔试题7
笔试题8(有难度)【看明白会有质的收获】
在这里我们需要先了解数组名的意义
- sizeof(数组名) ,这里的数组名表示整个数组,计算的是整个数组的大小
- &数组名,这里的数组名也表示整个数组,取出的是整个数组的大小
- 除了上述情况外,数组名都表示数组首元素的地址
一、数组
1.一维数组
分析下方代码及判断输出结果
//一维数组
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(a + 0));
printf("%d\n", sizeof(*a));
printf("%d\n", sizeof(a + 1));
printf("%d\n", sizeof(a[1]));
printf("%d\n", sizeof(&a));
printf("%d\n", sizeof(*&a));
printf("%d\n", sizeof(&a + 1));
printf("%d\n", sizeof(&a[0]));
printf("%d\n", sizeof(&a[0] + 1));
(1) sizeof(a)
sizeof(a) 是 sizeof(数组名) ,所以求的是整个数组的大小,数组元素类型是 int(占4个字节) ,有4个元素。
【结果】16
(2) sizeof( a+0 )
sizeof(a+0) 在这里要看清,这里是除上述两种情况外的一般情况,这里的 a 表示的是数组首元素地址,a+0 还是数组首元素地址,可以理解为 &a[0], 注意这里是取出的是地址。地址一般是4/8字节(32位平台/64位平台)
【结果】4(在x86环境下)/8(在x64环境下)
(3) sizeof(*a)
sizeof(*a) ,这里的 a 也是数组首元素的地址,*a解引用得到 首元素 1(int类型)
【结果】4
(4) sizeof(a+1)
sizeof(a+1) 表示的是 第二个元素的地址大小(4/8)
【结果】4/8
(5) sizeof(a[1])
sizeof(a[1]) , 计算的是第二个元素的大小
【结果】4
(6) sizeof(&a)
sizeof(&a),这里的取地址a,表示的是整个数组,但是还是地址,地址的大小(4/8)
【结果】4/8
(7) sizeof(*&a)
sizeof(*&a),这里有两种理解的方法 1. &a整个数组, *&a解引用 得到数组 相当于 sizefo(a), *&这两者相互抵消,计算的是整个数组。2.因为&a 的类型 是int( * ) [4],数组指针(指向数组的指针),数组类型,这里的数组有4 int 的元素,计算的也是整个数组
【结果】16
(8) sizeof(&a+1)
sizeof(&a+1)表示的是跨度一整个数组,但也是地址
【结果】4/8
(9) sizeof(&a[0])
sizeof(&a[0]),这里的&a[0],表示的是取出首元素的地址
【结果】4/8
(10) sizeof(&a[0]+1)
sizeof(&a[0]+1)表示的是取出的是第二个元素的地址
【结果】4/8
图解
2.字符数组
分析下方代码及判断输出结果
//字符数组
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));
//strlen求大小
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));
(1) sizeof(arr)
sizeof(arr),计算的是整个数组的大小,数组有 6 char 类型的字符
【结果】 6
(2) sizeof(arr+0)
sizeof(arr+0),这里是 数组首元素地址+0,还是数组首元素的地址
【结果】 4/8
(3) sizeof(*arr)
sizeof(*arr),这里的*arr 相当于arr[0], 计算的是数组首元素的大小
【结果】 1
(4) sizeof(arr[1])
sizeof(arr[1]),计算的是数组第二个元素的大小(数组下标从0开始)
【结果】 1
(5) sizeof(&arr)
sizeof(&arr),&arr,取的是整个数组的地址
【结果】 4/8
(6) sizeof(&arr+1)
&arr+1,表示的是跳过整个数组的地址
【结果】 4/8
(7) sizeof(&arr[0]+1)
&arr[0]+1表示的是第二个元素的地址
【结果】 4/8
strlen 计算的是字符'\0'之前的字符个数
(8) strlen(arr)
strlen(arr),计算的是整个字符数组的长度,但是 字符数组里面没有 ‘\0’,系统一直望下去找,直到找到 ‘\0’ 程序才停止执行
【结果】 随机值
(9) strlen(arr+0)
strlen(arr+0),这里是从数组首元素开始找‘\0’,已知数组里面没有‘\0’
【结果】 随机值
(10) strlen(*arr)
strlen(*arr), *arr会得到首元素 a strlen('a'), 字符a的地址是 97,所以会从地址97开始访问查找(访问97地址对应的空间,属于非法访问空间) ,系统会报错
【结果】 error
(11) strlen(arr[1])
这里的arr[1] 表示的是字符 b ,strlen 函数会从 'b'对应的地址98访问(非法访问)
【结果】 error
(12) strlen(&arr)
&arr,表示这个整个数组的地址,会从数组首元素地址开始访问找'\0'
【结果】 随机值
(13) strlen(&arr+1)
这是跳过整个数组进行找‘\0’
【结果】 随机值
(14) strlen(&arr[0]+1)
&arr[0] + 1是第二个元素的地址。结果也是随机值
【结果】 随机值
这里变成字符串了 "abcdef" 元素有 a b c d e f \0
分析下方代码
char arr[] = "abcdef";//[]这里面没有规定元素具体的个数
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));
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));
(1) sizeof(arr)
这里计算的整个数组的大小 "abcdef" 元素有 a b c d e f \0
【结果】 7
(2) sizeof(arr+0)
arr+0 首元素地址
【结果】 4/8
(3) sizeof(*arr)
计算首元素的大小
【结果】 1
(4) sizeof(arr[1])
这里计算第二个元素的大小
【结果】 1
(5) sizeof(&arr)
计算的整个数组的地址的大小
【结果】 4/8
(6) sizeof(&arr+1)
计算的是 跳过 整数组的地址的大小
【结果】 4/8
(7) sizeof(&arr[0]+1)
求的是第二个元素地址的大小
【结果】 4/8
(8) strlen(arr)
这里求的是字符串的长度 即‘\0’之前的字符的个数
【结果】 6
(9) strlen(arr+0)
arr+0 从第一个字符向后找‘\0’
【结果】 6
(10) strlen(*arr)
*arr 是 字符‘a’ 字符a 对应的ASCII 码为 97,从地址97访问(非法访问)
【结果】 error
(11) strlen(arr[1])
arr[1] 是 字符‘b’ 字符b 对应的ASCII 码为 98,从地址98访问(非法访问)
【结果】 error
(12) strlen(&arr)
这里计算的整个数组的地址的大小,但是其中的地址的值与数组首元素的地址的值相同
【结果】 6
(13) strlen(&arr+1)
从跳过整数组的地址开始找‘\0’
【结果】 随机值
(14) strlen(&arr[0]+1)
从第二个元素开始计算字符串长度
【结果】 5
变成了 字符指针 char *p = "abcdef"; (我们知道字符指针里面存放的是 首字符的地址)
分析下方代码
char *p = "abcdef";
printf("%d\n", sizeof(p));
printf("%d\n", sizeof(p+1));
printf("%d\n", sizeof(*p));
printf("%d\n", sizeof(p[0]));
printf("%d\n", sizeof(&p));
printf("%d\n", sizeof(&p+1));
printf("%d\n", sizeof(&p[0]+1));
printf("%d\n", strlen(p));
printf("%d\n", strlen(p+1));
printf("%d\n", strlen(*p));
printf("%d\n", strlen(p[0]));
printf("%d\n", strlen(&p));
printf("%d\n", strlen(&p+1));
printf("%d\n", strlen(&p[0]+1));
(1) sizeof(p)
p里面存放的是首字符的地址
【结果】 4/8
(2) sizeof(p+1)
p里面存放的是b的地址
【结果】 4/8
(3) sizeof(*p)
*p 相当于 *(p+0), p[0] 就是a 的大小
【结果】 1
(4) sizeof(p[0])
p[0] 就是a 的大小
【结果】 1
(5) sizeof(&p)
拿到指针变量 p 的地址 类型是 char **
【结果】 4/8
(6) sizeof(&p+1)
拿到指针变量 p 的地址 再+1的 地址
【结果】 4/8
(7) sizeof(p[0]+1)
b的地址
【结果】 4/8
(8) strlen(p)
从首字符的地址开始找‘\0’ 相当于计算字符串长度
【结果】6
(9) strlen(p+1)
从第二个字符开始计算字符串长度
【结果】5
(10) strlen(*p)
*p 是 字符‘a’ 字符a 对应的ASCII 码为 97,从地址97访问(非法访问)
【结果】error
(11) strlen(p[0])
p[0]是 字符‘a’ 字符a 对应的ASCII 码为 97,从地址97访问(非法访问)
【结果】 error
(12) strlen(&p)
指针变量 p 的地址开始找 ‘\0’
【结果】 随机值
(13) strlen(&p+1)
指针变量 p 的地址+1 开始找 ‘\0’
【结果】随机值
(14) strlen(&p[0]+1)
从第二个元素开始计算字符串长度
【结果】5
3.二维数组
分析下方代码
//二维数组
int a[3][4] = {0};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a[0][0]));
printf("%d\n",sizeof(a[0]));
printf("%d\n",sizeof(a[0]+1));
printf("%d\n",sizeof(*(a[0]+1)));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(*(a+1)));
printf("%d\n",sizeof(&a[0]+1));
printf("%d\n",sizeof(*(&a[0]+1)));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a[3]));
(1) sizeof(a)
sizeof(a)求整个数组的大小
【结果】 48
(2) sizeof(a[0][0])
计算的是首元素的大小
【结果】 4
(3) sizeof(a[0])
a[0]表示 第一行的所有元素,计算的是第一行元素的大小
【结果】 16
(4) sizeof(a[0]+1)
a[0]+1表示第一行第二个元素的地址
【结果】 4/8
(5) sizeof(*(a[0]+1))
计算的是第一行第二个元素的大小
【结果】 4
(6) sizeof(a+1)
a+1 相当于a[1] 表示的数组的第二行,sizeof(a+1)求的是第二行数组的地址大小
【结果】 4/8
(7) sizeof(*(a+1))
sizeof(*(a+1)) 相当于sizeof(a[1])求的是第二行数组的大小
【结果】 16
(8) sizeof(&a[0]+1)
&a[0]+1 表示的是第二行地址
【结果】 4/8
(9) sizeof(*(&a[0]+1))
解引用得到的是第二行的数组
【结果】 16
(10) sizeof(*a)
*a 是第一行的数组 *a <-->*(a+0)<-->a[0]
【结果】 16
(11) sizeof(a[3])
注意这里的数组的下标,数组是3行(下标应该在0~2),说明数组越界,但是还是按一行的数组的大小进行计算
【结果】 16(但是数组已越界)
二、指针
做题前小提示:画图
笔试题1
#include<stdio.h>
int main()
{
int a[5] = {1,2,3,4,5};
int* ptr = (int*)(&a + 1);
printf("%d,%d",*(a+1),*(ptr-1));
return 0;
}
//程序的结果是什么?
【结果】2,5
【解析】
笔试题2
#include<stdio.h>
struct Test
{
int Num;
char pcName;
short sDate;
char cha[2];
short sBa[4];
}*p = (struct Test*)0x100000;
//假设*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;
}
【结果】
00100014
00100001
00100004
【解析】
p 是结构体指针变量,这里的0x1表示1, p+0x1表示跳过一个结构体(20个字节)0x100000+0x000014 == 0x100014输出第一个为 0x100014
(unsigned long) p这里是强制类型转换,转换为数值unsigned long 类型(注意是数值,不是地址)数值0x100000+0x1 == 0x 100001
(unsigned int*)p转化为无符号整型指针,(unsigned int*)p+0x1跳过四个字节0x100004
笔试题3
//系统使用小端模式
#include<stdio.h>
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;
}
【结果】
4,2000000
【解析】
这里ptr1[-1] 相当于 ptr1 -1, (int*)((int)a + 1);这里(int)a转为数值再+1后再转为地址(注意是采取的小端模式0x 02 00 00 00)
笔试题4
#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;
}
【结果】1
【解析】p[0]打印第一行的首元素,注意是 圆括号(0,1)== 1
笔试题5
#include<stdio.h>
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;
}
【结果】FFFFFFFC -4
【解析】p = a,这里的p是数组指针int(*)[4] (4列),&p[4][2] - &a[4][2]这是小地址-大地址。%p打印(16进制)时不存在有符号与无符号之分
&p[4][2] - &a[4][2] //-4
10000000000000000000000000000100
11111111111111111111111111111011
1111 1111 1111 1111 1111 1111 1111 1100
F F F F F F F C
笔试题6
#include<stdio.h>
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;
}
【结果】10,5
【解析】
笔试题7
#include<stdio.h>
int main()
{
char* a[] = {"work","at","alibaba"};
char** pa = a;
pa++;
printf("%s\n",*pa);
return 0;
}
【结果】 at
【解析】pa 存放的是首元素的地址(第一个字符串的地址),pa+1,则pa指向第二个字符串的地址,*pa得到第二个字符串的地址
笔试题8(有难度)【看明白会有质的收获】
#include<stdio.h>
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;
}
【结果】
POINT
ER
ST
EW
【超详细解析】
这道题用到了三级指针。
先看图解(打印之前的存储情况)
char *c[ ] 是一个指针数组(元素类型都是指针) ,所以c数组里面存的是每个字符串首字符的地址
cp数组里面存放的是 c数组每个元素的地址,cpp三级指针指向cp数组的首元素
(1) printf("%s\n",**++cpp);
**++cpp,这里由于前缀自增(++)高于间接访问(*)的优先级,所以是先对cpp+1,再进行解引用
cpp+1指向cp[1],*(cpp+1)得到 c+2 ,再解引用一次*(c+2),打印得到字符串 POINT
(2) printf("%s\n",*--*++cpp+3);
*--*++cpp+3,这里先用空格区分一下 * -- * ++cpp +3,这里要注意的是 此时的cpp已经指向cp[1],而不是cp[0]了
在进行继续算 * -- * ++cpp +3,这里++cpp,指向了 cp[2], 再对其解引用(*++cpp)得到c+1
c+1,然后* -- * ++cpp +3 就变成了 *--(c+1)+3, 这个 --(c+1) 变成了 c,然后再对c解引用得到
c[0],c[0]里面存的是“ENTER”首字符的地址,可以理解为c+0,接着再对其+3,即c+3,
打印字符串得到 ER
(3) printf("%s\n",*cpp[-2]+3);
cpp,跟据上述的自增,cpp指向的是cp[2]
*cpp[-2],这里的下标引用操作符的优先级高于*,先是cpp[-2],cpp[-2]相当于*(cpp-2),*(cpp-2)得到的是c+3,因为之前*cpp[-2]+3有一个解引用,所以*(c+3)得到字符串“FIRST”的首字符的地址,之后再+3则指向 S
打印字符串得到 ST
(4) printf("%s\n",cpp[-1][-1]+1);
cpp[-1][-1]+1,首先确定cpp指向的是cp[2],然后cpp[-1][-1] +1相当于 * ( * (cpp-1) - 1) +1
cpp-1指向cp[1],解引用得到c+2,c+2 - 1 后得到 c+1,解引用得到字符“NEW”首字符的地址,然后再+1则指针指向
打印字符串得到 EW