目录
一. 元素类型说明
回顾一下5里面讲的顺序表类型定义:
typedef struct
{
ElemType *elem; // 存储空间的基地址
int length; // 当前长度
}SqList; // 顺序表的结构类型为 SqList
Elemtype是顺序表中元素的类型,可以自己定义并更换成需要的类型(int/float/char...),如:
typedef char ElemType
如果是一个复杂类型,我们可以定义一个结构类型:
# define MAXSIZE 1000 //多项式可能达到的最大长度
typedef struct{ //线性表中元素结构数据类型的定义
float p; //系数
int e; //指数
}Polynomial;
typedef struct{
Polynomial *elem; //存储空间的基地址
int length; //多项式中当前项的个数
}SqList; //多项式的顺序存储结构类型为SqList
二. 数组的定义
数组静态分配:
//数组静态分配
typedef struct{
ElemType data[MaxSize];
int length;
}Sqlist; //顺序表类型
数组动态分配,数组的名字存放的是数组的第一个元素data[0]的地址,也就是首地址或者叫基地址。既然他是存放地址的,我们可以定义一个指针变量*data放数组的地址。形式上看起来像是一个指针变量,但是实际上定义了数组:
//数组动态分配
typedef struct{
ElemType *data; //仍然是定义了数组
int length;
}Sqlist; //顺序表类型
之所以说数组的本质是指针,是因为在在具体实现上,数组是基于指针实现的,编译器只提供了数组首元素的地址。因此在访问时需要使用首地址+偏移量的形式,所谓的偏移量由下标决定。当我们访问第一个元素是可以使用a[0],也可以使用*a,或者*(a+0);同理当我们访问第二个元素时可以使用a[1],同样的,指针表示为*(a+1)。
对于动态数组怎么分配空间,可以使用内存分配函数:
SqList L;
L.data=(ElemType*)malloc(sizeof(ElemType)*MaxSize);
三. 内存分配函数
这行代码是在C语言中动态分配内存来存储一个名为L的数据结构的元素。ElemType是一个自定义的数据类型,MaxSize是一个表示L中最大元素数量的整数。作下面说明:
malloc(m)函数,开辟m字节长度的地址空间,并返回这段空间的首地址;
sizeof(x)运算,计算变量x的长度;例如之前介绍的多项式计算,每个元素需要8字节;
free(p)函数,释放指针p所指变量的存储空间,即彻底删除一个变量;
具体来说,代码中的malloc函数用于动态分配内存。sizeof(ElemType)表示ElemType类型的对象所占用的字节数。乘以MaxSize后,得到需要分配的总字节数。然后,malloc函数会分配这么多字节的内存,并返回指向该内存块的指针。
最后是内存的划分,将返回的指针强制转换(小括号)为指向ElemType类型的指针(*号),并将其赋值给L.data。这样,L.data就指向了一个具有足够空间存储MaxSize个ElemType类型对象的内存块。通过这样的动态内存分配,我们可以根据实际需要来灵活地调整数据结构的大小,而不需要提前确定元素数量或者固定的内存空间。
此代码需要加载头文件:<stdlib.h>
四. C++的动态存储分配
语法:
new 类型名T (初值列表)
功能:申请用于存放T类型对象的内存空间,并依初值列表赋以初值
结果:成功:T类型的指针,指向新分配的内存;
失败:0(NULL);
int *p1 = new int; //指针变量存储该块分配空间的地址
int *p1 = new int(10); //赋初值10
删除操作:释放指针所指向的内存,P必须是new操作的返回值(指针型);
delete 指针P
五. C++的参数传递
函数调用时传送给形参表的实参必须与形参保持三个一致:类型、个数、顺序
参数传递有两种方式:值传递,地址传递
(1)按值传递方式
参数为整型、实型、字符型等;
void main()
{float a,b;
cin>>a>>b; //输入
swap(a,b); //函数名,交换a,b,这里a,b作为实参
cout<<a<<endl<<b<<endl; //输出,传值方式a,b的值没有任何变化
}
这里,第二行cin是C++标准库中的输入流对象,用于读取用户输入。>>是输入运算符,用于将输入流中的数据提取出来,并存储到指定的变量中。swap(a, b)是一个函数调用,用于交换两个变量a和b的值。cout是C++标准库中的输出流对象,用于输出数据。<<是输出运算符,用于将数据插入到输出流中。endl是插入一个换行符。
# include <iostream.h>
void swap(float m,float n) //m,n都是形参
{float temp;
temp = m;
m = n;
n = temp;
} //执行完毕以后,m,n被释放
执行完上述代码后,形参m,n的值交换,但是实参a,b的值未发生变化。这就是按值传递,形参和实参采用不同的存储空间。
(2)传地址方式
包含参数为指针变量,参数为引用类型,参数为数组名三种;
2.1 指针变量做参数
a.形参影响实参
#include <iostream>
using namespace std;
void swap(float *m,float *n)
{
float t;
t = *m; //*m是解引用操作符,用于获取指针m所指向的值。如果m是一个指针,*m就是该指针所指向的值。
//例如,如果m是一个指向整数的指针,而且它指向的值是5,那么执行t=*m后,变量t的值将是5。
*m = *n;
*n = t;
}
int main()
{
float a,b,*p1,*p2;
cin>>a>>b; //输入
p1 = &a; //&取地址运算符,存放a的地址
p2 = &b; //存放b的地址
swap(p1,p2); //函数名,交换p1,p2,这里p1,p2作为实参
cout<<"a="<<a<<endl<<"b="<<b<<endl; //输出,传值方式a,b的值变化
}
这里交换的是指针指向的值,因此输出a,b发生了交换:
b.形参不影响实参
#include <iostream>
using namespace std;
void swap(float *m,float *n)
{
float *t;
t = m; //这里把m,也就是a的地址赋值给t
m = n;
n = t; //这里实现的是指针m和n的交换
} //运行结束后,m,n释放掉
int main()
{
float a,b,c,*p1,*p2;
cin>>a>>b; //输入
p1 = &a; //&取地址运算符,存放a的地址
p2 = &b; //存放b的地址
swap(p1,p2); //函数名,交换p1,p2,这里p1,p2作为实参
cout<<"a="<<a<<endl<<"b="<<b<<endl; //输出,传值方式a,b的值不发生变化
cout<<*p1<<endl; //p1,p2指向的值也没有发生变化
cout<<*p2<<endl;
}
这里a,b都没发生变化,p1,p2指针所指向的值也没发生变化。
2.2 数组名做参数
数组名做参数,传递的是数组的首地址,对形参数组所做的任何改变都将反映到实参数组中。
以下面的代码为例:
#define _CRT_SECURE_NO_WARNINGS
//添加到头行,添加到头文件后还是会出警告的
#include <iostream>
#include <cstring>
using namespace std;
void sub(char b[]) //指向char类型的指针b,也可以写成*b
{
string s;
s = "world";
strcpy(b, s.c_str()); //将s中的内容拷贝到b指向的位置
}
int main(void)
{
char a[10] = "hello";
sub(a); //数组名做参数传递,实际上就是传递了首地址
cout<<a<<endl; //输出a,这时应该打印world
}
运行结果:
2.3 引用类型做参数(重点,以后常用)
什么是引用-用来给对象提供一个替代的名字。
#include <iostream>
using namespace std;
int main()
{
int i=5;
int &j=i; //j是一个引用类型,代表i的一个替代名
//i的值发生变化时,j的值也发生变化
//也可以理解为i,j是同一块地址,使用相同内存空间
i=7;
cout<<"i="<<i<<endl;
cout<<"j="<<j<<endl;
}
输出:i=7;j=7;接下来还考虑之前的swap交换函数:
#include <iostream>
using namespace std;
void swap(float &m,float &n) //引用型参数,m引用了a,n引用了b
{
float temp;
temp = m; //因此对m的操作就是等价于对a的操作
m = n;
n = temp;
}
int main()
{
float a,b;
cin>>a>>b; //输入
swap(a,b); //函数名,a,b作为实参
cout<<"a="<<a<<endl<<"b="<<b<<endl; //输出,传地址方式a,b的值发生变化
}
2.4 几点说明
(1)传递引用给函数与传递指针的效果是一样的,形参变化实参也发生变化;
(2)引用类型作形参,在内存中并没有产生实参的副本,它直接对实参操作;而一般变量作参数,形参与实参就占用不同的存储单元,所以形参变量的值是实参变量的副本。因此,当参数传递的数据量较大时,用引用类型做形参比用一般变量传递参数的时间和空间效率都好;
(3)指针参数虽然也能达到与使用引用的效果,但在被调函数中需要重复使用“*指针变量名”的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。