目录
1.strlen和sizeof的区别
1.1sizeof
1.2strlen
1.3sizeof和strlen对比
2. 数组和指针笔试题解析
2.1一维数组(sizeof)
2.2字符数组(sizeof和strlen)
sizeof
strlen
加入指针变量(sizeof和strlen)
2.3二维数组(sizeof求大小)
2.4理解数组名的意义
3.指针运算笔试题
3.1题目一
3.2题目二
3.3题目三
3.4题目四
3.5题目五
3.6题目六
**++cpp
*- - * ++cpp + 3
*cpp[-2] + 3
cpp[-1][-1] + 1
sizeof 计算变量所占内存内存空间⼤⼩的,单位是 字节,如果操作数是类型的话,计算的是使⽤类型创建的变量所占内存空间的⼤⼩。
sizeof 只关注占⽤内存空间的⼤⼩,不在乎内存中存放什么数据。
int arr2[] = { 0,1,2,3 };
printf("%d ", sizeof(arr2));//求的是该数组的字节总长度
printf("%d ", sizeof(arr2[0]));//求数组中一个元素所占的字节大小
printf("%d ", sizeof(int));//求数组类型的字节大小。
sizeof
1. sizeof是操作符
2. sizeof计算操作数所占内 存的⼤⼩,单位是字节
3. 不关注内存中存放什么数 据
strlen
1. strlen是库函数,使⽤需要包含头⽂件 string.h
2. srtlen是求字符串⻓度的,统计的是 \0 之前字符的隔个数
3. 关注内存中是否有 \0 ,如果没有 \0 ,就会持续往后找,可能 会越界
char arr[20] = "abcdef" ;
int ret = strlen(arr);
int ret2 = sizeof(arr);
printf("%zd \n", ret);
printf("%d \n", ret2);
char arr[20] = { "abcdef" };
int ret = strlen(arr);
int ret2 = sizeof(arr);
printf("%zd \n", ret);
printf("%d \n", ret2);
分为三类
int arr[] = { 1,2,3,4 };
printf("%d \n", sizeof(arr));//16 求的是整个数组的总长度
printf("%d \n", sizeof(*&arr));//16
//两个角度来看
//1.& 来 ,* 回,相当于来回,相互抵消了,最后得到sizeof(arr);
//2.从类型来看,比如 int(*p)[10] = &arr;,所以在这里&arr是数组指针,要解引用一个数组指针,那么大小就是16
printf("%d \n", sizeof(arr+0));//4/8 arr+0 在这里arr不是单独出现,所以arr代表的是首元素地址,arr+0还是首元素的地址,是地址那么就是4/8
printf("%d \n", sizeof(arr + 1));//4/8 这个同上,arr+1是数组第二个元素的地址,因此是4/8
printf("%d \n", sizeof(&arr)); // 4/8 取的是数组的地址,数组地址也是地址,所以是4/8
printf("%d \n", sizeof(&arr + 1)); //4/8 同上 取的是数组的下一个数组地址,也是地址
printf("%d \n", sizeof(&arr[0])); //4/8 取得是数组的第一个元素的地址
printf("%d \n", sizeof(&arr[0] + 1)); //4/8 取得是数组的第二个元素的地址
printf("%d \n", sizeof(*arr));//4 arr在这里是首元素的地址,*arr就是数组第一个元素,所以字节大小就是4
printf("%d \n", sizeof(arr[1]));//4 arr[1]是数组的第二个元素,大小是4
printf("%d \n", sizeof(arr[0]));//4 同理arr[0]是数组的第一个元素,大小是4
char arr[] = { 'a','b','c','d','e','f' };//这是六个元素,没有/0
printf("%d \n", sizeof(arr));//6
printf("%d \n", sizeof(arr+0));//4/8 arr为首元素地址,arr+0也是首元素地址
printf("%d \n", sizeof(*arr));//1 这是首元素地址然后解引用,得到第一个元素
printf("%d \n", sizeof(arr[1]));//1 第一个元素
printf("%d \n", sizeof(&arr));//4/8 取数组的地址
printf("%d \n", sizeof(&arr+1));//4/8 取数组的下一个数组地址,虽然下一个是空,但也是地址
printf("%d \n", sizeof(&arr[0]+1)); //4 / 8第二个元素的地址
strlen的参数是地址,不接受值,不然会错误。
char arr[] = { 'a','b','c','d','e','f' };//这是六个元素,没有/0
printf("%d \n", strlen(arr));//随机值,strlen只能找到abcdef找不到'/0'会往前或者向后找,值是不确定的
printf("%d \n", strlen(arr + 0));//随机值,这个同上,也是首元素地址
printf("%d \n", strlen(*arr));//错误,传过去的是‘a’因为strlen接收到的是地址,这里传的是值,是错误的
printf("%d \n", strlen(arr[1]));//错误,同上
printf("%d \n", strlen(&arr));//随机值,strlen只能找到abcdef找不到'/0'会往前或者向后找,值是不确定的
printf("%d \n", strlen(&arr + 1));//随机值,这个随机值跟上面可能不同,访问的是跳过了一个数组的大小,可能越界,不确定能不能找到/0
printf("%d \n", strlen(&arr[0] + 1));//随机值,只是从‘b’开始,但也是找不到/0.
//上述情况,主要因为数组没有/0原因。
换一个数组写法
//对于strlen函数,strlen(const char *s)接收的是地址
char arr[] = "abcdef";//这是六个元素,有/0
printf("%d \n", strlen(arr));//6,传过去的是首元素地址
printf("%d \n", strlen(arr + 0));//6,arr是首元素地址,arr+0也是
printf("%d \n", strlen(*arr));//错误,传的是‘a’这是不对的,只能是地址
printf("%d \n", strlen(arr[1]));//错误,同上原理,传的是‘b’
printf("%d \n", strlen(&arr));//6,传的是数组地址,但确实是首元素地址,会报错,但是可以实现的
printf("%d \n", strlen(&arr + 1));//随机值,跳过了数组arr,那么后面无法确定能找到/0
printf("%d \n", strlen(&arr[0] + 1));//5,从‘b’开始计算
char* p = "abcdef";
/*char arr = "abcdef";
char* p = &arr;*/
printf("%zd\n", sizeof(p)); // 4/8 ,算的是指针变量p的大小
printf("%zd\n", sizeof(p + 1));//4/8,p+1是'b'的地址,是地址那么就是4/8
printf("%zd\n", sizeof(*p)); //1,*p代表是‘a’,大小是1
printf("%zd\n", sizeof(p[0]));//1,这个可以理解成char * p=arr,p=arr,所以p[0] = arr[0],指的就是‘a’大小是1,
//或者是p[0]->*(p+0) = *p
printf("%zd\n", sizeof(&p)); //4/8,取的是指针变量p的地址,char * *p1 = &p;
但也是地址,所以是4/8
printf("%zd\n", sizeof(&p + 1));//4/8,取的是指针变量p地址,
然后跳过一个指针变量,得到的是指针变量后面的地址,也是地址
printf("%zd\n", sizeof(&p[0] + 1));//4/ 8 ,p[0]=*(p+0) ,&*(p+0)得到a地址,然后+1得到‘b’的地址
要明白,p指针变量也有自己的地址空间,字符串也有自己的地址空间,是两块不同的空间。
char* p = "abcdef";
printf("%zd\n", strlen(p)); //6 ,第一个元素地址。
printf("%zd\n", strlen(p + 1));//5,取的是第二个元素地址
//printf("%zd\n", strlen(*p)); //错误
//printf("%zd\n", strlen(p[0]));//错误
printf("%zd\n", strlen(&p)); //随机值,取的是指针p的地址空间,里面不清楚有没有/0
printf("%zd\n", strlen(&p + 1));//随机咯,找不到/0
printf("%zd\n", strlen(&p[0] + 1));//5,p[0]是第一个元素,&p[0]+1就是取数组第二个元素的地址。
二维数组也遵循,除了以下两种情况,其他情况数组名都是首元素地址
1.sizeof(数组名) 是代表求整个数组总长度大小字节
2.&数组名,这是取的是整个数组地址
二维数组,数组名[0]代表的是一行
不是单独放的话数组名就是首元素地址,比如a[0]-> a[0][0],a->a[0]
int a[3][4] = { 0 };
printf("%zd\n", sizeof(a));//3*4*4=48,数组名单独放到sizeof里面求的是总大小
printf("%zd\n", sizeof(&a+1));//4/8,&a = &a[0]+1 -> &a[1],取的是第二行的地址
printf("%zd\n", sizeof(a[0][0]));//4,就是第一行第一列元素,大小为4
printf("%zd\n", sizeof(a[0])); //16,a[0]是第一行的一维数组的数组名,所以求的是这一行的大小,4*4
printf("%zd\n", sizeof(a[0] + 1));//4/8,a[0]不是单独放,所以是首元素地址,就是a[0][0],即a[0][0]+1->a[0][1]地址
printf("%zd\n", sizeof(*(a[0] + 1)));//4,*(a[0][1]) 这是一个元素,大小是4
printf("%zd\n", sizeof(a + 1)); //4/8,a为首元素地址a[0],a[0]+1 -> a[1],所以是a[1]的地址
printf("%zd\n", sizeof(*(a + 1)));//16,求的是*(a[1])一行的大小
printf("%zd\n", sizeof(&a[0] + 1));//4/8,这里是&a[0]的地址,然后+1,就是&a[1]的地址
printf("%zd\n", sizeof(*(&a[0] + 1)));//16,*(&a[1])的大小,16
printf("%zd\n", sizeof(*a));//16,*a,在这里是首元素,*a=*(a+0) =a[0],所以求的是第一行的大小
printf("%zd\n", sizeof(a[3]));//16,不会越界,size并不是真实的计算,他只需要知道数组的类型,然后就可以算出来,这里可以
//a[3] =a[0],在这里是数组名,所以是16
数组名的意义:
1. sizeof(数组名),这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩。
2. &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址。
3. 除此之外所有的数组名都表⽰⾸元素的地址。
int a[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a + 1);
//&a是取整个数组地址,然后+1就是跳过这个数组,强制类型转换为int*型指针
printf("%d,%d", *(a + 1), *(ptr - 1));
//a+1指向的是2的位置,然后解引用得到2;ptr指向的是数组后面,所以往前进一位就是指向5位置。
int a[3][2] = { (0, 1), (2, 3), (4, 5) };//这里有坑,初始化用(),说明是逗号表达式,从做到右取最大
//上面数组简化int a[3][2] = { {{1,3},{5,0},{0,0} };
int* p;
p = a[0];
printf("%d", p[0]);//p[0]就是数组首元素地址就是1
在X86环境下
假设结构体的⼤⼩是20个字节
程序输出的结构是啥?
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}*p = (struct Test*)0x100000;//结构体指针
int main()
{
//0x1是1
//指针+整数 =》指针+指针的本身大小*整数
printf("%p\n", p + 0x1);//p是结构体指针 +1就是加20,在十六进制下20 = 14(16),所以=0x100000+14=0x100014
printf("%p\n", (unsigned long)p + 0x1);//在这里p不是指针类型而是整数,整数相加直接加,0x100001.
printf("%p\n", (unsigned int*)p + 0x1);//在这里p为int类型的指针,大小为4,所以+1相当于加上4,=0x100004
return 0;
}
假设环境是x86环境,程序输出的结果是啥?
指针-指针=整数,同一个数组之间,两个指针(地址)相减绝对值得到指针与指针之间的元素个数,小的地址-大的地址得到负数。
地址存放的是补码。要经过源码-反码-补码
数组随着地址下标的增长是由低到高的
int a[5][5];
int(*p)[4];//这个是一个数组指针,大小为4,[4]其实就是每行四列
p = a;
printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);//这里要画图找到对应的位置
//-4 源码100000000000000000000100 反码 11111111111111111111111111111011 补码(16进制)FFFFFFFC
指针-指针=整数,同一个数组之间,两个指针(地址)相减绝对值得到指针与指针之间的元素个数,
// 小的地址-大的地址得到负数。
//这题主要是要会画图分析
char* a[] = { "work","at","alibaba" };
char** pa = a; //这是二级指针
pa++;
printf("%s\n", *pa); //pa指向a的,类型是char* ,所以当pa++的时候就是跳过一个char * 大小
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);//考虑优先级,最后的是+3
printf("%s\n", *cpp[-2] + 3);
printf("%s\n", cpp[-1][-1] + 1);
由上诉题目可以得出下面的图
得到下面此图,然后通过解引用,找到c+2,最后找到POINT的首地址P。
,先进行++,使cpp指向cp的下一个指针,即char **c+1,进行解引用找到c的char*c+1,然后让char *c+1 减1,得到 char *c,就是c数组的首元素指针地址,然后+3就是指向字符'E'的地址,然后打印出ER.
首先cpp[- 2 ] -> *(cpp -2)就是cpp往前退两位置,回到原处就是指向cp,然后解引用找到c+3,然后*(c+3),这个的话就是继续解引用找到 char * c+3位置,然后c+3 指向的字符串,进行+3,此时就是指向S,所以打印的ST。
这个的话要会一个一个的去[ ],先出cpp[ -1 ] --> *(cpp -1 ),这里没-1之前是保持在*- - * ++cpp + 3 这个位置,也就是指向char **c+1 ,然后cpp-1指向c+2位置,然后进行解引用找到 char *c+2位置,此时 是 char * (c+2)[ -1 ] ==》 *(c+2-1 ) 得到--> *(c+1) 位置,然后再解引用找到char *c+1 的首地址 N ,然后加 1 ,此时指向E,最后打印 EW