数据结构(C语言版)基础知识

37 阅读7分钟

数据结构基础知识

函数

可以实现某个具体功能的代码块

增加代码的复用性

降低代码的难度

函数不被调用是不会执行的

对内隐藏细节,对外暴露接口

<返回值类型\void> 函数名([参数列表])
{
  //函数体
  [return 返回值]

}

字符串

初始化

#include<stdio.h>
#include<string.h>

int main() {
   //直接初始化
	char str1[11] = "helloworld";

   //err
	char str1[11];
	str2 = "helloworld";
	
	//正确
	char str2[11];
	strcpy(str2,"helloworld");
	return 0;
}

计算机体系结构

b0e01e6b511b0f88b1b331058f8932f4.jpg

虚拟内存地址

内存条、显卡、各种适配卡都有其各自的存储地址空间。

操作系统将这些设备的存储地址空间抽象成一个巨大的一维数组空间。

对于内存的每一个字节会分配一个32位或64位的编号,这个编号称为内存地址。

数组

相同数据类型的集合。

数组长度一旦被定义就不能改变。

数组中每一个元素可以用下标位置表示位置,如果一个数组中有n个元素,那么下标的取值范围是0~n-1。

#include<stdio.h>

int main() {

	char str1[11] = "helloworld";
	for (int i = 0; i < 11; i++)
	{
		printf("%c ", str1[i]);
	}
	return 0;
}

获取数组地址

使用取地址符&获取数组地址时,返回值是第0个元素的内存地址

#include<stdio.h>

int main() {
	char str1[11] = "helloworld";
	printf("%p\n", &str1);//数组名是数组首元素地址
	printf("%p", &str1[0]);
	return 0;
}

数组与 sizeof

#include<stdio.h>

int main() {

	char str1[11] = "helloworld";

	printf("%zu\n", sizeof(str1));//11

	printf("%zu\n", sizeof(str1[0]));//1

	int len = sizeof(str1) / sizeof(str1[0]);
	printf("数组长度为:%d\n", len);//11
	return 0;
}

指针

指针是用来存放内存地址的变量。

指针的声明

int a;//声明一个整型变量
int* p;//声明一个指针变量,该指针指向一个int类型值的内存地址
a = 5;
#include<stdio.h>

int main() {
	int a;//声明一个整型变量
	int* p;//声明一个指针变量,该指针指向一个int类型值的内存地址
	a = 5;
	p = &a;
	printf("a的地址为:%p\n", &a);
	printf("p的值为:%p\n", p);
	return 0;
}

//运行结果
a的地址为:000000F2077EF8A4
p的值为:000000F2077EF8A4

指针的使用

间接引用操作符*返回指针变量的指向地址的值,通常把这个操作叫做“解引用指针”。

#include<stdio.h>

int main() {
	int a = 5;
	int* p = &a;
	
	printf("%d\n", *p); //5  从指定的地址中读取
	*p = 100;  //存储到指定的地址
	printf("%d\n", a); //10
	
	return 0;
}

指针与函数

设计一个函数,传入两个int参数,并交换两个参数的值

#include<stdio.h>
void swap(int a, int b)//不改变m,n的值
{
	int t;
	t = a;
	a = b;
	b = t;
	printf("a=%d,b=%d", a, b);
}

int main() {
	int m = 5, n = 10;
	swap(m, n);//值传递
	return 0;
}
#include<stdio.h>
void swap(int *a,int *b)//改变m,n的值
{
	int t;
	t = *a;//5
	*a = *b;//把n的值赋给m
	* b = t;//把t的字赋给n
	printf("a=%d,b=%d\n", *a, *b);
}

int main() {
	int m = 5, n = 10;
	swap(&m, &n);//址传递,用指针接收
	printf("m=%d,n=%d\n", m, n);
	return 0;
}

指针与数组

在c语言中,指针与数组的关系十分密切。

通常数组下标能完成的操作都可以通过指针完成。

一般来说,用指针编写的程序比用数组下标编写的程序执行速度快。

#include<stdio.h>
int main() {
	
	int a[] = { 15,20,34,56,13 };
	int* p;
	p = a;

	printf("%p\n", a); //数组名是数组首元素地址 000000359050FC58
	printf("%p\n", p); //000000359050FC58
	printf("%d\n", *p); //15
	
	return 0;
}

#include<stdio.h>
int main() {
	
	int a[] = { 15,20,34,56,13 };
	int* p;
	p = a;
//数组遍历
	for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
	{
		printf("%d ", a[i]); //15 20 34 56 13

	}
	printf("\n");
//指针遍历
	for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
	{
		printf("%d ", *(p + i)); //15 20 34 56 13

	}
	

	return 0;
}

指针做算术运算

给指针加上一个整数,实际上是加上这个整数和指针数据类型对应字节数的乘积。

