目录
数组和指针初阶的文章可查看编程小白进阶秘籍:C 语言指针和数组初阶全解析(值得反复观看)
1.前言
在讲 C 语言指针和数组进阶内容前,首先强调一遍,C 语言的核心内容是指针、数组和内存管理,它们之间的联系才是 C 语言真正的灵魂,而要真正做到从 “了解 C” 迈向 “熟悉 C”,便得掌握数组和指针的内容。对于这部分内容而言,我们之前所学的只是基础,在实际的开发场景中,这些基础内容很多时候难以直接派上用场,那些只适合做些小程序,效率不高而且代码量大。
那么我们该如何学习这块内容呢?首先,我们必须得多虚拟画图,从手绘到脑绘是一个过程,尤其是遇到一些一时间不好口述的情况,这时画图往往可以让你找到答案(本人在学习过程中也是跟着我的老师一步步进行图文结合来学习的)。然后是做一些相关的题目,提升你对这块内容的印象,以及深度去思考指针、数组和内存之间的联系。
接下来,我们便正式进入本章关于数组和指针的学习,成为一名 “熟悉 C” 的程序员。
2.数组进阶
2.1 数组名含义
数组名在C语言程序中出现时,有两种含义:
1) 代表整个数组
2) 代表数组首元素的地址
以下数组名出现时,代表整个数组的情况有:
1) 在数组定义中
2) 在 sizeof 运算表达式中
3) 在取址符&中(前面讲过)
示例代码:
#include <stdio.h>
int main(int argc, char const *argv[])
{
// (1)、以下数组名出现时,代表整个数组的情况有
// 1、在数组定义中
int buf1[5] = {0};
// 数组在内存中不能随便移动位置(数组名不能进行加减操作:buf1=buf+1)(数组名相当于一个常量指针)
// 2、在 sizeof 运算表达式中
printf("数组buf1的大小 == %lu\n", sizeof(buf1));
/*
解析:
sizeof是将其当成一个指针来对待吗??
如果将其当成一个指针来对待的话,那么sizeof(buf1)的大小就应该为系统位数的大小
所以,sizeof(buf1)其实是算这个buf1数组的大小,而非指针的大小
*/
// 3、在取址符&中
printf("数组buf1的首元素的地址 == %p\n", buf1) ;
printf("数组buf1的首元素的地址+1 == %p\n", buf1+1);
/*
解析:
数组buf1的首元素的地址 == 0x7ffe387f2510
数组buf1的首元素的地址+1 == 0x7ffe387f2514
地址移动了4个位置,所以是4个字节,所以其buf1的作用
范围是4个字节(数组元素的作用范围)
*/
printf("整个数组buf1的地址 == %p\n", &buf1) ;
printf("整个数组buf1的地址+1 == %p\n", &buf1+1);
/*
解析:
整个数组buf1的地址 == 0x7ffc202c2650
整个数组buf1的地址+1 == 0x7ffc202c2664
地址移动了20个位置,所以是20个字节,所以其&buf1的
作用范围是20个字节(整个数组的作用范围)
*/
return 0;
}
以下数组名出现时,代表数组首元素的地址的情况有:
1) 函数传参时
2) 指针指向该数组首元素的地址时
3) 使用scanf函数
4) 直接使用数组名(前面讲过)
示例代码:
#include <stdio.h>
// 函数传参时,数组代表其首元素地址(相当于一个指针)
// 传参,不论是指针还是数组,都会将其当成指针来对待
void upper_case(char *p) // char *p = str;
{
printf("sizeof(p) == %lu\n", sizeof(p));
printf("sizeof(p[0]) == %lu\n", sizeof(p[0]));
int step = 'a' - 'A'; // 32 a-32 == 'A'
// sizeof(p) == sizeof(指针): 由系统位数决定(32位:4个字节 64位:8个字节)
// sizeof(p[0]) == sizeof(数组里面的元素): 1个字节(char型数组元素)
for(int i = 0; i<sizeof(p)/sizeof(p[0]); i++)
{
if((p[i] >='a') && (p[i]<='z') ) // 判断其是否是小写字符
p[i] -= step; // 转换小写变为大写
}
}
int main()
{
// (2)- 当出现其他情形时,数组代表其首元素地址。
// 1、指针指向该数组首元素的地址时
int buf2[128] = {0};
int *p1 = buf2;
int *p2 = &buf2[0];
printf("数组buf2的首元素地址 == %p\n", buf2);
printf("数组buf2的首元素地址+1 == %p\n", buf2+1);
printf("p1的变量里面存放的地址 == %p\n", p1);
printf("p1的变量+1后里面存放的地址 == %p\n", p1+1);
printf("p2的变量里面存放的地址 == %p\n", p2);
printf("p2的变量+1后里面存放的地址 == %p\n", p2+1);
/*
解析:
数组buf2的首元素地址 == 0x7ffda56858d0
数组buf2的首元素地址+1 == 0x7ffda56858d4
p1的变量里面存放的地址 == 0x7ffda56858d0
p1的变量+1后里面存放的地址 == 0x7ffda56858d4
p2的变量里面存放的地址 == 0x7ffda56858d0
p2的变量+1后里面存放的地址 == 0x7ffda56858d4
地址一样,且+1后移动的地址也相同,所以其作用范围都一致
*/
// 2、函数传参时
char str[] = "abcdefghijklnmopqrstuvwxyz";
printf("原数组:%s\n", str);
upper_case(str);
// 传进去的数组,在另一个函数中,由一个相同类型的指针变量,将其数组的首元素地址获取了,后续计算的只是指针变量,而非数组。
printf("转换后:%s\n", str);
/*
解析:
原数组:abcdefghijklnmopqrstuvwxyz
转换后:ABCDEFGHijklnmopqrstuvwxyz
此处打印出来的数据是只有前8个字符做了改变,而其它字符没有改
说明函数里的:sizeof(p)/sizeof(p[0]),算的p变量不是整个数组的大小,而是指针的大小
*/
// 3、使用scanf函数
char buf3[128] = {0};
printf("请输入你要输入的数据:\n");
scanf("%s", buf3);
// buf3表示的是数组的首元素的地址(&buf3表示就是整个数组的地址,切记不要写错了)
// 由于buf3是首地址,所以不用加&,直接使用buf3
// 4、直接使用数组名
char buf4[128] = "来一根超级棒棒糖";
printf("数组buf4的首地址 == %p\n", buf4); // 将数组的首元素的地址给打印出来
printf("数组buf4的首地址+1 == %p\n", buf4+1);
printf("buf4数组里面的字符串 == %s\n", buf4); // 把数组里面的内存给打印出来
return 0;
}
- 重点信息:
- 指针的大小取决于系统位数
- 传参时,传的是数组的首地址,函数可以通过这个地