深入理解指针(5)
目录
1.sizeof和strlen的对比
1.1sizeof
sizeof计算变量所占内存内存空间大小的,单位是字节,如果操作数是类型的话,计算的是使用类型创建的变量所占内存空间的大小。
sizeof只关注占用内存空间的大小,不在乎内存中存放什么数据。
1.2strlen
strlen是C语言库函数,功能是求字符串长度。
函数原型:
1.size_t strlen ( const char * str )
统计的是从strlen函数的参数str中这个地址开始向后,\0之前的字符串中字符的个数。
strlen函数会一直向后找\0字符,直到找到为止,所以可能存在越界查找。
1.3sizeof和strlen的对比
sizeof:
- sizeof是操作符
- sizeof计算操作数所占内存的大小,单位是字节
- 不关注内存中存放什么数据
strlen:
- strlen是库函数,使用需要包含头文件string.h
- strlen是求字符串长度的,统计的是\0之前的字符的个数
- 关注内存中是否有\0,如果没有\0,就会持续往后找,可能会越界
2.数组和指针笔试题解析
2.1一维数组
#include <stdio.h>
int main()
{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));return 0;
}
数组名是数组首元素的地址,但是有两个例外:
- sizeof(数组名)
- &数组名--这里的数组名表示整个数组,取出的是数组的地址
int a[] = { 1,2,3,4 };
1.printf("%zd\n", sizeof(a));//16--数组名a单独放到sizeof内部,a表示整个数组,计算的是整个数组的大小,单位是字节
2.printf("%zd\n", sizeof(a + 0));//4/8--这里的a表示首元素的地址,a+0还是首元素的地址
3.printf("%zd\n", sizeof(*a));//4--这里的a表示首元素的地址,*a就是首元素--a[0]
4.printf("%zd\n", sizeof(a + 1));//4/8--这里的a表示首元素的地址,a+1是第二个元素的地址
5.printf("%zd\n", sizeof(a[1]));//4--第二个元素,第二个元素大小是4个字节
6.printf("%zd\n", sizeof(&a));//4/8--这里的数组名表示整个数组,&a是整个数组的地址,数组的地址也是地址
7.printf("%zd\n", sizeof(*&a));//16--*和&相互抵消 => sizeof(a),a表示整个数组,计算的是整个数组的大小,单位是字节
8.printf("%zd\n", sizeof(&a + 1));//4/8--&a是数组的地址。&a+1是跳过整个数组后那个位置的地址,但它也是地址
9.printf("%zd\n", sizeof(&a[0]));//4/8--a[0]是首元素,对首元素取地址也是地址
10.printf("%zd\n", sizeof(&a[0] + 1));//4/8--第二个元素的地址
2.2字符数组
代码1:
#include <stdio.h>
int main()
{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));return 0;
}
char arr[] = { 'a','b','c','d','e','f' };
1.printf("%d\n", sizeof(arr));//6--这里的数组名表示整个数组,单位字节,没有\0
2.printf("%d\n", sizeof(arr + 0));//4/8--这里的arr表示首元素的地址,arr+0还是首元素的地址
3.printf("%d\n", sizeof(*arr));//1--这里的arr表示首元素的地址,*arr就是首元素,首元素的大小是1个字节
4.printf("%d\n", sizeof(arr[1]));//1--arr[1]是数组的第二个元素,第二个元素的大小是1个字节
5.printf("%d\n", sizeof(&arr));//4/8--这里的arr表示整个数组,取出整个数组的地址
6.printf("%d\n", sizeof(&arr + 1));//4/8--这里的arr表示整个数组,取出数组的地址+1,跳过整个数组,但也是地址
7.printf("%d\n", sizeof(&arr[0] + 1));//4/8--arr[0]是首元素,&arr[0]是首元素地址,+1表示第二个元素的地址
代码2:
#include <stdio.h>
#include <string.h>
int main()
{char arr[] = { 'a','b','c','d','e','f' };printf("%zd\n", strlen(arr));printf("%zd\n", strlen(arr + 0));printf("%zd\n", strlen(*arr));printf("%zd\n", strlen(arr[1]));printf("%zd\n", strlen(&arr));printf("%zd\n", strlen(&arr + 1));printf("%zd\n", strlen(&arr[0] + 1));return 0;
}
strlen是计算字符串中‘\0’之前的字符的个数的
char arr[] = { 'a','b','c','d','e','f' };
1.printf("%zd\n", strlen(arr));//随机值--这里的arr是首元素的地址,arr中没有\0,所以strlen会一直往后找,直到找到\0
2.printf("%zd\n", strlen(arr + 0));//随机值--这里的arr是首元素的地址,arr+0还是首元素的地址,没有\0,所以会一直往后找
3.printf("%zd\n", strlen(*arr));//程序崩溃--这里的arr是首元素的地址,*arr是首元素,‘a’的ACCLL码值是97,strlen是地址,我们把97传进去,strlen就会把97当成地址进行访问,程序直接崩溃
4.printf("%zd\n", strlen(arr[1]));//程序崩溃--arr[1]是第二个元素,‘b’的ACCLL码值是98,把98传给strlen,strlen会把98当做地址进行访问,程序崩溃
5.printf("%zd\n", strlen(&arr));//随机值--这里的arr是整个数组,取整个数组的地址,strlen还是会往后找\0
6.printf("%zd\n", strlen(&arr + 1));//随机值--这里的arr表示整个数组,arr+1跳过整个数组,后面不知道有没有\0,随机值,和前面从首元素开始找随机值差6,所以跳过了整个数组
7.printf("%zd\n", strlen(&arr[0] + 1));//随机值--取出首元素地址+1,第二个元素地址开始往后找\0,比从首元素地址开始的少1
代码3:
#include <stdio.h>
int main()
{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));return 0;
}
char arr[] = "abcdef";
1.printf("%zd\n", sizeof(arr));//7--arr整个数组的大小
2.printf("%zd\n", sizeof(arr + 0));//4/8--arr数组首元素的地址,arr+0还是首元素的地址
3.printf("%zd\n", sizeof(*arr));//1--arr是数组首元素的地址,*arr首元素
4.printf("%zd\n", sizeof(arr[1]));//1--数组第二个元素
5.printf("%zd\n", sizeof(&arr));//4/8--arr表示整个数组,&arr整个数组的地址
6.printf("%zd\n", sizeof(&arr + 1));//4/8--arr表示整个数组,&arr整个数组的地址,&arr+1跳过整个数组的地址,还是地址
7.printf("%zd\n", sizeof(&arr[0] + 1)); //4/8--&arr[0]是首元素的地址,&arr[0]+1第二个元素的地址
代码4:
#include <stdio.h>
#include <string.h>
int main()
{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));return 0;
}
char arr[] = "abcdef";
1.printf("%d\n", strlen(arr));//6--arr首元素的地址,有\0
2.printf("%d\n", strlen(arr + 0));//6--arr表示首元素的地址,arr+0还是首元素
3.printf("%d\n", strlen(*arr));//程序崩溃--arr表示首元素的地址,*arr首元素,‘a’的ACCLL码值是97,将97传给strlen,strlen会将97当成地址访问,程序崩溃
4.printf("%d\n", strlen(arr[1]));//程序崩溃--arr[1]表示第二个元素,‘b’的ACCLL码值是98,将98传给strlen,strlen会将98当成地址访问,程序崩溃
5.printf("%d\n", strlen(&arr));//6--arr整个数组,&arr整个数组的地址传给strlen
6.printf("%d\n", strlen(&arr + 1));//随机值--&arr+1跳过整个数组,会跳过\0
7.printf("%d\n", strlen(&arr[0] + 1)); //5--取第一个元素+1
代码5:
#include <stdio.h>
int main()
{char* p = "abcdef";printf("%zd\n", sizeof(p));printf("%zd\n", sizeof(p + 1));printf("%zd\n", sizeof(*p));printf("%zd\n", sizeof(p[0]));printf("%zd\n", sizeof(&p));printf("%zd\n", sizeof(&p + 1));printf("%zd\n", sizeof(&p[0] + 1));return 0;
}
char* p = "abcdef";
1.printf("%zd\n", sizeof(p));//4/8--p是指针变量,计算的是指针变量的大小,指针变量就是存放地址的
2.printf("%zd\n", sizeof(p + 1));//4/8--p+1是第二个元素的地址
3.printf("%zd\n", sizeof(*p));//1--p的类型是char*,*p只能访问一个字节
4.printf("%zd\n", sizeof(p[0]));//1--p[0]->*(p+0)->*p,和上面一样
5.printf("%zd\n", sizeof(&p));//4/8--&p是指针变量的地址,&p->char**->二级指针
6.printf("%zd\n", sizeof(&p + 1));//4/8--&p是p的地址,&p+1是跳过p变量,但还是地址
7.printf("%zd\n", sizeof(&p[0] + 1));//4/8--p[0]是a,取出a的地址+1,指向b的地址
代码6:
#include <stdio.h>
#include <string.h>
int main()
{char* p = "abcdef";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));return 0;
}
char* p = "abcdef";
1.printf("%d\n", strlen(p));//6--p里面存放的是a的地址,将a的地址传给strlen
2.printf("%d\n", strlen(p + 1));//5--p+1里面存放的是b的地址
3.printf("%d\n", strlen(*p));//程序崩溃--p里面存放的是a的地址,*p是‘a’,‘a’的ACCLL码值是97,将97传给strlen,strlen会将97当成地址访问,程序崩溃
4.printf("%d\n", strlen(p[0]));//程序崩溃--p[0]->*(p+0)->*p
5.printf("%d\n", strlen(&p));//随机值--&p取出变量p的地址,类型是char**
6.printf("%d\n", strlen(&p + 1));//随机值--&p取出变量p的地址,&p+1跳过一个指针变量
7.printf("%d\n", strlen(&p[0] + 1));//5--&p[0]->p,p+1指向b的地址
2.3二维数组
#include <stdio.h>
int main()
{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]));return 0;
}
int a[3][4] = { 0 };
1.printf("%zd\n", sizeof(a));//48--a表示整个数组,计算的是整个数组的大小,单位是字节
2.printf("%zd\n", sizeof(a[0][0]));//4--a[0][0]是第一行第一个元素
3.printf("%zd\n", sizeof(a[0]));//16--a[0]表示一整个一维数组的大小
4.printf("%zd\n", sizeof(a[0] + 1));//4/8--a[0]是第一行的数组名,但是没有单独放在sizeof内部,那么只能是数组首元素的地址,那就是第一行第一个元素的地址,+1则是第一行第二个数的地址
5.printf("%zd\n", sizeof(*(a[0] + 1)));//4--是第一行第二个元素
6.printf("%zd\n", sizeof(a + 1));//4/8--a是二维数组的数组名,a表示首元素的地址,也就是第一行的地址,a+1则是第二行的地址
7.printf("%zd\n", sizeof(*(a + 1)));//16--第二行整个数组的大小
8.printf("%zd\n", sizeof(&a[0] + 1));//4/8--a[0]是第一行的数组名,&a[0]是第一行的地址,&a[0]+1是地址
9.printf("%zd\n", sizeof(*(&a[0] + 1)));//16--&a[0]+1第二行的地址,*(&a[0]+1)是第二行整个数组的大小
10.printf("%zd\n", sizeof(*a));//16--a是第一行的地址,*a第一行的值
11.printf("%zd\n", sizeof(a[3])); //没有越界,a[3]是第四行的数组名,int[4]sizeof不会去真实计算,根据类型推算
3.指针运算笔试题解析
题目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:
#include <stdio.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);printf("%p\n", (unsigned long)p + 0x1);printf("%p\n", (unsigned int*)p + 0x1);return 0;
}
1.printf("%p\n", p + 0x1);//p是结构体指针,+1就是跳过结构体,跳过20个字节,转十六进制0x14
2.printf("%p\n", (unsigned long)p + 0x1);//整数+1,0x100001
3.printf("%p\n", (unsigned int*)p + 0x1); //=1是跳过一个unsigned int*类型变量,是4个字节
题目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;
}
题目4:
//假设环境是x86环境,程序输出的结果是啥?
#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;
}
小地址-大地址=&p[4][2]-&a[4][2]=-4
1000000 0000000 0000000 0000100 --原码
11111111 11111111 11111111 11111011 --反码
11111111 11111111 11111111 11111100 --补码
FF FF FF FC
题目5:
#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;
}
题目6:
#include <stdio.h>
int main()
{char* a[] = { "work","at","alibaba" };char** pa = a;pa++;printf("%s\n", *pa);return 0;
}
题目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;
}