目录
1. sizeof和strlen的比较
1.1 sizeof
操作符sizeof是变量所占内存空间大小的,单位是字节,如果操作数是类型,计算的是类型创建的变量所占内存空间的大小
sizeof只关注占用内存空间的大小,不在乎内存中存放什么数据
int a=10;
sizeof(a);
sizeof a;
sizeof(int)
上面三个计算的大小是一样的,都是计算变量a所占空间大小。当操作数是变量的时候,可以省略括号
sizeof里的表达式不会计算
int a = 10;
printf("%d\r\n", sizeof(a=a+12));
printf("%d\r\n", a);
a的结果仍为10
1.2 strlen
strlen是库函数,功能是求字符串长度
函数原型如下:
size_t strlen (const char* str);
统计的是从strlen参数str这个地址开始向后,\0之前字符串字符的个数
strlen会一直往后找\0,找到才停,所以可能存在越界查找
char arr1[3]={'a','b','c'};
char arr2[]="abc";
printf("%d\r\n", strlen(arr1));
printf("%d\r\n", strlen(arr2));
printf("%d\r\n", sizeof(arr1));
printf("%d\r\n", sizeof(arr2));
结果:
arr1数组由于\0的位置不确定,所以打印的是随机值,arr2统计字符串长度3。arr1有三个字符,所以大小3字节,第二个还有\0,实际占用4个字节。
字符串数组中\0也会占一个空间,所以申请时需要包含\0的大小,char str[3]=“abc”,这样不包含\0,字符串找不到\0会打印错误
1.3 sizeof和strlen的对比
2. 数组和指针笔试题解析
2.1 一维数组
int a[ ] = {1,2,3,4};
(1) printf (“%d\n”,sizeof (a) );
(2) printf (“%d\n”,sizeof (a+0) );
(3) printf (“%d\n”,sizeof (a) );
(4) printf (“%d\n”,sizeof (a+1) );
(5) printf (“%d\n”,sizeof (a[1]) );
(6) printf (“%d\n”,sizeof (&a) );
(7) printf (“%d\n”,sizeof (&a) );
(8) printf (“%d\n”,sizeof (&a+1) );
(9) printf (“%d\n”,sizeof (&a[0]) );
(10) printf (“%d\n”,sizeof (&a[0]+1) );
两种特殊情况:
- sizeof (数组名),表示整个数组,是整个数组大小,单位字节
- &数组名,取出整个数组的地址
(1) 16,sizeof+数组名,是整个数组的大小
(2) 4/8,数组名表示首地址,+0是第一个元素的地址
(3) 4,*取数组第一个元素,int是4字节
(4) 4/8,和(2)相同,是数组第二个元素地址
(5) 4,表示第二个元素int
(6) 4/8,取出数组的地址,地址都是看环境
(7) 16,*和&抵消,和(1)一样
(8) 4/8,取出数组地址+1,跳过整个数组大小,指向数组后第一个地址
(9) 4/8,取第一个元素的地址
(10) 4/8,取第二个元素的地址
2.2 字符数组
题目1
char arr[ ] = { ‘a’,‘b’,‘c’,‘d’,‘e’,‘f’ };
(1) printf (“%d\n”,sizeof (arr) );
(2) printf (“%d\n”,sizeof (arr+0) );
(3) printf (“%d\n”,sizeof (*arr) );
(4) printf (“%d\n”,sizeof (arr[1]) );
(5) printf (“%d\n”,sizeof (&arr) );
(6) printf (“%d\n”,sizeof (&arr+1) );
(7) printf (“%d\n”,sizeof (&arr[0]+1) );
(1) 6,数组的大小
(2) 4/8,第一个元素地址
(3) 1,第一个元素,char
(4) 1,第二个元素,char
(5) 4/8,数组地址
(6) 4/8,数组后第一个地址
(7) 4/8,第二个元素地址
题目2
char arr[ ] = { ‘a’,‘b’,‘c’,‘d’,‘e’,‘f’ };
(1) printf (“%d\n”,strlen(arr) );
(2) printf (“%d\n”,strlen(arr+0) );
(3) printf (“%d\n”,strlen(*arr) );
(4) printf (“%d\n”,strlen(arr[1]) );
(5) printf (“%d\n”,strlen(&arr) );
(6) printf (“%d\n”,strlen(&arr+1) );
(7) printf (“%d\n”,strlen(&arr[0]+1) );
(1) 随机值,数组地址开始,遇到\0停止
(2) 随机值,数组地址开始,遇到\0停止,同(1)
(3) 崩溃,第一个元素’a’的ascii97当做地址,访问错误
(4) 崩溃,第二个元素,ascii98当做地址,访问错误
(5) 随机值,从数组地址开始,遇到\0停止,(同1)
(6) 随机值,从数组后的地址开始,遇到\0停止
(7) 随机值,第二个地址开始,遇到\0停止
题目3
char arr[ ] = “abcdef”;
(1) printf (“%d\n”,sizeof(arr) );
(2) printf (“%d\n”,sizeof(arr+0) );
(3) printf (“%d\n”,sizeof(*arr) );
(4) printf (“%d\n”,sizeof(arr[1]) );
(5) printf (“%d\n”,sizeof(&arr) );
(6) printf (“%d\n”,sizeof(&arr+1) );
(7) printf (“%d\n”,sizeof(&arr[0]+1) );
实际上存储的是: a b c d e f \0
(1) 7,计算整个数组大小
(2) 4/8,首元素的地址
(3) 1,首元素,char类型
(4) 1,第二个元素的大小
(5) 4/8,整个数组的地址
(6) 4/8,跳过整个数组的地址
(7) 4/8,第二个元素的地址
题目4
char arr[ ] = “abcdef”;
(1) printf (“%d\n”,strlen(arr) );
(2) printf (“%d\n”,strlen(arr+0) );
(3) printf (“%d\n”,strlen(*arr) );
(4) printf (“%d\n”,strlen(arr[1]) );
(5) printf (“%d\n”,strlen(&arr) );
(6) printf (“%d\n”,strlen(&arr+1) );
(7) printf (“%d\n”,strlen(&arr[0]+1) );
实际上存储的是: a b c d e f \0
(1) 6,\0之前的字符数
(2) 6,同(1)
(3) err,首元素,char类型,改地址不可访问
(4) err,第二个元素,同上
(5) 6,整个数组的地址,char类型与char[]级别不同,会警告,但也是首元素的地址
(6) 随机值,跳过整个数组的地址,未知
(7) 5,第二个元素的地址开始找\0
题目5
char *p = “abcdef”;
(1) printf (“%d\n”,sizeof(p ) );
(2) printf (“%d\n”,sizeof(p +1) );
(3) printf (“%d\n”,sizeof(*p ) );
(4) printf (“%d\n”,sizeof(p [0]) );
(5) printf (“%d\n”,sizeof(&p ) );
(6) printf (“%d\n”,sizeof(&p +1) );
(7) printf (“%d\n”,sizeof(&p [0]+1) );
实际上存储的是: a b c d e f \0
(1) 4/8,指针变量的大小
(2) 4/8,首地址加1,第二个元素地址
(3) 1,第一个元素大小
(4) 1,同上
(5) 4/8,指针的地址
(6) 4/8,指针类型char**,跳过一个char*的地址,跳过4个字节
(7) 4/8,第二个元素的地址
题目6
char *p = “abcdef”;
(1) printf (“%d\n”,strlen(p ) );
(2) printf (“%d\n”,strlen(p +1) );
(3) printf (“%d\n”,strlen(*p ) );
(4) printf (“%d\n”,strlen(p [0]) );
(5) printf (“%d\n”,strlen(&p ) );
(6) printf (“%d\n”,strlen(&p +1) );
(7) printf (“%d\n”,strlen(&p [0]+1) );
实际上存储的是: a b c d e f \0
(1) 6,\0之前的字符数
(2) 5,第二个字符开始计长度
(3) err,首元素,char类型,该地址不可访问
(4) err,第二个元素,同上
(5) 随机,p的地址
(6) 随机值,p的地址+1
(7) 5,第二个元素的地址开始找\0
总结:
- ary[]的形式,判断sizeof里面是不是单放数组名,单放表示整个数组,有其他则表示数组首元素
- &数组名,是取的整个数组大小,但仍为指针类型
- strlen的参数是地址,不是地址会转为地址,地址不对会崩溃
- ary[]取ary的地址仍是ary,只不过类型变为**,二级指针
- char* p的形式仍为指针,大小仍为4/8
2.3 二维数组
int a[3][4] = {0};
(1) printf (“%d\n”,sizeof (a));
(2) printf (“%d\n”,sizeof (a[0][0]));
(3) printf (“%d\n”,sizeof (a[0]));
(4) printf (“%d\n”,sizeof (a[0]+1));
(5) printf (“%d\n”,sizeof ((a[0]+1)));
(6) printf (“%d\n”,sizeof (a+1));
(7) printf (“%d\n”,sizeof ((a+1)));
(8) printf (“%d\n”,sizeof (&a[0]+1));
(9) printf (“%d\n”,sizeof (*(&a[0]+1)));
(10) printf (“%d\n”,sizeof (*a));
(11) printf (“%d\n”,sizeof (a[3]));
(1) 48,数组的大小,12个元素,每个int
(2) 4,第一个元素的大小
(3) 16,第一行元素的数组名,一行4个元素,int
(4) 4/8,第一行数组首元素地址,+1就是第二个元素的地址
(5) 4,第二个元素,等价于a[0][1]
(6) 4/8,a是数组首元素的地址,+1是第二行的地址
(7) 16,第一行的地址解引用,就是第一行的数组大小
(8) 4/8,第一行地址+1是数组第二行的地址
(9) 16,第二行地址解引用,是数组第二行大小
(10) 16,数组首地址解引用是第一行的数组大小
(11) 16,sizeof只判断类型,不会计算,不会错误,是第三行数组类型大小
3. 指针运算
题目1
int a[5] = {1,2,3,4,5};
int* ptr = (int*) (&a + 1);
printf("%d %d",*(a+1),*(ptr-1));
//输出结果是什么
分析
2,5
&a取整个数组,+1跳过整个数组,转为int*指针。a+1是首元素地址+1,第二个元素地址解引用,是2,ptr是-1,数组后面指针减一个int是最后一个元素地址,解引用5
验证
题目2
//x86环境下
//假设结构体大小20字节
//程序输出是什么
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p = (struct Test*)0x100000;
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
这种写法意思是创建了一个指针变量,将100000地址转为结构体指针
分析
0x100014,0x100001,0x100004
p指向结构体,+1加一个结构体大小20,转换为long型值,加1就是值加1,int*指针,加1就是4字节
验证
题目3
![int a\[3\]\[2\] = { (0, 1), (2, 3), (4, 5) };
int *p;
p = a\[0\];
printf( "%d", p\[0\]);](https://img-blog.csdnimg.cn/7f0e12c7de14402eb8240c5e704b1b84.png)
分析
1
数组要用中括号初始化,()是逗号表达式,结果为最后一个,所以数组元素为{1,3,5}
a[0]是第一行数组的地址,再取[0]是第一个元素
验证
题目4
//假设环境是x86环境,程序输出的结果是啥?
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]);
分析
FFFFFFFC,-4
p是数组指针,指向有4个元素的数组,p[4]就是加4个长度解引用,每次加4个元素的长度,就是第4个元素的地址,p[4][2]在之前的地址加2个int的地址长度解引用,取地址。
深蓝色箭头是a[4][2],浅蓝色是p[4][2],指针减指针是数,相减就是-4个元素
%d打印-4,但%p需要打印地址,存的什么就打印什么,将-4的补码打印为十六进制,FF FF FF FC
验证
题目5
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));
分析
10,5
&arr是整个数组地址,+1越过整个数组,转为int*
aa是数组首元素,+1解引用是数组第二行的地址,转为int*
ptr1减1回到最后一个元素,解引用
ptr2-1第二行地址,此时为int*指针,减1是数组第一行最后一个元素地址,解引用
验证
题目6
char *a[] = {"work","at","alibaba"};
char**pa = a;
pa++;
printf("%s\n", *pa);
分析
at
a是char类型的数组,有3个元素,每个元素是char
pa是二级指针,指针类型是char*,指向a的地址
pa++加该类型大小,类型为char*,加一个char*就是a第二个元素字符串的地址
验证
题目7
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);
分析
POINT,ER,ST,EW
指向下面是数组c的地址,里面存储的是四个字符串的地址,但需要解引用才能得到
下面是cp的地址,存储的是c值+数字的值,需要两次解引用才能得到对应的字符串
cpp是cp的地址,同cp需要两次解引用访问字符串
cpp加1,加一个char**,c+2的地址,解引用取到c+2元素,再解引用,取到c+2指向的字符串
cpp目前指向c+2的地址,再++到c+1元素的地址,解引用得到c+1,再–是c的地址,解引用是c[0] ENTER的地址,+3加到E,打印
cpp目前在c+1的地址,-2到c+3元素的地址,解引用到c+3,解引用得到c+3存储的字符串的首地址,再+3字符S的地址,打印
cpp在c+1,-1到c+2解引用得到c+2的地址,c+2再减1得到c+1解引用到NEW字符串的地址,加1到E打印
验证