选用教材为“数据结构(C语言版 第三版)李冬梅 严蔚敏 吴伟民编著”
线性表的顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素,通常称这种存储结构的线性表为顺序表(Sequential List)。特点:地址连续、依次存放、随机存取、类型相同。
实验一设计如下:
- 回忆c/c++的基础知识,介绍clion软件的使用,联系上节课讲述的typedef 自定义抽象数据类型,完成练习;
- 第二节课讲述顺序表的基础操作;
- 带刷力扣题目。
一、c/c++的基础知识
指针
- &和*为互补运算;
- 指针必须指向对象后,才能引用。
-
#include <stdio.h> void main () { int x ,*p; x=55; p=&x; printf ( “ %d, %d\n”, x, *p) ; *p=65; printf ( “ %d, %d”, x, *p) ; }
程序中 int *p; 只是定义了一个指针变量 p,此时它没有指向任何对象(内容是随机值)。
👉 如果直接使用 *p,会访问非法内存,程序可能报错。所以必须先执行 p = &x;,让 p 指向变量 x。
&x:取变量 x 的地址。
*p:取指针 p 所指向地址中的值。在程序中,p = &x;,所以 *p 就等价于 x。
比如执行 *p = 65; 其实就是 x = 65。之后 printf 输出 65, 65。
✔️ 这说明 *(&x) == x,&(*p) == p,互相抵消,是互补运算。
数组

