你确定你学会指针了? 你确定你明白数组名了?
如果你觉得你学的还不错,就进来看看吧,相信你看完之后一定能收获更多。
数组名的理解一定要弄清楚
数组名是数组首元素的地址 但是有2个例外:
- sizeof(数组名),这里的数组名表示整个数组,sizeof(数组名)计算的是整个数组的大小,单位是字节
- &数组名,这里的数组名表示整个数组,&数组名取出的是数组的地址
下面是常见 指针和数组笔试题及解析 (解析以注释的形式写在代码当中)
下面题目的类型都是给你一个数组,你来判断printf到底输出什么。
int main()
{
//一维数组
int a[] = { 1,2,3,4 };//4个元素,每个元素4个字节(int类型)
printf("%d\n", sizeof(a));//16
//数组名a单独放在sizeof内部,数组名表示整个数组,计算的是整个数组的大小单位是字节,是16字节
printf("%d\n", sizeof(a + 0));//4
///a并非单独放在sizeof内部,也没有&,所以数组名a是数组首元素的地址,a+0还是首元素的地址
printf("%d\n", sizeof(*a));//4
//a并非单独放在sizeof内部,也没有&,所以数组名a是数组首元素的地址,*a是首元素,相当于a[0]
printf("%d\n", sizeof(a + 1));//4
//a并非单独放在sizeof内部,也没有&,所以数组名a是数组首元素的地址,a+1就是第二个元素的地址,相当于a[1]
printf("%d\n", sizeof(a[1]));//4
//a[1]就是数组的第二个元素,这里计算的就是第二个元素的大小
printf("%d\n", sizeof(&a));//4
//是取出数组的地址,但是数组的地址也是地址,是地址就是4/8个Byte,数组的地址和数组首元素的地址的本质区别是类型的区别,并非大小的区别
//a -- int* int * p = a;
//&a -- int (*)[4] int (*p)[4] = &a;
printf("%d\n", sizeof(*&a));//16
//对数组指针解引用访问一个数组的大小,即*(解引用)和&(取地址)可以相互抵消,sizeof(*&a)==sizeof(a)
printf("%d\n", sizeof(&a + 1));//4
//&a数组的地址,&a+1还是地址,此时指向的是数组中a[3](4)后面的地址(数组指针+1跳过一个数组)
printf("%d\n", sizeof(&a[0]));//4
//&a[0]是首元素的地址
printf("%d\n", sizeof(&a[0] + 1));//4
//&a[0]是首元素的地址,&a[0]+1就是第二个元素的地址
return 0;
}
int main()
{
//字符数组
char arr[] = { 'a','b','c','d','e','f' };//6个字符
printf("%d\n", sizeof(arr));//6
//数组名arr单独放在sizeof内部,计算的是整个数组的大小
printf("%d\n", sizeof(arr + 0));//4
//arr是首元素的地址==&arr[0],是地址就是4个字节,指针变量的大小和类型无关,不管什么类型的指针变量,大小都是4/8个字节
printf("%d\n", sizeof(*arr));//1
//arr是首元素的地址,*arr就是首元素
printf("%d\n", sizeof(arr[1]));//1
//第二个元素
printf("%d\n", sizeof(&arr));//4
//&arr是数组的地址,只要是地址就是4/8,sizeof(&arr)就是4/8个字节
printf("%d\n", sizeof(&arr + 1));//4
//&arr+1 是跳过数组后的地址,即f后的地址
printf("%d\n", sizeof(&arr[0] + 1));//4
//第二个元素的地址,是地址就是4/8
//strlen是求字符串长度的函数,统计的是在字符串中\0之前出现的字符的个数
printf("%d\n", strlen(arr));//随机值(>=6)
//arr是首元素的地址,往后走不确定到哪里才能到\0,字符数组不像整型数组一样最后一个数字后面就是\0,字符数组的\0不知道在后面什么地方
printf("%d\n", strlen(arr + 0));//随机值(>=6)
//arr是首元素的地址, arr+0还是首元素的地址
printf("%d\n", strlen(*arr));//非法访问,错误
//arr是首元素的地址, *arr就是首元素 - 'a'的ASCLL码 - 97,站在strlen的角度,认为传参进去的'a'-97就是地址,97作为地址,直接进行访问,就是非法访问
printf("%d\n", strlen(arr[1]));//非法访问,错误
//'b' 的ASCLL码- 98,错误与上面相同
printf("%d\n", strlen(&arr));//随机值(不知道/0在哪里)
//取出的是整个数组的地址,&arr取出的类型是 char (*)[6](数组指针类型),strlen的参数类型是const char*,编译器会报警告但是不影响,类型发生了变化但是值不变。
printf("%d\n", strlen(&arr + 1));//随机值
//&arr+1,是f后的地址(跳过了整个数组),不知道什么时候遇到/0,所以也是随机值
printf("%d\n", strlen(&arr[0] + 1));//随机值
//一个字符的地址+1,是b的地址,后面不知道/0在哪里,也是随机值
return 0;
}
int main()
{
char arr[] = "abcdef";//a b c d e f /0
printf("%d\n", sizeof(arr));//7
//abcdef和/0一共七个元素
printf("%d\n", sizeof(arr + 0));//4
//首元素地址+0还是首元素地址
printf("%d\n", sizeof(*arr));//1
//首元素地址解引用,还是首元素a,一个字节
printf("%d\n", sizeof(arr[1]));//1
//第二个元素,一个字节
printf("%d\n", sizeof(&arr));//4
//数组的地址,为4/8
printf("%d\n", sizeof(&arr + 1));//4
//跳过整个数组的地址,为/0后的地址,是地址就是4/8
printf("%d\n", sizeof(&arr[0] + 1));//4
//第一个元素的地址+1,就是第二个元素的地址,是地址就是4/8
printf("%d\n", strlen(arr));//6
//首元素地址传给strlen,往后数因为有/0,所以是6
printf("%d\n", strlen(arr + 0));//6
//首元素地址+0,还是首元素地址,往后数,因为有/0,所以是6
printf("%d\n", strlen(*arr));//非法访问,错误
//对首元素地址解引用,其实就是字符a,传进去的是a的ASCLL码值97,会报错
printf("%d\n", strlen(arr[1]));//非法访问,错误
//传进去的是第二个元素b,穿的是b的ASCLL码,会报错
printf("%d\n", strlen(&arr));//6
//传进去的是首元素的地址,往后数,因为有/0,所以是6
printf("%d\n", strlen(&arr + 1));//随机值
//首元素的地址+1,跳过整个数组,即到/0之后,/0之后什么时候遇到下一个/0不知道,为随机值
printf("%d\n", strlen(&arr[0] + 1));//5
//第二个元素的地址,从b数到/0,有5个
return 0;
}
int main()
{
char* p = "abcdef";//a b c d e f \0,但p中存放的是a的地址(p能够找到abcdef/0)
printf("%d\n", sizeof(p));//4
//计算的是指针变量的大小
printf("%d\n", sizeof(p + 1));//4
//char*指针+1向后偏移一个,但p+1还是一个地址,是地址就是4/8
printf("%d\n", sizeof(*p));//1
// *p=='a'
printf("%d\n", sizeof(p[0]));//1
//p[0]等价于*(p+0),等价于*p,即第一个元素a
printf("%d\n", sizeof(&p));//4
//&p仍然是一个地址,是地址就是4/8,假设p的地址是0x0012fc60,指向的就是0x00的地址,类型是char**二级指针
printf("%d\n", sizeof(&p + 1));//4
//&p是地址,&p+1还是地址,是地址就是4/8,假设p的地址是0x0012fc60,指向的就是0x60,类型是char**二级指针
printf("%d\n", sizeof(&p[0] + 1));//4
//指向第二个元素的地址,是地址就是4/8
printf("%d\n", strlen(p));//6
//a的地址往后数到/0
printf("%d\n", strlen(p + 1));//5
//b的地址往后数到/0
printf("%d\n", strlen(*p));//非法访问,错误
//p指向a的地址,*p就是a,传的a的ASCLL码,错误
printf("%d\n", strlen(p[0]));//非法访问,错误
//和*p一样的,是a,传的a的ASCLL码,错误
printf("%d\n", strlen(&p));//随机值
//&p假设取的是0x0012ff40,这个指针变量占4个字节,即从0x0012ff40往后找/0,后面什么位置有/0不知道
printf("%d\n", strlen(&p + 1));//随机值
//&p假设取的是0x0012ff40,&p+1指的是0x40后的地址,即从0x40往后找/0,后面什么位置有/0不知道
printf("%d\n", strlen(&p[0] + 1))//5
//第二个元素b的地址,往后数到/0,有五个
return 0;
}
int main()
{
//二维数组
int a[3][4] = { 0 };
printf("%d\n", sizeof(a));//48
//数组名a单独放在sizeof内部,表示整个数组,一共3*4=12个元素,每个元素4个字节即12*4=48
printf("%d\n", sizeof(a[0][0]));//4
//a[0][0]是数组的第一行第一列的元素
printf("%d\n", sizeof(a[0]));//16
//二维数组其实是一维数组的数组,a[0]可以看成第一行的元素,a[1],a[2]可以看成二三行的元素,即整个第一行的大小,即4*4=16
printf("%d\n", sizeof(a[0] + 1));//4
//a[0]并非放在sizeof内部,所以a[0]表示数组首元素的地址,也就是第一行第一个元素的地址。a[0]+1表示a[0][1]的地址。是地址就是4/8
printf("%d\n", sizeof(*(a[0] + 1)));//4
//a[0]+1是a[0][1]的地址,解引用就是第二个元素
printf("%d\n", sizeof(a + 1));//4
//a作为二维数组的数组名,名没有单独放在sizeof内部,a就是首元素的地址,也就是第一行的地址。a+1就是第二行的地址,a的类型是一个数组指针int(*)[4],是地址就是4/8
printf("%d\n", sizeof(*(a + 1)));//16
//a+1是第二行的地址,解引用就是第二行 4*4=16
printf("%d\n", sizeof(&a[0] + 1));//4
//a[0]是第一行的数组名,&a[0]取出的是数组的地址,取出的是第一行这个一维数组的地址,类型就是int(*)[4],&a[0]+1就是第二行的地址,是地址就是4/8
printf("%d\n", sizeof(*(&a[0] + 1)));//16
//对第二行的地址解引用就是第二行,4*4=16
printf("%d\n", sizeof(*a));//16
//a没有单独放在sizeof内,a表示数组首元素的地址,也就是第一行的地址,解引用就代表第一行,4*4=16
printf("%d\n", sizeof(a[3]));//16
//a[3]表示第四行,按理说应该越界了,但是没有,sizeof是计算,不会去真的访问第四行,其相当于和a[0]一样,4*4=16
return 0;
}
指针和数组名在C语言中非常重要,它们是C语言中最基本的数据类型和数据结构之一。以下是指针和数组名的重要性:
1.内存管理:指针允许我们直接访问和操作内存中的数据。通过指针,我们可以动态地分配和释放内存,有效地管理内存资源。
2.数组访问:数组名本质上是一个指向数组第一个元素的指针。通过数组名,我们可以方便地访问和操作数组中的元素。
3.函数参数传递:通过指针作为函数参数,可以实现对函数外部变量的修改。这在需要在函数内部修改变量的值时非常有用。
4.字符串处理:C语言中的字符串是以null字符结尾的字符数组。通过指针和数组名,我们可以方便地处理字符串,比如拷贝、连接、比较等操作。
5.动态数据结构:指针可以用来创建和操作动态数据结构,比如链表、树等。通过指针,我们可以动态地创建和销毁节点,连接和遍历节点。
6.性能优化:在一些需要高效处理大量数据的场景中,通过指针和数组名可以减少内存拷贝和数据移动的开销,提高程序的执行效率。
总的来说,指针和数组名在C语言中扮演着非常重要的角色,它们是C语言实现高效、灵活和可靠的关键工具。在学习和使用C语言时,理解和掌握指针和数组名的概念和用法是非常重要的。