本文是我在学习c语言指针时遇到的一些有难度的题目,我水平有限,有的地方没说清请见谅!
1.strlen函数和sizeof运算符
首先是stlen,
让我们打开cplusplus网站搜索这两个函数。
//Fuck you ,English
让我们使用百度来翻译一下
//Fuck!!!
还是查词典吧
来理解以一下这段文字,就是说你如果想调用这个函数的话,需要传入一个指向字符串首元素的char*类型的指针,它会返回这个字符串的长度,返回值是size_t类型(size_t是unsigned int 类型重命名,用%zd打印)。它的实现方法返回从起始地址到下一个指向'\0'的地址的距离,不包含'\0'。
sizeof
sizeof是一个运算符,传入一个数据,会返回这个数据所占内存大小。sizeof只是计算传入的类型的大小,传入的数据不会真的去计算,不会越界访问,返回值也i为size_t类型
#include<stdio.h>
#include<string.h>
int main()
{
int n = 10;
printf("%zd\n", sizeof(n++));
printf("%d\n", n);//10
return 0;
}
因为sizeof是运算符,所以当传入数据时它后面可以不加括号,传入数据类型必须加括号
#include<stdio.h>
#include<string.h>
int main()
{
int a = 0;
printf("%zd\n", sizeof(a));//4
printf("%zd\n", sizeof a);//4
printf("%zd\n", sizeof(int));//4
//printf("%zd\n", sizeof int);//报错
return 0;
}
sizeof()在c语言中为第二优先级,比加减乘除优先级要高,能加括号还是建议加括号
stlen和sizeof对比
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = {"abc'\0'123456"};
printf("%zd\n", sizeof(arr1));//13
printf("%zd\n", strlen(arr1));//4
char arr2[] = {'a','b','c','d'};
printf("%zd\n", sizeof(arr2));//4
printf("%zd\n", strlen(arr2));//随机值
return 0;
}
2指针
2.1
#include<stdio.h>
#include<string.h>
int main()
{
int a[] = { 1,2,3,4 };
printf("%zd\n", sizeof(a));//1
printf("%zd\n", sizeof(a + 0));//2
printf("%zd\n", sizeof(*a));//3
printf("%zd\n", sizeof(a + 1));//4
printf("%zd\n", sizeof(a[1]));//5
printf("%zd\n", sizeof(&a));//6
printf("%zd\n", sizeof(*&a));//7
printf("%zd\n", sizeof(&a + 1));//8
printf("%zd\n", sizeof(&a[0]));//9
printf("%zd\n", sizeof(&a[0] + 1));//10
return 0;
}
a是一个整形数组内部含有四个元素
2.1.1 sizeof(a)
当数组名单独位于sizof内部时,数组名代表整个数组,求a数组所占内存大小,一个int四个字节,四个元素所以一共16字节。
返回16.
2.1.2 sizeof(a+0)
此时a+0放在sizeof内部,a为首元素地址,地址的话32位机子为4字节,64位为8字节。
返回 4/8
2.1.3 sizeof(*a)
a表示首元素地址,*a表示首元素,也就是int型的1.一个int4字节
返回 4
2.1.4 sizeof(a+1)
a+1放在sizeof内部,a为首元素地址,a+1为 a[1] 的地址,地址的话~~~
返回 4/8
2.1.5 sizeof(a[1])
a[1]为 int 的 2 。一个int~~~
返回 4
2.1.6 sizeof(&a)
&数组名会取出整个数组的地址,地址~~~
返回 4/8
2.1.7 sizeof(*&a)
&a 取出数组的地址*&a把这个地址解引用,得到整个数组,这个数组4个int一个共16字节
返回 16
2.1.8 sizeof(&a + 1)
&a拿出这个数组的地址,再+1跳过这个数组,指向这个数组后面,虽然它指的地方越界了,但是sizeof()括号内的语句不会真的去执行,对于系统来说,这只是要求一个指针占的的内存大小。指针~~~
返回 4/8
2.1.9.sizeof(&a[0])
下标运算符 [] 比 取地址运算符 &优先级高.a[0]为首元素,&a[0]为首元素地址,地址的话
返回4/8
2.1.10.sizeof(&a[0]+1)
&优先级比+高,接着上一题分析&a[0]为首元素地址,&a[0]+1为第二元素的地址。
返回4/8
答案我检查过了,没问题,不过还是建议读者去敲一敲。
2.2
#include<stdio.h>
#include<string.h>
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
printf("%zd\n", sizeof(arr));//1
printf("%zd\n", sizeof(arr + 0));//2
printf("%zd\n", sizeof(*arr));//3
printf("%zd\n", sizeof(arr[1]));//4
printf("%zd\n", sizeof(&arr));//5
printf("%zd\n", sizeof(&arr + 1));//6
printf("%zd\n", sizeof(&arr[0] + 1));//7
return 0;
}
arr是字符数组含有6个元素
2.2.1。sizeof(arr)
数组名放在sizeof内部代表整个数组,arr有6个char类型数据,一个char占一个字节,故6个字节
返回6
2.2.2 sizeof(arr+0)
此时arr为首元素地址,arr+0后内容不变,故该值是一个指向首元素的指针
返回4/8
2.2.3sizeof(*arr)
arr为首元素地址,*arr代表这个地址解引用,是一个char元素
返回1
2.2.4sizeof(arr[1])
arr[1]为该字符数组第二个元素,类型为char
返回1
2.2.5sizeof(&arr)
&arr得到的是指向arr数组的指针,
返回4/8
2.2.6sizeof(&arr+1)
&arr得到指向数组的指针,&arr+1跳过一个数组,它是一个指向arr后面的指针。sizeof只关心后面元素的数据类型,并不在意是否越界。
返回4/8
2.2.7sizeof(&arr[0]+1)
查运算符优先级表得:先下标,再&,最后+。arr[0]为首元素,&arr[0]为首元素地址,&arr[0]+1为第二个元素的地址
返回4/8
2.3
ps:4,5不能过编译
#include<stdio.h>
#include<string.h>
int main()
{
char arr[] = { 'a','b','c','d','e','f' };//1
printf("%zd\n", strlen(arr));//2
printf("%zd\n", strlen(arr + 0));//3
printf("%zd\n", strlen(*arr));//4
printf("%zd\n", strlen(arr[1]));//5
printf("%zd\n", strlen(&arr));//6
printf("%zd\n", strlen(&arr + 1));//7
printf("%zd\n", strlen(&arr[0] + 1));//8
return 0;
}
2.3.1stlen(arr)
arr为首元素地址,stlen会从传入地址开始往后找,一直找到一个解引用后是\0的返回中间地址之差因为没有往数组里面放‘0‘,所以它会越界访问,直到找到’0‘(或者电脑炸掉,不过可能性不大)
返回 一个>=6的随机值 (假入把这个值记为random_8_19的话)
2.3.2stlen(arr+0)
arr+0仍然是首元素地址,所以同上。
返回 随机值 (值为random_8_19)
2.3.3stlen(*arr)
arr为首元素地址,*arr为首元素,是一个字符。
传参有误,报错
2.3.4stlen(arr[1])
arr[1]为第二个元素,也为字符
传参有误,报错
2.3.5stlen(&arr)
&arr代表arr数组的地址,数组的地址和数组开头的地址是一样的,所以会从数组开头开始找’0‘
返回 随机 (random_8_19)
2.3.6stlen(&arr+1)
&arr+1会跳过数组,指向数组后面的元素,所以会从数组后面开始查找'\0'
返回 随机值 (rand_8_19-6)
2.3.7strlen(&arr[0]+1)
&arr[0]代表首元素地址,+1跳过一个元素
返回 随机值 (rand_8_19-1)
2.4
#include<stdio.h>
#include<string.h>
int main()
{
char arr[] = "abcdef";
printf("%zd\n", sizeof(arr));//1
printf("%zd\n", sizeof(arr + 0));//2
printf("%zd\n", sizeof(*arr));//3
printf("%zd\n", sizeof(arr[1]));//4
printf("%zd\n", sizeof(&arr));//5
printf("%zd\n", sizeof(&arr + 1));//6
printf("%zd\n", sizeof(&arr[0] + 1));//7
return 0;
}
arr数组含有7个char元素,最后一个是‘\0'
此题与2.2相似,不做过多讲解,
答案为7, 4/8, 1, 1, 4/8 ,4/8 , 4/8
2.5
#include<stdio.h>
#include<string.h>
int main()
{
char arr[] = "abcdef";
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;
}
arr数组含有7个char元素,最后一个是‘\0'
此题与2.3类似
答案6 6 报错 报错 6 随机值 5
2.6
#include<stdio.h>
#include<string.h>
int main()
{
char* p = "abcdef";
printf("%d\n", sizeof(p));
printf("%d\n", sizeof(p + 1));
printf("%d\n", sizeof(*p));
printf("%d\n", sizeof(p[0]));
printf("%d\n", sizeof(&p));
printf("%d\n", sizeof(&p + 1));
printf("%d\n", sizeof(&p[0] + 1));
return 0;
}
p指向该数组的首元素'a',该数组含有7个char元素,最后一个是‘\0'
解析略
4/8 4/8 1 1 4/8 4/8 4/8
2.7
#include<stdio.h>
#include<string.h>
int main()
{
char* p = "abcdef";
printf("%zd\n", strlen(p));
printf("%zd\n", strlen(p + 1));
printf("%zd\n", strlen(*p));
printf("%zd\n", strlen(p[0]));
printf("%zd\n", strlen(&p));
printf("%zd\n", strlen(&p + 1));
printf("%zd\n", strlen(&p[0] + 1));
return 0;
}
解析略
6 5 报错 报错 随机值 随机值 随机值
2.8
#include<stdio.h>
#include<string.h>
int main()
{
int a[3][4] = { 0 };
printf("%zd\n", sizeof(a));//1
printf("%zd\n", sizeof(a[0][0]));//2
printf("%zd\n", sizeof(a[0]));//3
printf("%zd\n", sizeof(a[0] + 1));//4
printf("%zd\n", sizeof(*(a[0] + 1)));//5
printf("%zd\n", sizeof(a + 1));//6
printf("%zd\n", sizeof(*(a + 1)));//7
printf("%zd\n", sizeof(&a[0] + 1));//8
printf("%zd\n", sizeof(*(&a[0] + 1)));//9
printf("%zd\n", sizeof(*a));//10
printf("%zd\n", sizeof(a[3]));//11
return 0;
}
2.8.1sizeof(a)
a是数组名,单独放在sizeof内部代表整个数组,该数组总共12个int类型元素故占48字节
返回 48
2.8.2sizeof(a[0][0])
a[0][0]代表首元素,为int类型,占4字节
返回 4
2.8.3sizeof(a[0])
a[0]也是一个数组名,单独放在sizeof内部代表整个数组,有三个元素,每个元素是4个整形组成的数组
返回 16
2.8.4sizeof(a[0]+1)
a[0]为数组名,此处代表首元素地址,+1后跳过一个元素,为指向第一个一维数组的第二个元素的指针
返回 4/8
2.8.5sizeof(*(a[0] + 1))
(a[0] + 1)为指向第一个一维数组的第二个元素的指针,解引用后为第二个元素
返回 4
2.8.6sizeof(a + 1)
a为首元素地址,即第一个一维数组的地址,+1跳过一个一维数组,是指向第二个一维数组的指针
返回 4/8
2.8.7sizeof(*(a + 1))
接着上题,解引用后代表第二个一维数组。
返回 16
2.8.8sizeof(&a[0] + 1))
&a[0]取出第一个一维数组的地址+1。是指向第二个一维数组的指针
返回 4/8
2.8.9sizeof(*(&a[0] + 1))
接上题,解引用后代表第二个一维数组。
返回 16
2.8.10sizeof(*a)
a为首元素地址,即第一个一维数组的地址,解引用代表第一个一维数组
返回 16
2.8.11sizeof(a[3])
a[3]是是数组名,含有4个int,越界,但不会去具体执行。
返回 16
3面试题
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;
}
a为首元素地址(a+1)代表第二个元素地址,解引用后得2
&a代表整个数组地址,+1跳过这个数组,故ptr指向数组的后面,ptr-1表示上一个元素的地址,即5的地址
返回 2 5
3.2
#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;
}
(0,1)是逗号表达式,代表1故a中放的元素为{1,3,5,0,0,0},p是a[0],p[0]也就是a[0][0]
为该数组首元素
返回 1
3.3
#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;
}//假设是x86
p是指向一个有四个整形的数组的指针,一个a有5个含有5个整形元素的数组。让p指向a的首元素
&p[4][2]==a[0]+4*4+2
&a[4][2]==a[0]+4*5+2
相减为-4,
二进制源码10000000 00000000 00000000 00000100
反码 111111111 111111111 111111111 111111011
补码 111111111 111111111 111111111 111111100
以%p打印0xFF FF FF FC
返回 0xFF FF FF FC -4
3.4
#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;
}
&aa代表整个数组,+1跳过这个这数组,故ptr1指向数组后面那个位置,-1再解引用返回末端元素
aa代表首元素地址,+1指向第二个元素,即第二个一维数组,-1再解引用返回第一个一维数组末端元素
返回 5 10
3.5
#include <stdio.h>
int main()
{
char *a[] = {"work","at","alibaba"};
char**pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
a数组里放着三个char*类型指针,pa指向a的首元素地址,+1指向第二个元素地址,
返回 at
3.6最后一题!!!!
#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;
}
++cpp指向cp的第二个元素,两次解引用,返回POINT
再++cpp指向cp第三个元素,解引用后--,c+1这个元素变为c解引用得c数组首元素地址,+3后从第四个元素开始打印,返回ER
cpp[-2]访问cp的第一个元素,解引用后+3得ST
cpp[-1][-1]指向NEW,+1返回EW。
返回 POINT ER ST EW