链表
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct Node{ //单链表的节点类型
int data;
struct LNode * next;
}LNode, * PNode; //LinkList相当于 struct Node * 链表节点的指针形式
//创建一个链表
PNode create_list(){
int len;
int val; //临时存放用户输入的节点值
printf("输入链表长度len:\n");
scanf("%d", &len);
//(struct Node *)/(PNode)将分配的空间强制转换为适合struct Node *的chunk,sizeof中填入LNode计算一个链表节点所占的字节数
//不存放数据的头节点
LNode * pHead = (LNode *)malloc(sizeof(LNode));
if(NULL == pHead){
printf("分配失败,程序终止!\n");
exit(-1);
}
PNode pre = pHead;
pre->next = NULL;
for(int i = 0; i < len ; i++){
printf("请输入该节点的值val:\n");
scanf("%d", &val);
LNode * pnode = (LNode *)malloc(sizeof(LNode));
//将pnode加入链表并赋值,在链表尾部设置为NULL
pre->next = pnode;
pnode->data = val;
pnode->next = NULL;
pre = pnode;
}
return pHead;
}
//遍历链表并输出每一个元素
void traverse_list(PNode pHead){
printf("该链表内容如下:\n");
PNode pcur = pHead->next; //pHead为空
while (pcur != NULL)
{
printf("%d ", pcur->data);
pcur = pcur->next;
}
printf("\n");
return;
}
//返回链表是否为空
bool is_Empty(PNode pHead){
if(pHead->next == NULL){
return true;
}else{
return false;
}
}
//返回链表长度
int length_list(PNode pHead){
PNode pcur = pHead->next;
int len = 0;
while (pcur != NULL)
{
len++;
pcur = pcur->next;
}
return len;
}
//在第pos个位置上(pos从1开始)插入一个元素data, 返回是否操作成功 len=5则pos可以为6不能为7
bool insert_list(PNode pHead, int pos, int val){
PNode pcur = pHead; //初始节点为第一个节点的前驱节点(头节点)
int len = length_list(pHead); //获取链表长度
//首先判断输入是否合理
if(pos < 1 && pos > len + 1){
return false;
}
//找到前驱节点pcur
for(int i = 1; i < pos; i++){
pcur = pcur->next;
}
//创建一个新的节点并插入
PNode pNew = (PNode)malloc(sizeof(LNode));
if(NULL == pHead){
printf("分配失败,程序终止!\n");
exit(-1);
}
pNew->data = val;
pNew->next = pcur->next;
pcur->next = pNew;
return true;
}
//删除第pos个位置上的元素,并将删除的元素返回
bool delete_list(PNode pHead, int pos, int * p){
PNode pcur = pHead; //初始节点为第一个节点的前驱节点(头节点)
int len = length_list(pHead); //获取链表长度
//首先判断输入是否合理
if(pos < 1 && pos > len){
return false;
}
//找到前驱节点pcur
for(int i = 1; i < pos; i++){
pcur = pcur->next;
}
//删去q
PNode q = pcur->next; //q为需要删去的节点, 在这里不能直接写pcur->next->next
pcur->next = q->next;
*p = q->data;
free(q);
return true;
}
//链表的排序(选择排序) 升序
void sorted_list(PNode pHead){
PNode p, q; //p为左侧待比较指针,q为右侧遍历指针
int t;
for(p = pHead->next ; p->next != NULL ; p = p->next){ //左指针移动, 循环条件:下一个元素不为空
for(q = p->next ; q != NULL ; q = q->next){ //右指针移动,循环条件:当前指针不为空
if(p->data > q->data){ //出现更小的值就进行交换
t = p->data;
p->data = q->data;
q->data = t;
}
}
}
}
int main(){
PNode pHead = NULL; //必须提前声明Phead并设为NULL
pHead = create_list(); //创建链表
int val; //保存删去元素的值
traverse_list(pHead); //遍历输出链表
printf("链表长度为:%d\n", length_list(pHead));
// sorted_list(pHead);
traverse_list(pHead);
printf("插入一个元素\n");
if(insert_list(pHead, 3, 100) == false){
printf("输入不合法!\n");
}
traverse_list(pHead);
printf("删去一个元素\n");
if(delete_list(pHead, 2, &val) == false){
printf("输入不合法!\n");
}
printf("删去的值为:%d\n", val);
traverse_list(pHead);
return 0;
}
指针(地址)
指针基础
cpu和内存的交互:地址线、控制线、数据线(以4G内存为例)
-
指针==地址
-
指针变量是存放内存单元地址的变量
-
指针的本质是一个操作受限的非负整数
int * p; //p是一个变量名,int * 表示该变量只能存储int类型变量的地址
int i = 10; //假设i的地址为2000H,存储的值为10
int j;
// p = 10;不可以存放值
p = &i; //p可以存放地址,即为i的地址2000H,可以称作p指向了i,则*p就代表了i
j = *p; //等价于j = i
printf("i = %d, j = %d, p = %d\n", i, j, *p);
int *p = &i; //等价于int *p; p = &i;
通过被调函数修改主函数中的普通变量的值:
void f(int *p){ //定义一个指针变量(形参)
*p = 100; //将p指向的地址内的数值改为100
}
void function2(){
int i = 9;
f(&i);
printf("i = %d\n", i);
}
指针和一维数组
- 一维数组名是一个指针常量
- a[i] 等价于 *(a+i)
- p±i的值是p±i*(p所指向的变量所占的字节数)
void function3(){
int a[5] = {1, 2, 3, 4, 5};
// 3[a] = 0; //等价于*(3+a)即a[3]
// printf("%d", a[3]);
a[3] == *(3 + a);
//(a+1)代表的是a+1(数组第二个元素)的地址 数组每元素长度4个字节所以输出结果是以4为间隔
//实质上为 a+sizeof(int)
printf("%p %p %p %p \n", a+1, a+2, a+3, a+4);
printf("%d\n", *a+10); //取到*a的值后再加10
}
指针与地址
32位指针4个字节,64位指针8个字节,且用第一个字节的地址表示整个变量的地址:
void function5(){
double * p;
double x = 66.6;
//32位指针4个字节,64位指针8个字节
p = &x; //x占8个字节,1个字节8位,但是p只保存首地址(第一个字节的地址)
double arr[3] = {1.1, 2.2, 3.3};
printf("%p\n", &arr[0]);
printf("%p\n", &arr[1]);
}
指针传入函数
void Show_Array(int * p, int len){
for(int i = 0;i<len;i++){
printf("%d\n", p[i]);
}
}
void function4(){
int a[5] = {1, 2, 3, 4, 5};
Show_Array(a, 5); //a等价于&a[0]
}
通过函数修改实参的值:
void f2(int ** q){ //p本身为指针,指针的地址传入,所以类型为int **
*q = (int *)0xFFFFFFFF;
}
void function6(){
int i = 9;
int * p = &i; //int * p; p = &i;
printf("%p\n", p);
f2(&p); //希望改写p,所以需要发送p的地址
printf("%p\n", p);
}
结构体
- 结构体变量不能加减乘除,可以相互赋值
- 普通结构体变量和结构体指针
struct Student{
int sid;
char name[200];
int age;
}; //分号不能省略
void function1(){
struct Student st = {1000, "zhangsan", 20};
st.sid = 100;
strcpy(st.name, "lisi");
// printf("%d %s %d\n", st.sid, st.name, st.age);
struct Student * pst;
pst = &st;
pst->sid = 99; //pst->sid 等价于 (*pst).sid 等价于st.sid
//上式表示pst所指向的结构体变量中的sid这个成员
}
void f(struct Student * pst){
pst->age = 11;
strcpy(pst->name, "aaa");
pst->sid = 666;
}
//这种方式耗内存 耗时间 不推荐(至少向函数传递了208个字节) 可以改为指针传递
void g(struct Student st){
printf("%d %s %d\n", st.sid, st.name, st.age);
}
void g2(struct Student * st){
printf("%d %s %d\n", st->sid, st->name, st->age);
}
//只发送8个字节(64位指针),省时省力
void function2(){
struct Student st;
f(&st);
// printf("%d %s %d\n", st.sid, st.name, st.age);
g2(&st);
}
typedef的使用
typedef int element; //为int重新取一个名字
typedef struct Student{
int sid;
char name[200];
int age;
}* PST; //等价于struct Student *
int main(){
struct Student st;
PST ps = &st;
ps->age = 200;
printf("%d\n", ps->age);
return 0;
}
动态分配内存
int * p = (int *)malloc(4);
free(p);
//释放p指向的内存,p本身是静态的,不能手动释放,p本身的内存只能在p变量所在的函数运行终止时由系统自动释放
/*
1.要使用malloc函数,必须添加malloc.h这个头文件
2.malloc函数只有一个形参,并且形参是整型
3. 4表示请求系统为本程序分配4个字节
4.malloc函数只能返回第一个字节的地址
5.12行分配了8个字节,p变量占4个字节,p所指向的内存也占4个字节
6.p本身所占的内存是静态分配的,p指向的内存是动态分配的
*/