1. 指针和数组笔试题解析
- 一维数组sizeof()
- sizeof计算是对象占内存大小,单位是字节,
- 数组名是首元素的地址
sizeof有两个例外:
- sizeof(数组名),数组名表示整个数组,计算的是整个数组大小
- &数组名 取出的是整个数组地址,sizeof(&arr) = 4或8 , 而第7行,结果会是整个数组大小, sizeof(*&arr);对整个数组的地址解引用了
简言之,arr单独放或取地址&arr是特殊的
所以第一行,对应例外1,得到16
第二行:sizeof对a+0,不是第一种,不是第二种,所以就是第一个元素1的地址,+0,还是它自己,所以1的地址大小,因为整型就是4字节(32位)。
第三行:sizeof(*a)中,*a不是例外1,也不是例外2,所以是第一个元素大小,结果也为4字节
第四行:sizeof(a+1),a+1是第二个元素地址的大小为4,其实地址的大小也就是它自己的大小,地址多大,存多大元素
第五行:很普通
第六行:例外第二种,它虽然取出的是整个数组,但它的值也是个地址大小,为4
第七行:sizeof(*&a):取地址得到数组地址,对数组解引用,拿到整个数组,所以是16。 其实 取地址&和解引用*结合,相当于直接sizeof(arr);
第八行: sizeof(&a+1):&a拿到数组地址,数组地址+1,跳过一个数组,指向了4的后面,但结果也是一个地址,按照每个地址内存块大小,4字节。
第九行:普通 4/8
第十行:一个元素地址+1,普通情况
另外:
回忆数组指针,要指向它:
int (*p_arr)[5] = &arr;
记忆:数组指针、函数指针都要括号*name
- 字符数组sizeof()
char arr[] = {‘a’, ‘b’, …};
strlen()求字符串长度才可能是随机值,它要找\0
sizeof专门计算数组大小
第一行:因为没有初始化,用了[], sizeof只关心所占内存大小:6个字符,不会管\0,sizeof() 结果为6
如果你初始化 char arr[10] = {‘a’, ‘b’, ‘c’,}这样结果不是3,而是初始化空间的大小。
总之sizeof()专门看数组空间大小,存字符时也不考虑内容\0
第二行:首元素大小+0,还是首元素,结果为4或8
第三行:sizeof(*arr):*arr不是例1,也不是例外2,记忆*arr为第一个元素内容,第一个元素为char,所以大小为1字节
第四行:普通的1个元素,为char1个字节
第五行:&arr,整个数组的地址,地址的大小,是4或8
第六行:(&arr+1),向后跳过整个数组,这片区域大小下一个块应该还是一个地址大小,所以大小是地址大小:4字节或8
第七行:&arr[0]+1,还是个地址,大小为4或8
*和&放一起,可以理解为抵消掉了
到这里,应感悟到 对于&之后的+1或-1,的sizeof值还是地址,地址大小都是4或8 ,带解引用*的要考虑数组中元素大小或直接arr[i]都是元素大小
sizeof返回类型是无符号整型,在64位输出为:%llu,32位输出为%lu
strlen()分析
arr[]没有大小,没有初始化
strlen()专门统计字符串,它一定要找\0,不知道它在哪里遇到\0,所以第一行: 结果是随机的。 因为arr【】中没有\0,strlen继续往后找\0
第二行:不是sizeof中,arr+0还是arr,数组首元素地址,和1一样
第三行:arr是数组首元素地址,*arr是数组首元素,即字符’a’,相当于给了strlen()数字97,strlen()会从地址97开始找\0,所以可,以出现野指针,因为非法访问,结果出错
strlen()源代码中,第一个是const char *string,它等着接收地址,给它一个97,它认为是地址。所以会非法访问,结果出错。
第四行:arr[1],也是一个char值,strlen()认为给它一个地址,还是会同第三行一样,非法访问。
第五行:&arr:// 取整个数组arr的地址,也是首元素的地址,还是会往后找\0,随机值。
第六行:&arr+1,得到的还是个地址,streln()意思还是从那个地方开始往后找\0,还是非法访问。
第七行:&arr[0]+1,取第一个地址,+1,到第二个地址,还是继续给后面找\0,随机值
char arr[] = “abc”, 字符数组用常字符串赋值""
当:
char arr[] ="abcdef";
arr中存的是7个字符 : [a b c d e f \0]
Sizeof 对初始化用常量的字符数组:
sizeof:求元素个数
如果给它的值是地址,它计算肯定是4或8字节,如果给特例1或2
1: sizeof(arr) 大小为7,7 = 7 * 1
2: sizeof(arr+0), 还是首元素的地址开始,找了7个字符
3: sizeof(*arr) *arr是数组中的第一个字符,char,char的大小是1,给它的是字符
4. sizeof(arr[1]):数组中的元素,求字符元素大小,肯定是1
5: sizeof(&arr),给的是地址值,但这是数组的地址,地址的大小是4/8
6: sizeof(&arr+1) 取地址arr+1 ,数组地址+1,指向\0后面的地址,是4/8
7: sizeof(&arr[0]+1) :起始地址+1,给的还是地址,地址大小为4/8
易混淆:sizeof求的是大小,给它地址,必然是4/8,如果给它是某个元素,看这个元素的类型是char ,还是int,可能会不一样,又或者直接给它数组名,那么大小就是整个数组中元素数量*每个元素大小
Sizeof 对用常字符串初始化的字符指针:
1:常变量赋值,p中存字符串中a的地址,而地址,即指针大小应该为4或8
2:sizeof(p+1) , p+1仍然是地址,也是指针,大小4、8
3:*p解引用是个值,内容是char,a,大小为1字节
4:p[0] = * (p+0),加0 相当于每家, 也就是*p ,和上面一样
5:&p,取p地址,先不用考虑和常量,是指针p的位置,也是指针大小,4或8
6:&p+1,&p是指针变量p在内存中跳过p的地址 4或8 , 取地址p,相当于&p,类型是 char ** q= &p;
7: &p[0] 第一个元素地址,即a的地址+1,b的地址,还是地址,sizeof为4或8
strlen 对常字符串初始化的字符指针:
strlen()是看长度,找到\0位置,且strlen()本身要的就是指针
1: strlen§ ,p是指针,给函数p,p的值是&‘a’,所以就从a往后走到\0,6。
2: p为a位置,+1到了b的地址,统计bcdef,结果为5
3: p为a,一个char,而char给了指针,它会把char变成ascall的97,找地址97,是个错误。
4: p[0]也是个p
5: &p 取地址p 得到一个指针地址,得到的是p的地址,往后走,是随机值,问题在p地址上,跟abcd的地址没有关系。输出是随机值
6: &p+1 也是随机值
7: &p[0] + 1, p【0】相当于a,对a取地址+1,是b的地址,所以统计到\0有5个
二维数组:
a是数组名:
二维数组sizeof(a):
1: sizeof内放arr,数组整个大小,124 = 48字节
2: sizeof(a[0][0]) :4/8 因为给它一个元素
3:sizeof(a【0】),作为第一行一维数组的地址,大小为16
4: sizeof(a【0】+1),a[0]作为第一行一维数组的地址,但它不是单独放进来的,+了1,所以应该理解为a[0][0]下一个地址,a[0][1],大小为int,整型
5:(a[0]+1) 上面的基础上,是一个元素,a[1][2] = 0, 4字节
6: a表示二维数组首元素地址,即第一行地址,a+1是第二个元素,即第二行地址
7: 它是第二行的地址,地址大小必然是4或8
8: 第二行解引用,必然是16
9: 对整个数组解引用,第一行解引用,16
10:特别 不会访问地址,直接看a[3],是int[4],和前面一样,是行,虽然数组越界了,但是能知道类型
- 从10上发现,sizeof()不会真的去访问,它只要知道你的类型,而strlen()会从你提供的位置一直往后找,越界也会找。
- 且数组arr[i]的本质是 *(arr)+i
指针笔试题
int a[5] = {1, 2, 3, 4, 5};
对数组名取地址,&a -> int(*)[5] ,应该是一个数组指针类型,(有括号是数组指针,是新学的,每括号是旧的指针数组。)
且ptr由数组指针变为整型指针,步长变小了,类型不会变
指针类型决定指针+1到底加几个字节
解析:0x是十六进制
1: p+0x1,p为指针,+1加它一样大的值,20,0x 100000 + 0x1 =
0x100014,因为20对应14,在16进制里面
2:p+0x1,这里p为0x100000 ,就是个整型,+1,整型+1
3:p+0x1,这里p为指针相当于+4,变为0x100004
4
第一行:跳过一个数组地址,对地址-1解引用,得到前面的4
第二行:把a转为int,a代表首元素,相当于把首元素地址转为int,+1相当于对1的地址+1,相当于向后跳1字节,所以要分析数字1的4个字节内容。
假设是小端模式:
01 00 00 00,02 00 00 00, 03 00 00 00, 04 00 00 00
低------------------------------------------------------------------->高
数组名表示首元素地址,假设a的1地址为:
0x00000020 :转为10进制为32,+1=33,33转为int指针,再转为int*类型,往后挪一个字节,得到地址:00 00 00 02
因为是小端模式:02 00 00 00,若求值,则对它转换,但这个题要求地址,直接输出02 00 00 00。
5。易错点:
初始化右值是(,),(,),(,),而不是{},最终结果是 1,3,5
a[3][2] = {1, 3, 5};
所以初始时完a数组应该是右边,后三个值是0 0 0
分析 p,它是数组指针,指向数组,数组大小为4,p挪动依次挪动4个元素位。
&p[4][2] —> 先是 *(p+4),这是一个一维数组,然后 *(*(p+4)+2),得到4,2
指针-指针,得到的是之间的元素个数。
第一个位置两个相减,差了4个整型,但是小地址-大地址,=-4,-4以%d,是-4,而以%p打印,应该是16进制,先求-4的补码,再转16进制,-4:1000 0000 0000 0000 0000 0000 0000 0100
转为补码,结果: FFFFF…FFFF,-4
第一行:&aa+1,对二维数组取地址+1,指针挪动到最后10的后面,然后地址转为整型指针,+1,-1挪动大小变为4字节。所以最后打印的是10.
第二行:aa是二维数组名,本质是数组指针,做一次数组指针解引用后,拿到的是首元素地址,所以是6的地址。
一般二维数组指针访问元素:(*(p+i))[j],所以一次解引用拿到的只是数组元素地址。
char *a 中存的都是这几个常量字符串的首字母地址
一级指针的地址放到二级指针变量中去,没问题
pa指针指向a,pa++,它指向了第二个字符地址,解引用pa,拿到的是第二个字符串的地址,给字符串首元素地址+%s,输出为整个字符串。
视频8
c中存常字符串几个首地址
cp中存c数组的地址
cpp中存cp的地址
分析:
cp中存c的地址,c是数组名,char*,而char地址应该是char**
即cp中存char**,所以cpp中是char** ,
第一行:cpp指向cp,++后指向cp第二个地址,解引用一次得到c+2,再解引用得到point的首元素地址,此时%s,得到整个字符串
第二行:cpp受第一行影响,先++,指向c+1的地址,解引用得到值c+1,c+1-1=c,这片地址变为c,解引用c,包含常量字符串的字符指针数组可以理解为二维数组,得到第一行字符串首地址,+3得到E地址,%s得ER。c本身可以理解为 = 数组指针,是地址, 解引用c,得到的是C中的值,但C中的值‘E’的地址,+3是第二个E的地址,%s得ER。
第三行:cpp[-2] 等价于*(cpp-2),cpp-2得到cp首元素地址,再解引用得到c+3的值,对c+3解引用得到c+3内容,‘f’的地址,+3得到S地址,%s结果为ST。
第四行:cpp[-1] ->*(cpp-1) , cpp在元素c+2的地址,解引用得到c+2值,第二个【-1】得到*(c+1),得到new中n的地址,n+1得到E的地址,%s得:EW
*a[1] -> (a+1)
变种水仙花:
对一个数字:
先%10得到右边,再除以%10的结果,得到左边
%100得右边,再除以%100的结果,得到左边
指针作业(一)=
- 关于指针错误的说法:
D. 野指针指未分配或已释放的内存地址 (正确)
A. free释放后一个指针内存的地址是标记为它可以被使用,数据还存在,但可以被任意分配
动态内存管理:malloc申请 free释放/回收 calloc realloc
指针类型决定解引用去拿的空间大小
3. 数组指针
a.X,因为 数组指针是一种指针
b. 指针数组是存放数组的指针 X 指针不是存放数组
c.
4. 哪个 是数组指针
首先数组指针肯定是:(*arr),再加上大小 (*arr)[10] ,加内部类型 char*
C
A. arr表示首元素地址,整型接收首元素地址,没问题。
B. ptr是指针,数组指针,每个元素是int
C. 取地址arr[0] 取第一个元素地址,和arr和&arr[0]都表示首元素地址,而&arr表示数组的地址,所以D错了。
单独数组名表示首元素地址
A. 数组名arr和&arr[0]都表示首元素地址,而普通情况arr表示首元素地址,&arr表示整个数组地址。
B. sizeof(arr)是整个数组
D. 一定要记下,除了sizeof和&arr,平时的arr都是数组首元素地址。
定义个int类型指针数组,数组元素个数为10:
int* arr[10];
字符数组str1和str2是两个不同地址,都放入了这些字符
而str3和str4,都是指向内存中同一个常变量地址,所以
1和2不一样,3和4一样
函数指针题
首先,一说函数指针,联想最简单的:
int (*p)(int, int):它是个指针因为括号星p, (*p),名字外左是返回类型,右边是参数,所以描述:函数指针指向的函数有两个int形参,且返回int类型数据
如果函数指针有两个int形参,且返回函数指针:
(*p)(int, int) 它的返回 是函数指针: 所以把他名字部分括号起来:
(*(*p)(int, int)) ,然后左边是外层函数指针左边返回int,右边是参数int