指针(5)
1.sizeof和strlen的对比
1.1sizeof
sizeof 计算变量所占内存内存空间大小的,单位是字节,如果操作数是类型的话,计算的是使用类型创建的变量所占内存空间的大小。
sizeof 只关注占用内存空间的大小,不在乎内存存放的是什么数据。
比如:
#include<stdio.h>
int main()
{
int a = 0;
printf("%zd\n", sizeof(a));//4
printf("%zd\n", sizeof a);//4
printf("%zd\n", sizeof(int));//4
return 0;
}
1.2strlen
strlen 是C语言库函数,功能是求字符串长度。函数原型如下:
size_t strlen(const char* str);
统计的是从 strlen 函数的参数 str 中这个地址开始向后, \0 之前字符串中字符的个数。
strlen 函数会一直向后找 \0 字符,直到找到为止,所以可能存在越界查找。
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[3] = { 'a', 'b', 'c' };
char arr2[] = { "abc" };
printf("%zd\n", strlen(arr1));//35:随机值
printf("%zd\n", strlen(arr2));//3
printf("%zd\n", sizeof(arr1));//3
printf("%zd\n", sizeof(arr2));//4
return 0;
}
1.3sizeof和strlen的对比
2.数组和指针笔试题解析
2.1一维数组
#include<stdio.h>
#include<string.h>
int main()
{
int a[] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%zd\n", sizeof(a));
//a表示的是整个数组的地址,所以是sizeof(int)*10 == 40个字节
printf("%zd\n", sizeof(a + 0));
//a表示的是数组首元素的地址,计算的是首元素的地址,是地址就是4/8个字节
printf("%zd\n", sizeof(*a));
//a表示的是数组首元素的地址,*a表示的是 首元素的大小,是int类型,为4个字节
printf("%zd\n", sizeof(a + 1));
//a表示的是数组首元素的地址,a + 1,表示的是a[1]的地址,是地址就是4/8个字节
printf("%zd\n", sizeof(a[0]));
//a表示的是数组首元素的地址,a[0]表示的是数组首元素,是int类型,为4个字节
printf("%zd\n", sizeof(a[1]));
//a表示的是数组首元素的地址,a[1]表示的是第二个元素,是int类型,为4个字节
printf("%zd\n", sizeof(&a));
//&a,表示的是取出的是整个数组的地址,是地址就是4/8个字节
printf("%zd\n", sizeof(&a + 1));
//&a,表示的是取出的是整个数组的地址,&a + 1就跳出整个整型数组,是地址就是4/8个字节
printf("%zd\n", sizeof(&a[0]));
//&a[0]表示的是首元素的地址,是地址就是4/8个字节
printf("%zd\n", sizeof(&a[0] + 1));
//&a[0]表示的是首元素的地址,&a[0] + 1表示的是下标为1的元素的地址,是地址就是4/8个字节
return 0;
}
2.2字符数组
代码1:
#include <stdio.h>
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
// a b c d e f
printf("%zd\n", sizeof(arr));
//arr表示的是整个数组的地址,所以是sizeof(char)*6 == 6个字节
printf("%zd\n", sizeof(arr + 0));
//arr + 0表示的是数组首元素的地址,是地址就是4/8个字节
printf("%zd\n", sizeof(*arr));
//arr表示的是整个数组的地址,*arr得到的是首元素,arr的类型是char,是1个字节
printf("%zd\n", sizeof(arr[1]));
//arr[1],表示的是下标为1的数组元素,类型是char,是1个字节
printf("%zd\n", sizeof(&arr));
//&arr表示取出的是整个数组的地址,是地址就是4/8个字节
printf("%zd\n", sizeof(&arr + 1));
//&arr,表示的是取出的是整个数组的地址,&a + 1就跳出整个字符数组,是地址就是4/8个字节
printf("%zd\n", sizeof(&arr[0] + 1));
//&arr[0]表示的是首元素的地址,&arr[0] + 1表示的是下标为1的数组元素的地址,是地址就是4/8个字节
return 0;
}
代码2:
#include <stdio.h>
#include<string.h>
//size_t strlen ( const char * str );
//strlen()需要的是首元素的地址
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
// a b c d e f
printf("%zd\n", strlen(arr));
//arr表示的是首元素的地址,数组中没有\0,就会导致越界访问,结果就会是随机值
printf("%zd\n", strlen(arr + 0));
//arr表示的是首元素的地址,数组中没有\0,就会导致越界访问,结果就会是随机值
printf("%zd\n", strlen(*arr));
//arr是首元素的地址,*arr是首元素,就是'a','a'的ASCII码值是97,相当于把97当作地址传给strlen(),strlen得到的就是野指针, 代码是有问题的,结果错误//err
printf("%zd\n", strlen(arr[1]));
//arr[1]表示的是下标为1的数组元素,就是'b','b'的ASCII码值是98,相当于把98当作地址传给strlen(),strlen得到的就是野指针, 代码是有问题的,结果错误//err
printf("%zd\n", strlen(&arr));
//&arr表示取出的是整个数组的地址,数组中没有\0,就会导致越界访问,起始位置指向数组的第一个元素的位置,结果就会是随机值 x
printf("%zd\n", strlen(&arr + 1));
//&arr表示取出的是整个数组的地址,&arr + 1跳过整个数组的地址,数组中没有\0,就会导致越界访问,起始位置指向数组的最后一个元素的下一个位置,结果就会是随机值 x - 6
printf("%zd\n", strlen(&arr[0] + 1));
//从第2个元素开始向后统计的,得到的也是随机值 x - 1
return 0;
}
代码3:
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = { "abcdef" };
// a b c d e f \0
printf("%zd\n", sizeof(arr));
//arr表示的是整个数组的地址,计算的是整个数组的大小,sizeof(char)*7 == 7个字节
printf("%zd\n", sizeof(arr + 0));
//arr + 0表示的是数组首元素的地址,是地址就是4/8个字节
printf("%zd\n", sizeof(*arr));
//*arr得到的是数组的首元素,类型是char,1个字节
printf("%zd\n", sizeof(arr[1]));
//arr[1]是数组下标为1的元素,类型是char,1个字节
printf("%zd\n", sizeof(&arr));
//&arr取出的是整个数组的地址,是地址就是4/8字节
printf("%zd\n", sizeof(&arr + 1));
//&arr取出的是整个数组的地址,&arr + 1跳过整个数组,还是地址,是地址就是4/8个字节
printf("%zd\n", sizeof(&arr[0] + 1));
//&arr[0]表示的是数组首元素的地址,&arr[0] + 1就是下标为1的数组元素的地址,是地址就是4/8个字节
return 0;
}
代码4:
#include <stdio.h>
#include<string.h>
int main()
{
char arr[] = "abcdef";
// a b c d e f \0
printf("%zd\n", strlen(arr));
//arr首元素的地址,向后在\0之前有6个字符
printf("%zd\n", strlen(arr + 0));
//arr首元素的地址,arr+0还是首元素的地址,向后在\0之前有6个字符
printf("%zd\n", strlen(*arr));
//arr是首元素的地址,*arr是首元素,就是'a','a'的ASCII码值是97,相当于把97当作地址传给strlen(),strlen得到的就是野指针, 代码是有问题的,结果错误
printf("%zd\n", strlen(arr[1]));
//arr[1]是下标为1数组的元素,就是'b','b'的ASCII码值是98,相当于把98当作地址传给strlen(),strlen得到的就是野指针, 代码是有问题的,结果错误
printf("%zd\n", strlen(&arr));
//&arr表示取出的是整个数组的地址,起始位置指向数组的第一个元素的位置,向后在\0之前有6个字符
printf("%zd\n", strlen(&arr + 1));
//&arr表示取出的是整个数组的地址,&arr + 1跳过整个数组的地址,起始位置指向数组的最后一个元素的下一个位置,数组中没有\0,就会导致越界访问,结果就会是随机值
printf("%zd\n", strlen(&arr[0] + 1));
//&arr[0]表示的是数组首元素的地址,&arr[0] + 1就是下标为1的数组元素的地址,向后在\0之前有5个字符
return 0;
}
代码5:
#include <stdio.h>
int main()
{
const char* p = "abcdef";
// a b c d e f \0
printf("%zd\n", sizeof(p));
//p是指针变量,计算的是指针变量的大小,4/8个字节
printf("%zd\n", sizeof(p + 1));
//p + 1是b的地址,是地址大小就是4/8个字节
printf("%zd\n", sizeof(*p));
//p的类型是const char*,*p就是char类型,大小是1个字节
printf("%zd\n", sizeof(p[0]));
//1.p[0] -> *(p + 0) -> *p -> 得到的就是'a',大小是1个字节
//2.把常量字符串想象成数组,p可以理解为数组名,p[0], 就是首元素
printf("%zd\n", sizeof(&p));
//取出的是p的地址,地址的大小是4/8个字节
printf("%zd\n", sizeof(&p + 1));
//&p + 1是跳过p指针变量后的地址,是地址就是4/8个字节
printf("%zd\n", sizeof(&p[0] + 1));
//&p[0]表示的是取出字符串首字符的地址,&p[0] + 1是第二个字符的地址,大小是4/8个字节
return 0;
}
代码6:
#include <stdio.h>
#include <string.h>
int main()
{
const char* p = "abcdef";
// a b c d e f \0
printf("%zd\n", strlen(p));
//p是指针变量,向后在\0之前有6个字符
printf("%zd\n", strlen(p + 1));
//p是指针变量,p + 1向后在\0之前有5个字符
//printf("%zd\n", strlen(*p));
//p的类型是const char*,*p就是char类型,*p就是'a'-97,err
//printf("%zd\n", strlen(p[0]));
//p[0] -> *(p + 0) -> *p -> 得到的就是'a'-97.err
printf("%zd\n", strlen(&p));
//&p是指针变量p的地址,和字符串"abcdef"关系不大,从p这个指针变量的起始位置开始向后数的,p变量存放的地址是什么,不知道,所以是随机值
printf("%zd\n", strlen(&p + 1));
//和上面的情况一样了,随机值
printf("%zd\n", strlen(&p[0] + 1));
//p[0] -> *(p + 0) -> *p,&p[0] == &*p == p:首元素的地址
//&p[0]-取出字符串首字符的地址,+1是第二个字符的地址, 5
return 0;
}
2.3二维数组
#include<stdio.h>
int main()
{
int a[3][4] = { 0 };
printf("%zd\n", sizeof(a));
//a是二维数组的数组名,单独放在sizeof()中,计算的是整个数组的大小,3*4*sizeof(int) == 48个字节
printf("%zd\n", sizeof(a[0][0]));
//a[0][0] 是第一行第一个元素,大小是4个字节
printf("%zd\n", sizeof(a[0]));
//a[0]表示的是第一行的数组名,数组名单独放在sizeof内部了,计算的是数组的总大小 16 个字节
printf("%zd\n", sizeof(a[0] + 1));
//a[0] + 1表示的是第二行的数组名,数组名单独放在sizeof内部了,计算的是数组的总大小 16 个字节
printf("%zd\n", sizeof(*(a[0] + 1)));
//*(a[0] + 1)表示的是第一行第二个元素,大小就是4
//*(*(p + 0) + 1) == p[0][1]
printf("%zd\n", sizeof(a + 1));
//a表示数组首元素的地址,是二维数组首元素的地址,也就是第一行的地址,a+1,跳过一行,指向了第二行,a+1是第二行的地址,a+1是数组指针,是地址大小就是4/8个字节
printf("%zd\n", sizeof(*(a + 1)));
//1.a表示数组首元素的地址,是二维数组首元素的地址,也就是第一行的地址,a+1,跳过一行,指向了第二行,*(a+1)表示的是第二行,a+1是数组指针,计算的是第二行的大小 - 16字节
//2.*(a + 1) == a[1] -> 表示的是第二行的数组名,单独放在sizeof中,4*sizeof(int) == 16字节
printf("%zd\n", sizeof(&a[0] + 1));
//&a[0]取出的是第一行的数组名,&a[0] + 1指向的是第二行的地址,是地址大小就是4/8个字节
printf("%zd\n", sizeof(*(&a[0] + 1)));
//&a[0] + 1指向的是第二行的地址,*(&a[0] + 1)是对第二行的地址解引用,访问的就是第二行,大小是16字节
printf("%zd\n", sizeof(*a));
//1.a表示的是二维数组首元素的地址,计算的就是第一行的大小,16字节
//2.*a == *(a + 0) == a[0] ->第一行的数组名
printf("%zd\n", sizeof(a[3]));
//a[3]无需真实存在,仅仅通过类型的推断就能算出长度a[3]是第四行的数组名,单独放在sizeof内部,计算的是第四行的大小,16个字节
return 0;
}
数组名的意义:
- sizeof(数组名),这⾥的数组名表示整个数组,计算的是整个数组的大小。
- &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
- 除此之外所有的数组名都表表示首元素的地址。
3.指针运算笔试题解析
3.1 题目1
#include <stdio.h>
int main()
{
int a[] = { 1,2,3,4,5 };
int* ptr = (int*)(&a + 1);
printf("%d %d\n", *(a + 1), *(ptr - 1));
// 2 5
return 0;
}
//输出的结果是什么?
3.2 题目2
//在X86的环境下
//假设结构体的大小是20个字节
//输出的结果是什么?
#include <stdio.h>
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);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}
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]);//1
return 0;
}
3.4 题目4
//假设环境是x86环境,程序输出的结果是啥?
#include<stdio.h>
int main()
{
int a[5][5];
int(*p)[4];//p是一个数组指针,p指向的数组是4个整型元素
p = a;
printf("%p, %d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
// FFFFFFFC, -4
return 0;
}
3.5 题目5
#include<stdio.h>
int main()
{
int a[2][5] = { 1,2,3,4,5,6,7,8,9,10 };
int* ptr1 = (int*)(&a + 1);
int* ptr2 = (int*)(*(a + 1));
printf("%d, %d\n", *(ptr1 - 1), *(ptr2 - 1));
// 10, 5
return 0;
}
3.6 题目6
#include<stdio.h>
int main()
{
char* a[] = {"work", "at", "alibaba"};
char** pa = a;
pa++;
printf("%s\n", *pa);//at
return 0;
}
3.7 题目7
#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;
}