文章目录
1.sizeof和strlen
sizeof是操作符
sizeof计算操作数计算的是所占空间的大小,单位是字节,不关注内存中所存放的是什么数据,只关注空间有多大。
要知道每个类型的地址所占空间是不同的
可以看到我们可以用sizeof求一下各类型所占的字节
了解这些根据我们对指针的了解和sizeof开始做这些练习
1.sizeof练习
int a[] = {0, 1, 2, 3};
// 32位平台与64位平台占用字节不同,32位机器占4个字节,64位为8个字节
printf("%d\n",sizeof(a));// sizeof求的是整个数组大小为16字节
printf("%d\n",sizeof(a+0));//这里a+0表示首元素地址,所以为一个地址大小为4/8个字节
printf("%d\n",sizeof(*a));//a是首元素地址,*a是对首元素进行解引用,计算的是首元素的大小(int类型)如果单有一个a的sizeof取的是整个数组的空间大小)
printf("%d\n",sizeof(a+1));//a+1为首元素地址+1为跳过后的第二个地址大小,第二个首元素地址字节为4/8
printf("%d\n",sizeof(a[1]));//0为首元素,第二个元素的大小4个字节
printf("%d\n",sizeof(&a));//取地址a为取出数组的地址,都是地址,单位是4/8个字节
printf("%d\n",sizeof(*&a));//取地址a为取数组的地址,*&a相当于拿到了整个数组,计算的是整个数组的大小单位是16
//*&两个在一起的话因为一个是取地址,然后解引用,互相抵消,还是数组a,整个数组的大小,单位是16
printf("%d\n",sizeof(&a+1));//取数组地址+1跳过整个数组,还是地址,是地址单位为4/8个字节
//因为&a[0]=a;两个都是取的数组首元素的地址他们的类型为int*
//而&a来说它是数组的地址,不是单一的地址,类型为指针数组int(*)[4]
printf("%d\n",sizeof(&a[0]));//地址为首元素的地址,单位为4/8个字节
printf("%d\n",sizeof(&a[0]+1));//&a[0]为首元素地址+1跳过首元素地址为第二个元素地址,还是4/8个字节
char a[] = "abcdef";// a b c d e f '\0'
printf("%zd\n", sizeof(a));//sizeof为整个数组大小加'\0'为7字节
printf("%zd\n", sizeof(a+0));//a+0位首元素地址+0,单位为4/8字节
printf("%zd\n", sizeof(*a));//*a为首元素地址解引用为首元素,首元素的字符为1个字节
printf("%zd\n", sizeof(a[1]));//a[1]为第二个元素,元素占用1字节
printf("%zd\n", sizeof(&a));//取地址a为数组的地址,数组地址也是地址占用4/8字节
printf("%zd\n", sizeof(&a+1));//取地址a+1为整个数组地址+1,跳过整个数组占用4/8个字节
printf("%zd\n", sizeof(&a[0]+1));//取地址a[0]为首元素地址+1指向第二个元素地址,单位4/8个字节
char 类型的元素大小为1个字节
char a[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(a));//因为a是整个数组,占6个字节
printf("%d\n", sizeof(a+0));//因为a为首元素地址,a+0还是首元素地址,占用4/8个字节
printf("%d\n", sizeof(*a));//*a为数组的首元素,占用1个字节
printf("%d\n", sizeof(a[1]));//a[1]为第二个元素,占用1个字节
printf("%d\n", sizeof(&a));//取地址a为整个数组的地址,为4/8个字节
printf("%d\n", sizeof(&a+1));//取地址a为整个数组的地址+1,跳过整个a数组,还是地址也是4/8字节
printf("%d\n", sizeof(&a[0]+1));//取地址a[0]为首元素地址+1为第二个地址,占4/8个字节
return 0;
2.strlen练习
strlen是库函数需要包含头文件string.h
strlen需要求得从’\0’之前的所有字符长度,字符串必须要要有***’\0***’,只有找到’\0’才会停止,否则导致会越界
strlen函数的参数为strlen(const char* str)当数组穿参后会强制转换为(const char* str)类型,因为传入的是首元素的地址,
char a[] = {'a','b','c','d','e','f'};
printf("%zd\n", strlen(a));//这里的a代表的是首元素地址,从a开始寻找到'\0'停止,为随机值无法预测
printf("%zd\n", strlen(a+0));//a+0还是从从首元素寻找,知道遇到'\0'停止,为随机值无法预测
printf("%zd\n", strlen(*a));//*a首元素,首元素解引用为'a',ASICC码值为97,97传给strlen,97作为地址来统计字符串长度,空间不一定属于当前的程序,会引发异常
printf("%zd\n", strlen(a[1]));//a[1]也是输入的元素不是地址,a[1]元素为'b',为98,出现非法访问
printf("%zd\n", strlen(&a));//&a访问的是整个数组的地址,数组开始位置的编号,传给strlen后,它的类型为char(*)[6],strlen依旧从首元素开始向后数字符,还是随机值
printf("%zd\n", strlen(&a+1));//&a访问整个数组+1还是随机值,没有'\0'
printf("%zd\n", strlen(&a[0]+1));//&a[0]首元素地址+1指向‘b',第二个元素地址依然是随机值,因为没有'\0'
char a[] = "abcdef";// a b c d e f '\0'
//字符串长度
printf("%zd\n", strlen(a));//strlen从首元素地址开始向后找\0,长度为6字节
printf("%zd\n", strlen(a+0));//a为首元素地址+0还是首元素地址,长度为6字节
printf("%zd\n", strlen(*a));//*a为首元素地址解引用为首元素’a',会出现非法访问
printf("%zd\n", strlen(a[1]));//a[1]为第二个元素‘b',会出现非法访问
printf("%zd\n", strlen(&a));//取地址a为数组的地址,传入strlen后参数为首元素,长度为6
printf("%zd\n", strlen(&a+1));//取地址a+1为整个数组地址+1,随机值
printf("%zd\n", strlen(&a[0]+1));//取地址a[0]为首元素地址+1指向第二个元素地址,从第二个元素地址开始找'\0',长度为5个字节
2.指针中存储占用大小和长度
一级指针和二级指针的指向
int a = 10;
int *b = &a;//*b指向a的地址,int为类型
int* *c = &b;//*c指向b的地址,int*为类型
printf("a=%d b=%d c=%d", a, *b, **c);//结果都为10
1.指针存放字符串求大小
char *p = "abcdef";//将a的地址赋给了p
printf("%zd\n", sizeof(p));//p是指针变量,计算为4/8字节
printf("%zd\n", sizeof(p+1));//p存放a的地址+1(char类型字节为1)跳过一个字节为b的地址4/8
printf("%zd\n", sizeof(*p));//p为首元素a的地址,解引用为’a‘,大小1字节
printf("%zd\n", sizeof(p[0]));//p[0]首元素a把常量字符串看成数组
printf("%zd\n", sizeof(&p));//取地址p为二级指针地址大小为4/8字节
printf("%zd\n", sizeof(&p+1));//4/8个字节&p可以理解为用二级指针来接收,类型为(char*)*p为指针,和字符串没关系
printf("%zd\n", sizeof(&p[0]+1));//&p[0]为a地址+1为b地址,为4/8个字符
2.指针存放字符串的长度
char *p = "abcdef";
printf("%zd\n", strlen(p));//p存放的是a的地址,长度为6
printf("%zd\n", strlen(p+1));//p+1指向的是b,长度为5
printf("%zd\n", strlen(*p));//*p是指向的字符a,传入‘97’
printf("%zd\n", strlen(p[0]));//p[0]为*p,指向的也是a元素
printf("%zd\n", strlen(&p));//取地址p取的是p中不清楚'\0'在哪里,为随机值
printf("%zd\n", strlen(&p+1));//取地址+1也为随机值
printf("%zd\n", strlen(&p[0]+1));//&p[0]为a的地址+1指向b地址长度为5
3.二维数组
int a[3][4] = {0};
printf("%zd\n", sizeof(a));//a为48个字节,取的是整个数组的大小(int)类型占用4个字节
printf("%zd\n", sizeof(a[0][0]));//数组首元素,为4个字节
printf("%zd\n", sizeof(a[0]));//为第0行的所有元素,16个字节
printf("%zd\n", sizeof(a[0]+1));//a[0]作为数组名没有单独在sizeof里,代表的首元素地址+1为第二个元素的地址4/8字节
printf("%zd\n", sizeof(*(a[0]+1)));//第一行第二个数组的地址解引用,类型为int占用4字节
printf("%zd\n", sizeof(a+1));//a作为二维数组数组名并没有放在sizeof里,所以表示的首元素地址,第一行地址
//二维数组首元素地址代表第一行的地址,a+1跳过第一行指向第二行的地址4/8字节
printf("%zd\n", sizeof(*(a + 1))); // a+1为第二行地址解引用占用16个字节=a[1];
printf("%zd\n", sizeof(*(&a[0] + 1)));//取地址a[0]是首元素的地址第一行地址+1,为第二行地址解引用为16字节
printf("%zd\n", sizeof(&a[0]+1));//取地址a[0]是第一行地址+1指向第二行的地址,地址为4/8个字节
printf("%zd\n", sizeof(*a)); //*a为对首元素地址,对a进行解引用第一行大小为16个字节
//*a=a[0]=*(a+0);
printf("%zd\n", sizeof(a[3])); //sizeof只计算大小,数组已经固定了,字节占用16
4.指针运算笔试试题
第一题
#include<stdio.h>
#include<string.h>
int main()
{
int a[5] = {1, 2, 3, 4, 5};//a[5]中的五个元素
int *ptr = (int *)(&a + 1);//&a+1跳过整个元素
printf("%d %d", *(a + 1), *(ptr - 1));//a+1为首地址+1指向第二个元素地址解引用为2
//ptr存放的是&a+1跳过的元素-1为5
return 0;
}
第二题
#include<stdio.h>
#include<string.h>
//在x86环境下
//假设结构体的大小是20个字节
//程序输出的结果是什么?
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p = (struct Test *)0x100000;
int main()
{
printf("%p\n", p + 0x1);//P指向结构体0下100000+1(跳过20个字节),因为0x100000为16进制+20位0x100014
printf("%p\n", (unsigned long)p + 0x1);//被强制转换为unsigned long类型为整形,整形+1就是+1为100001
printf("%p\n", (unsigned int*)p + 0x1);
// 被转换为(unsigned int*)无符号整形长度为4个字节+1,为0100004;
return 0;
}
第三题
#include<stdio.h>
#include<string.h>
int main()
{
int a[3][2] = {(0, 1),(2, 3),(4, 5)};
//逗号为表达式从左向右开始最后为结果,左边操作数不起任何的结果
int *p;
p = a[0];//*p指向第一行数组的元素
printf("%d", p[0]);//如果可以输出的话值为1,3,第二行为5,0,第三行为0,0
return 0;
}
第四题
#include<stdio.h>
#include<string.h>
int main()
{
int aa[2][5] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *ptr1 = (int *)(&aa + 1);
//取地址aa整个数组地址+1为跳过首个元素
int *ptr2 = (int *)(*(aa + 1));
//aa为首元素地址+1为另一行的首元素地址解引用为6
printf("%d %d", *(ptr1 - 1), *(ptr2 - 1));
//跳过的首元素-1为10,ptr2-1为5
}
第五题
#include<stdio.h>
int main()
{
int a[5][5];
int(*p)[4];
p = a;
printf("%p\n%ld", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
//a数组中的第一行地址赋值了给了p,a中一行为5个元素,p指向为4个元素
//%d打印出来是原码
//%p内存中的补码直接以16进制的形式打印
//指针-指针得到元素个数
//因为a赋值给p,p以一行4个的形式来访问a小地址减去大地址为负数,差为4个元素
//-4的原码为
//100000000000000000000000000100
//111111111111111111111111111011取反
//111111111111111111111111111100//取反+1
//内存中的地址的补补码直接当成原码,以16进制打印
//每4个二进制位为一个F
//0xfffffffc
return 0;
}
第六题
#include<stdio.h>
int main()
{
char *a[] = {"work", "at", "alibaba"};
//*a数组指向为3行的数组
char **pa = a;//*pa指向a首元素地址w
pa++; // pa++,跳过一行,指向a
printf("%s\n", *pa);//输出结果为第二行到\0位置
return 0;
}
第七题
#include<stdio.h>
int main()
{
char *c[] = {"ENTER", "NEW", "POINT", "FIRST"};
char **cp[] = {c + 3, c+2, c + 1, c};
//*cp指向为FIRST,POINT,NEW,ENTER;
char ***cpp = cp;
//**cpp指向为cp首元素地址,F
printf("%s\n", **++cpp);
//*cpp++指向第P,这时候cpp为POINT
printf("%s\n", *--* ++cpp+3);
//++优先级高,++cpp指向了N,*解引用后拿到了N首元素地址中的内容
//然后--,cpp由原先的c+1--变为c,指向E解引用拿到ENTER,然后+3为ER
printf("%s\n", *cpp[-2]+3);
//*cpp还是在NEW[-2]为指向f解引用为FIRST+3,ST
printf("%s\n", cpp[-1][-1]+1);
//cpp[-1][-1]=*(*cpp(-1)-1)+1
//*(cpp-1)找到P地址之后解引用,找到p地址中的内容,然后-1,c+2-1后为c+1之后解引用+1为EW
return 0;
}