-
数组的本质
-
数组是一段连续的内存空间。
-
数组名就是这段空间的首地址,它是一个常量。
-
-
指针与数组的关联
-
一个指针变量可以存储数组的首地址。
-
int a[10]; -
int *p = a;(直接赋值,因为数组名a本身就是地址) -
int *p = &a[0];(取首个元素的地址,效果相同)
-
-
如何通过指针访问数组元素?
-
a[i]这种下标访问是数组的标准用法。 -
但它的底层原理是指针运算:
*(a + i)。 -
a + i表示从数组首地址a偏移i个元素单位的新地址。 -
*(解引用) 运算符则用来获取这个新地址上的值。
-
-
总结
-
a[i]和*(a + i)效果完全等价。 -
数组名
a是常量地址,不能改变其值 (a = p是错的)。 -
指针
p是变量地址,可以改变其指向 (p = a是对的)。
-
c++中的参数传递
- 传值方式
#include <iostream.h>
using namespace std;
void swap(float m,float n)
{
float temp;
temp=m;
m=n;
n=temp;
}
void main()
{
float a,b;
cin>>a>>b;
swap(a,b);
cout<<a<<endl<<b<<endl;
}
转换失败:把实参的值传送给函数局部工作区相应的副本中,函数使用这个副本执行必要的功能。函数修改的是副本的值,实参的值不变。
- 传地址方式-指针变量作参数(1)
-
#include <iostream> using namespace std; void swap(float *m,float *n) { float t; t=*m; *m=*n; *n=t; } void main() { float a,b,*p1,*p2; cin>>a>>b; p1=&a; p2=&b; swap(p1, p2); cout<<a<<endl<<b<<endl; }这段代码成功的关键在于指针和地址传递。当你调用
swap(p1, p2)时,你没有传递a和b的值,而是把它们的内存地址传了进去。在swap函数内部,m和n这两个指针就分别指向了a和b在内存中的位置。函数通过解引用操作符*,直接找到了这些地址上的原始数据,并进行了交换。 -
可以把这个过程想象成:你不是把两本书(值)的副本交给朋友去交换,而是直接把这两本书的位置(地址)告诉了他。这样,朋友就能直接在书架上把它们的位置换掉,而书架上的书(原始变量)也就真正被交换了。这就是指针实现地址传递的强大之处,它让函数可以直接操作函数外部的变量。
- 传地址方式-指针变量作参数(2)
#include <iostream> using namespace std; void swap(float *m,float *n) { float *t; t=m; m=n; n=t; } void main() { float a,b,*p1,*p2; cin>>a>>b; p1=&a; p2=&b; swap(p1, p2); cout<<a<<endl<<b<<endl; }上面是一个常见的指针陷阱。这段代码的目标是交换
a和b的值,但它无法成功。问题出在swap函数内部。虽然我们传进去了两个指针p1和p2,但函数在接收时,依然遵循值传递的原则。这意味着,swap函数创建了两个新的局部指针变量m和n,它们只是复制了p1和p2所存储的地址。函数内部的
t=m; m=n; n=t;这三行代码,仅仅是在交换这些局部地址副本的值,让m指向b,让n指向a。一旦swap函数执行完毕,它内部的所有局部变量,包括m、n和t,都会被销毁。而main函数中的原始指针p1和p2及其所指向的a和b,从头到尾都没有被触碰过。所以,a和b的值当然也就纹丝不动。要让代码成功,我们必须解引用指针,直接操作它们所指向的内存空间。正确的做法是使用
*符号,把m和n这两个指针当成“遥控器”,去改变a和b的值,而不是仅仅交换遥控器本身。 -
记住,交换指针和交换指针所指向的值是两码事。前者是无用功,后者才是实现功能的关键。
-
传地址方式-引用类型作参数
#include<iostream> using namespace std; void main(){ int i=5; int &j=i; i=7; cout<<"i="<<i<<" j="<<j; }引用的作用可以一句话概括:它是变量的别名。
-
就像你给一个好朋友起了个外号,无论你叫他的大名还是外号,指的都是同一个人。在代码里,
int &j=i;就是给变量i起了个别名j。从这一刻起,i和j就是同一个东西,它们共享同一块内存地址。所以,当你修改i的值时,j的值也同步改变。引用的这种特性,让它在函数参数传递中非常有用,可以让你直接操作函数外部的变量,而不需要像指针那样使用复杂的*和&符号。
-
#include <iostream> using namespace std; void swap(float& m,float& n) { float temp; temp=m; m=n; n=temp; } void main() { float a,b; cin>>a>>b; swap(a,b); cout<<a<<endl<<b<<endl; }同学们,我们来看这段代码。它成功了吗?答案是肯定的,它完全正确!为什么呢?秘密就在
swap函数的参数里,大家看,这里我们用的是float& m和float& n。这个&符号在这里可不是取地址,它是 C++ 的一个神奇功能——引用。引用是什么?
引用,你可以把它理解为一个变量的“别名”或者“外号”。
当我们在
main函数里调用swap(a, b)时,我们不是把a和b的值复制一份传进去,而是给它们分别起了个别名。m成了a的外号,n成了b的外号。所以,在swap函数内部,我们对m和n的任何操作,比如赋值、交换,实际上都是直接作用在main函数里的原始变量a和b身上。引用 vs 指针
这和我们之前讲的指针有什么区别呢?
用指针交换,我们得先取地址,再用
*来解引用,语法上稍微复杂点。而引用就简单多了,你看,在swap函数里,m和n就像是普通的float变量,你可以直接操作它们,编译器会自动帮你搞定幕后的地址传递。总结一下:
-
指针是告诉你变量的“门牌号”,你得拿着门牌号去找变量。
-
引用更直接,它就是变量的“另一个名字”,你喊这个名字,变量就会响应。
- 传地址方式-数组名作参数
#include<iostream> #include<cstring> // 添加这个头文件 using namespace std; void sub(char b[]); // 函数声明 int main() { char a[10]="hello"; sub(a); cout<<a<<endl; return 0; } void sub(char b[]) // 添加返回类型 void { strcpy(b,"world"); }这段代码的核心是演示数组作为函数参数的传递方式。
当程序运行到
sub(a);时,我们并没有把整个a数组复制一份传给sub函数。因为数组太大,复制会消耗大量内存和时间。在 C++ 中,数组名本身就代表着它的首地址。所以,sub函数实际接收到的是a数组在内存中的位置信息。在
sub函数内部,char b[]接收了这个地址。接着,strcpy(b, "world");这行代码就直接找到这个地址,并把"world"这个新字符串覆盖到原始数组a的内存空间里。所以,当
sub函数执行完毕后,a的内容已经从最初的"hello"变成了"world"。这正是为什么cout最后会输出"world"的原因。
结构体
在 C 语言里,我们可以用结构体来组合不同类型的数据,例如一本书的书号、书名和价格。定义一个结构体类型,并用 Book 作为该结构体的新名字。结构体内部有三个成员:no、name、price,分别存储书号、书名和价格。定义结构体有两种常见方式:
使用 typedef:
typedef struct {
char no[15];
char name[50];
float price;
} Book;
Book b[10];
不使用 typedef:
struct Book {
char no[15];
char name[50];
float price;
};
struct Book b[10]; // 正确
-
struct Book是结构体类型的名字。 -
声明变量时必须写
struct Book,比如struct Book b[10];。 -
每次使用这种结构体类型都需要加
struct,比较繁琐。 -
typedef给结构体类型起了一个别名Book, -
声明变量时可以直接写
Book b[10];,不用写struct,更加简洁方便。
💡 总结:
-
struct Book 是结构体的原名,声明变量时要加
struct。 -
typedef + struct 给结构体起了别名,可以直接用别名声明变量。
-
换句话说,
typedef就像给结构体类型取了一个‘绰号’,使用时更方便。
练习一
简单练习题:输入并显示一本书的信息(指针+结构体)
题目描述:
定义一个结构体 Book,包含 书号、书名和价格。
用指针输入一本书的信息。
用指针显示这本书的信息。
#include <stdio.h>
typedef struct {
char no[15];
char name[50];
float price;
} Book;
int main() {
Book b; // 声明一个 Book 类型变量
Book *p = &b; // 声明一个指针,指向 b
// 输入书的信息
printf("请输入书号:");
scanf("%s", p->no); // 使用指针访问结构体成员
printf("请输入书名:");
scanf(" %[^\n]", p->name); // 读取带空格的字符串
printf("请输入价格:");
scanf("%f", &p->price);
// 显示书的信息
printf("\n书籍信息:\n");
printf("书号:%s\n", p->no);
printf("书名:%s\n", p->name);
printf("价格:%.2f\n", p->price);
return 0;
}
可能会出现的问题:获取输入price时,为什么用引用符号,而其他两个属性不用?
在结构体中,不同类型的数据成员访问方式不同:
-
基本类型(如
int、float) 存的是具体的数值,所以在需要获取地址时要用&p->price,因为price本身是一个数。 -
数组类型(如
char name[50]) 在表达式中会自动转换为指向数组首元素的指针,所以p->name本身就代表地址,不需要再加&。
👉 总结成一句话:
数值型成员要取地址需要 &,而字符数组名本身就是地址,不用加 &。
3711

被折叠的 条评论
为什么被折叠?



