1、sizeof和strlen的对比
sizeof
sizeof计算变量所占内存内存空间⼤⼩的,单位是字节,如果操作数是类型的话,计算的是使⽤类型创建的变量所占内存空间的⼤⼩。简单来说,sizeof 只关注占⽤内存空间的⼤⼩,不在乎内存中存放什么数据。
#inculde <stdio.h>
int main()
{
int a = 10;
printf("%d\n", sizeof(a));
printf("%d\n", sizeof a);//从这里可以看出sizeof是操作符而不是函数,因为函数调用必须有函数调用操作符()。
printf("%d\n", sizeof(int));
return 0;
}
strlen
strlen是C语言中的一个库函数,功能是求字符串的长度。函数原型为:
size_t strlen ( const char * str );
strlen
统计的是从
strlen
函数的参数
str
中这个地址开始向后,
\0
之前字符串中字符的个数。如果找不到 \0 ,就会一直向后访问,直到找到为止,所以可能存在越界访问查找。
#include <stdio.h>
int main()
{
char arr1[3] = {'a', 'b', 'c'};
char arr2[] = "abc";
printf("%d\n", strlen(arr1));
printf("%d\n", strlen(arr2));
printf("%d\n", sizeof(arr1));
printf("%d\n", sizeof(arr1));
return 0;
}
如果多次输出结果,你会发现第一个值会不断变化,并且是随机的。这里就体现了strlen的性质,因为arr1中存放的只有abc三个字符,后面没有跟 \0 ,所以函数没有停止,会在c的地址继续向后访问。
sizeof 和 strlen的对比
sizeof1. sizeof是操作符2. sizeof计算操作数所占内存的⼤⼩,单位是字节(指针大小:32位:4 /64位:8)3. 不关注内存中存放什么数据
strlen
1. strlen是库函数,使⽤需要包含头⽂件 string.h2. srtlen是求字符串⻓度的,统计的是 \0 之前字符的隔个数3. 关注内存中是否有 \0 ,如果没有 \0 ,就会持续往后找,可能会越界
数组和指针笔试题分析
一维数组
int a[] = {1,2,3,4};
//arr是有四个整型元素的数组
printf("%d\n",sizeof(a));
//sizeof(a)是计算的整个数组的大小,16个字节
printf("%d\n",sizeof(a+0));
//sizeof(a+0)是计算的数组首元素的地址的大小,4/8个字节
printf("%d\n",sizeof(*a));
//sizeof(*a)是计算的数组首元素的大小,4个字节
printf("%d\n",sizeof(a+1));
//sizeof(a+1)是计算的数组第二个元素的地址的大小,4/8个字节
printf("%d\n",sizeof(a[1]));
//sizeof(a[1]) == sizeof(*(a+1))
//计算的是数组第二个元素的大小,4个字节
printf("%d\n",sizeof(&a));
//sizeof(&a)是计算的整个数组的地址的大小,4/8个字节
printf("%d\n",sizeof(*&a));
//sizeof(*&a) == sizeof(a)
//计算的是整个数组的大小,16个字节
printf("%d\n",sizeof(&a+1));
//sizeof(&a+1)计算的是a数组后相同空间的地址的大小,4/8个字节
printf("%d\n",sizeof(&a[0]));
//sizeof(&a[0])是计算的第一个元素的地址的大小,4/8个字节
printf("%d\n",sizeof(&a[0]+1));
//sizeof(&a[0]+1)是计算的第二个元素的地址的大小,4/8个字节
字符数组
代码段1:
char arr[] = {'a','b','c','d','e','f'};
//arr数组是存放了a~f 6个字符的数组
printf("%d\n", sizeof(arr));
//sizeof(arr)计算的是整个数组的大小,6个字节
printf("%d\n", sizeof(arr+0));
//sizeof(arr+0)计算的是数组首元素的地址的大小,4/8个字节
printf("%d\n", sizeof(*arr));
//sizeof(*arr)计算的是数组首元素的大小,1个字节
printf("%d\n", sizeof(arr[1]));
//sizeof(arr[1]) == sizeof(*(arr+1))
//计算的是数组第二个元素的大小,1个字节
printf("%d\n", sizeof(&arr));
//sizeof(&arr)计算的是整个数组的地址的大小,4/8个字节
printf("%d\n", sizeof(&arr+1));
//sizeof(&arr+1)计算的是数组地址后与数组大小相同的空间内的地址的大小,4/8个字节
printf("%d\n", sizeof(&arr[0]+1));
//sizeof(&arr[0]+1)计算的是数组第二个元素的地址的大小,4/8个字节
代码段2:
char arr[] = {'a','b','c','d','e','f'};
//arr数组是存放了a~f 6个字符的字符数组
printf("%d\n", strlen(arr));
//strlen(arr)计算的是从字符a开始到首个\0之前的元素个数,因为\0位置随机,所以结果为随机值x。
printf("%d\n", strlen(arr+0));
//strlen(arr+0)计算的是从字符a开始到首个\0之前的元素个数,因为\0位置随机,所以结果为随机值x。
printf("%d\n", strlen(*arr));
//strlen(*arr)表示的是a字符即ascll值97的地址位置为起始,首个\0之前的元素个数,但因为起始地址为不可访问地址,所以会报错,运行失败。
printf("%d\n", strlen(arr[1]));
//strlen(arr[1])表示的是b字符即ascll值98的地址位置为起始,首个\0之前的元素个数,但因为起始地址为不可访问地址,所以会报错,运行失败。
printf("%d\n", strlen(&arr));
//strlen(&arr):&arr表示整个数组的地址,同样表示数组首元素的地址,所以计算的是从字符a开始到首个\0之前的元素个数,因为\0位置随机,所以结果为随机值x。
printf("%d\n", strlen(&arr+1));
//strlen(&arr+1):&arr+1表示数组后与数组空间相同大小的内存处的地址,所以计算的是从字符f后一个位置开始到首个\0之前的元素个数,因为\0位置随机,所以结果为随机值x-6。
printf("%d\n", strlen(&arr[0]+1));
//strlen(&arr[0]+1):&arr[0]+1表示的是数组第二个元素的地址,所以计算的是从字符b开始到首个\0之前的元素个数,因为\0位置随机,所以结果为随机值x-1。
将两个报错代码屏蔽后:
字符串数组
代码段1:
char arr[] = "abcdef";
//arr数组是存放了字符串“abcdef\0”的字符串数组。
printf("%d\n", sizeof(arr));
//sizeof(arr)计算的是整个数组的元素大小,7个字节
printf("%d\n", sizeof(arr+0));
//sizeof(arr+0)计算的是数组首元素的地址的大小,4/8个字节
printf("%d\n", sizeof(*arr));
//sizeof(*arr)计算的是数组首元素的大小,1个字节
printf("%d\n", sizeof(arr[1]));
//sizeof(arr[1])计算的是数组第二个元素的大小,1个字节
printf("%d\n", sizeof(&arr));
//sizeof(&arr+1)表示的是整个数组的地址的大小,4/8个字节
printf("%d\n", sizeof(&arr+1));
//sizeof(&arr+1)表示的是整个数组地址+1的地址的大小,4/8个字节
printf("%d\n", sizeof(&arr[0]+1));
//sizeof(&arr[0]+1))表示的是数组第二个元素的地址,4/8个字节
代码段2:
char arr[] = "abcdef";
//arr数组是存放了字符串“abcdef\0”的字符串数组。
printf("%d\n", strlen(arr));
//strlen(arr)计算的是从a字符到第一个\0之前的元素个数,因为字符串末尾隐藏了一个\0,所以大小为6
printf("%d\n", strlen(arr+0));
//strlen(arr+0)计算的是从数组首元素的地址到\0之前的元素个数,大小为6
printf("%d\n", strlen(*arr));
//strlen(*arr)计算的是数组从以首元素的ascll值为地址的元素到第一个\0之前的元素个数,因为该地址不可访问,所以会报错。
printf("%d\n", strlen(arr[1]));
//计算的是数组从以第二个元素的ascll值为地址的元素到第一个\0之前的元素个数,因为该地址不可访问,所以会报错。
printf("%d\n", strlen(&arr));
//strlen(&arr):表示的是整个数组的地址到第一个\0之前的元素个数,整个数组的地址 == 首元素地址。
//大小为6
printf("%d\n", strlen(&arr+1));//因为起始位置是字符串末尾的\0之后,下一个\0位置未知,所以结果为随机值
printf("%d\n", strlen(&arr[0]+1));
//strlen(&arr[0]+1)表示的是数组第二个元素到字符串末尾\0之前的元素个数,大小为5.
将报错代码注释掉后:
代码段3:
char *p = "abcdef";
//p为指针变量,指向的是字符串的首元素a的地址。
printf("%d\n", sizeof(p));
//sizeof(p):p为指针变量,所以大小为4/8个字节
printf("%d\n", sizeof(p+1));
//sizeof(p+1)表示的是字符串中第二个元素b的地址的大小,4/8个字节
printf("%d\n", sizeof(*p));
//sizeof(*p)表示的是字符串首元素a的大小,1个字节(char)
printf("%d\n", sizeof(p[0]));
//sizeof(p[0])表示的是a的大小,1个字节(char)
printf("%d\n", sizeof(&p));
//sizeof(&p)表示的是指针变量p的地址的大小,4/8个字节
printf("%d\n", sizeof(&p+1));
//表示的是p的地址+1处的地址的大小,4/8个字节
printf("%d\n", sizeof(&p[0]+1));
//sizeof(&p[0]+1)表示的是字符串内第二个元素的地址的大小,4/8个字节
代码段4:
char *p = "abcdef";
//p是指针变量,指向字符串的元素的的地址
printf("%d\n", strlen(p));
//strlen(p)计算的是以p指针指向的地址为起始位置,到第一个\0之前的元素个数,大小为6
printf("%d\n", strlen(p+1));
//strlen(p+1)计算的是以p指针+1指向的地址为起始位置,到第一个\0之前的元素个数,大小为5
printf("%d\n", strlen(*p));//报错,解释同前
printf("%d\n", strlen(p[0]));//报错,解释同前
printf("%d\n", strlen(&p));
//strlen(&p)表示的是p指针变量的地址,\0位置不确定,所以大小为随机值
//从p这个指针变量的起始位置开始向后数的,p变量存放的地址是什么,不知道,所以答案是随机值
printf("%d\n", strlen(&p+1));
//strlen(&p+1)表示的是p指针变量+1的地址,\0位置不确定,所以大小为随机值
printf("%d\n", strlen(&p[0]+1));
//strlen(&p[0]+1)表示的是b的地址,计算的是以b的地址为起始位置,到第一个\0之前的元素个数,大小为5
二维数组
二维数组名的意义:1. sizeof(数组名),这⾥的数组名表⽰整个二维数组,计算的是整个二维数组的⼤⼩。2. &数组名,这⾥的数组名表⽰整个二维数组,取出的是整个二维数组的地址。3. 除此之外所有的数组名都表⽰⾸元素(首行)的地址。
int a[3][4] = {0};
//a是三行四列的二维数组,并且全部初始化为0
printf("%d\n",sizeof(a));
//sizeof(a)计算的是整个二维数组的大小,12*4 = 48个字节
printf("%d\n",sizeof(a[0][0]));
//sizeof(a[0][0])计算的是数组首行首元素的大小,大小为4个字节
printf("%d\n",sizeof(a[0]));
//sizeof(a[0])计算的是数组首行的元素的大小,大小为16个字节
printf("%d\n",sizeof(a[0]+1));
//sizeof(a[0]+1)计算的是数组首行第二个元素的地址的大小,大小为4/8个字节
printf("%d\n",sizeof(*(a[0]+1)));
//计算的是数组首行第二个元素的大小,4个字节
printf("%d\n",sizeof(a+1));
//sizeof(a+1)计算的是数组第二行的地址的大小4/8个字节
printf("%d\n",sizeof(*(a+1)));
//sizeof(*(a+1))计算的是数组第二行的元素的大小,16个字节
printf("%d\n",sizeof(&a[0]+1));
//sizeof(&a[0]+1)计算的是数组第二行的地址的大小,4/8个字节
printf("%d\n",sizeof(*(&a[0]+1)));
//sizeof(*(&a[0]+1))计算的是数组第二行元素的大小,16个字节
printf("%d\n",sizeof(*a));
//sizeof(*a)计算的是数组首行元素的大小,16个字节
printf("%d\n",sizeof(a[3]));
//表示的是数组越界访问的第四行的元素大小,16个字节
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 5
题目2:
// 在 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 ;}
题目三:
# 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 ;}这道题很容易让人进坑。题目中给的数组是由三个逗号表达式组成的数组,本质上是赋了3个初值,如果你当成了6个数,那就翻车了。结果:1
题目四:
// 假设环境是 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是int (*)[4]类型,说明p只将a的前四行地址保存了下来。