#include<stdio.h>
int main() {
	int a = 5;
	int* p = &a;
	printf("%p\n", p); //000000C7998FF624
	p++;
	printf("%p\n", p); //000000C7998FF628

	return 0;
}

个数据类型字节数

有符号数无符号数32位64位
charunsigned char11
shortunsigned short22
intunsigned int44
longundigned long48
float48
double88

结构体

结构体的声明

结构体是一个或多个变量的集合,这些变量可以是不同的类型。

声明语法:

struct 结构体名
{
   数据类型 变量名;
   数据类型 变量名;
   ...... 
};

struct point
{
   int x;
   int y;
};

结构体的初始化与调用

struct 结构体名 变量名;
struct poin p;

变量名.结构体内部变量名 = 值
p.x = 10;
p.y = 15;
#include<stdio.h>
struct point
{
	int x;
	int y;
};

int main() {
	struct point p;
	p.x = 10;
	p.y = 15;
	printf("%d\n", p.x);
	printf("%d\n", p.y);
	return 0;
}

结构体实例

创建一个 ponit

编写一个函数,传入两个int参数,在函数创建一个结构体point类型的变量,佳能传入的两个参数分别赋值给该结构体变量x和y,最后将该结构体变量返回。

#include<stdio.h>
struct point
{
	int x;
	int y;
};

struct point createPoint(int x, int y)
{
	struct point temp;
	temp.x = x;
	temp.y = y;
	return temp;
};

int main() {
	struct point p;
	p = createPoint(5, 10);
	printf("%d\n", p.x);
	printf("%d\n", p.y);

	return 0;
}

结构体与指针

在一些场景中,如果传递给函数的结构体很大,使用指针方式的效率通常更高。

//pp指向一个point结构体
struct point *pp;

//为pp所指向的结构体中的属性赋值
(*pp).x=10;    pp->x=10;
(*pp).y=5;     pp->y=5;
#include<stdio.h>

struct point
	{
		int x;
		int y;
	};

int main() {
	struct point p;
	p.x = 10; //(*p).x = 5;
	p.y = 5; //(*p).y = 10;
	
	struct point *pp;
	pp = &p;
	
	(*pp).x = 5;
	(*pp).y = 10;
	
	printf("x=%d y=%d\n", p.x,p.y); //5 10
	printf("x=%d y=%d\n", pp->x,pp->y); //5 10

	return 0;
}

类型定义

typedef 数据类型 别名

typedef int zx

#include<stdio.h>
typedef int myType1;
typedef char mytype2;

int main() {
	myType1 a = 5;
	mytype2 b = 'A';
	printf("%d\n", a);
	printf("%c", b);

	return 0;
}

typedef struct 结构体名 typedef struct

{ {

数据类型 变量名; 数据类型 变量名;

数据类型 变量名; 数据类型 变量名;

… …

}别名; }别名;

#include<stdio.h>
typedef struct
	{
		int x;
		int y;
	}po;
int main() {
	po p;
	p.x = 10;
	p.y = 5;
	printf("%d %d\n", p.x,p.y); //10 5
	return 0;
}

内存

内存分类

c程序编译后,会以三种形式使用内存:

==静态/全局内存==

静态声明的变量使用这部分内存,这些变量在程序开始运行时分配,直到程序结束才消失。

==自动内存(栈内存)==

函数内声明的变量使用这部分内存,在函数被调用时才创建。

==动态内存(堆内存)==

根据需求编写代码动态分配内存,可以编写代码释放,内存中的内容直到释放才消失。

动态内存分配

在c语言中,动态分配内存的基本步骤:

1.使用malloc(memory alocate)函数分配内存

void * malloc(size_t)

如果成功,会返回从堆内存上分配的内存指针

如果失败,会返回空指针

2.使用分配的内存

3.使用free函数释放内存

//整型
#include<stdio.h>
#include<stdlib.h>

int main() {
	int* p;
	p = (int*)malloc(sizeof(int));
	*p = 15;
	printf("%d\n", *p); //15
	free(p);

	return 0;
}

//字符串
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main() {
	char* s;
	s = (char*)malloc(10);
	strcpy(s, "hello");
	printf("%s\n", s);
	free(s);

	return 0;
}

//数组

#include<stdio.h>
#include<stdlib.h>

int main() {
	int* arr = (int*)malloc(5 * sizeof(int));
	for (int i = 0; i < 5; i++)
	{
		scanf("%d", &arr[i]);
		printf("\n");
	}
	for (int i = 0; i < 5; i++)
	{
		printf("%d\n", arr[i]);
	}
	free(arr);
	return 0;
}

//结构体
#include<stdio.h>
#include<stdlib.h>

typedef struct
{
	int x;
	int y;
}po;

int main() {
	po* p;
	p = (po*)malloc(sizeof(po));
	p->x = 5;
	p->y = 10;
	printf("%d\n", p->x);
	printf("%d\n", p->y);
	free(p);
	return 0;
}