前记:
这是我关于代码能力提高的第一篇博客,以后会出一个系列。为什么会想去出博,毕竟这比直接刷题要慢很多,而且得去经营,也很耗精力,这是因为之前做了一次笔试,5道编程题,嗯。。。你晓得那种知道实现,但是卡死在某个函数上吗?明白了自己的基本功有待加强,题看懂,思路清晰是一回事,落实到指尖,是另外一回事,愿你我共勉这句话:不能只做思想的巨人,行动的矮子,要想的开,写的出,动起来ヾ(◍°∇°◍)ノ゙
《剑指offer》这本书我会把按章节来走,每一个对应的例题,我都会以代码的形式尽量完整的呈现,如果有不太准确或是错误的地方,欢迎交流 (*^▽^*)
2.3 数据结构
2.3.1 数组——一维数组
第一题:
- 变量名:变量的内容;数组名:指针,当前数组的首地址(a是数组名)。
- sizeof()这个函数求变量所占的字节数,一般int,float,指针都占四个字节
- 关于数组的一些基本使用规则:1. 在 C++ 中要声明一个数组,需要指定元素的类型和元素的数量;2. 在 C++ 中,您可以逐个初始化数组,也可以使用一个初始化语句。罗列三种形式:① double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0}; ② double balance[] = {1000.0, 2.0, 3.4, 7.0, 50.0}; ③ balance[4] = 50.0;(③的语句把数组中第五个元素的值赋为 50.0);3. 数组元素可以通过数组名称加索引进行访问,或通过指针形式访问,比如:a[1] 等价于 a+1;
- 注意:对数组名进行sizeof操作,得到的是整个数组的长度。
//首先定义一个int类型的数组
//然后来分别输出数组名、数组以及每个单元的所占字节数
#include <iostream>
using namespace std;
int main()
{
// int a[];--->error: storage size of 'a' isn't known,关于数组的一些基本使用规则见博客
int a[] = {0,1,2,3,4};
cout<<"a:"<<sizeof(a)<<endl;
cout<<"a[0]:"<<sizeof(a[0])<<endl;
cout<<"1:"<<sizeof(1)<<endl;
//----->error: invalid type argument of unary '*' (have 'int'),不能对一个地址求所占字节长度
// cout<<"*a:"<<sizeof(*a[0])<<endl;
return 0;
}
输出结果:
a:20
a[0]:4
1:4
- 注意:一维数组中a[]可以在定义时使用,但不能作为数组名被使用,数组名是a。
- 注意:当数组作为函数的参数进行传递是,数组就自动退化为同类型的指针。
//首先定义一个int类型的数组
//然后来分别输出数组名、数组以及每个单元的所占字节数
#include <iostream>
using namespace std;
int getSize(int a[])
//效果等同于:int getSize(int* a)
{
return sizeof(a);
}
int main()
{
int a[] = {0,1,2,3,4};
cout<<"a:"<<sizeof(a)<<endl;
// cout<<"a[]:"<<sizeof(a[])<<endl; //定义的时候可以用a[],但是使用的时候a[]不能表示函数名
int size = getSize(a);
cout<<"a:"<<sizeof(size)<<endl;
return 0;
}
输出结果:
a:20
a:4
问题:那一维数组被传入函数中使用后,我们如何确定数组的长度呢,还是必须将数组的长度作为实参传给函数的形参呢?
答:C++ 传递数组给函数的三种方式:
- 方式1:形参是一个指针
void function(int *param)
{
}
- 方式2:形参是一个已定义大小的数组
void function(int param[10])
{
}
- 方式3:形参是一个未定义大小的数组
void myFunction(int param[])
{
}
所以,如果不传入数组的长度,光通过一个数组的头指针是并没有办法限定数组的,即就函数而言,数组的长度是无关紧要的,因为 C++ 不会对形式参数执行边界检查。那么只要程序写的好,那个指针附近的东西就可以随便(๑′ᴗ‵๑)I Lᵒᵛᵉᵧₒᵤ❤处理了,所以程序设计者一定要注意下标越界的问题。
- C++ 从函数返回数组:
C++ 不允许返回一个完整的数组作为函数的参数。但是,您可以通过指定不带索引的数组名来返回一个指向数组的指针。
int* function()
{
}
另外,C++ 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。
/*题目:生成 10 个随机数,并使用数组来返回它们*/
#include <iostream>
#include <cstdlib>
using namespace std;
int* getRandom()
{
// int random[10]; --->不会报错,但是C++程序不会返回这个值,使得程序停止
static int random[10];
for(int i = 0;i < 10;i++)
{
random[i] = rand();
cout<<random[i]<<"\t";
}
cout<<endl;
for(int i = 0;i < 10;i++)
{
cout<<&random[i]<<"\t";
}
cout<<endl;
return random;
}
int main()
{
int* rand; //这个地方我开始设计成了数组,结果是不对的,大家可以想想为什么
rand = getRandom(); //如果是数组,我是这么设计的:&rand[0] = getRandom()
for(int i = 0;i < 10;i++)
{
cout<<rand[i]<<"\t";
}
return 0;
}
输出结果:
41 18467 6334 26500 19169 15724 11478 29358 26962 24464
0x4c6040 0x4c6044 0x4c6048 0x4c604c 0x4c6050
0x4c6054 0x4c6058 0x4c605c 0x4c6060 0x4c6064
41 18467 6334 26500 19169 15724 11478 29358 26962 24464
问题:一开始设计的时候,我实现的是如上图注释部分的设计,int* rand[10];&rand[0] = getRandom; 但是报错:error: lvalue required as left operand of assignment|,讲道理应该是对的吧,于是乎我去找了一下原因。
答:上面的错误大致意思是:等号的左边只能是变量,不可以是表达式。还有一个问题,当我main函数中创建数组后,会被分配一片连续的内存,那我如何把由getRandom()函数获得的地址覆盖到现在申请的内存上呢,明显是可以实现的,但是代价有点得不偿失。实现如下:
/*题目:生成 10 个随机数,并使用数组来返回它们*/
#include <iostream>
#include <cstdlib>
using namespace std;
int* getRandom()
{
// int random[10]; --->不会报错,但是C++程序不会返回这个值,使得程序停止
static int random[10];
for(int i = 0;i < 10;i++)
{
random[i] = rand();
cout<<random[i]<<"\t";
}
cout<<endl;
for(int i = 0;i < 10;i++)
{
cout<<&random[i]<<"\t";
}
cout<<endl;
return random;
}
int main()
{
int* rand[10];
int* temp = getRandom();
for(int i = 0;i < 10;i++)
{
rand[i] = temp+i;
cout<<*rand[i]<<"\t";
}
return 0;
}
ps:程序设置时,一定要注意类型的匹配。
问题:还有就是关于static的声命周期?
答:开始对于static的生命周期产生的疑惑,通过一篇博文解决了:https://www.cnblogs.com/bigclould/p/9322248.html。文章的关键点提炼一下:static的声明周期是整个文件,为声明会生存的周期会超过一般定义的函数中的普通变量的声明周期,因为它们存在的位置不同,普通变量被初始化在对应函数的栈中,而全局变量被定义在了整个文件的一个全局变量区,而且其使用是一次初始化后,可以被多次使用,换句话说,你仍旧可以调用random函数,但是其中的random[10]数组位置固定了,你只是会改变它的值,并不会对其进行重复创建。所以当函数被调用结束后,普通变量就随栈消失了,但是静态变量仍旧等待着程序的最终完成。