我们如何把现实中大量而复杂的问题以特定的数据类型和特定的存储结构保存到主存储器(内存)中,以及在此基础上为实现某个功能(比如查找某个元素,删除某个元素,对所有元素进行排序)而执行的相应的操作,这个相应的操作也叫算法
数据结构=个体+个体的关系
算法=对存储数据的操作
算法:解题的方法和步骤
衡量算法的标准:
①时间复杂度:大概程序要执行的次数,而非执行的时间
②空间复杂度:算法执行过程中大概所占的最大内存
③难易程度
④健壮性
程序 = 数据的存储+数据的操作+可以被计算机执行的语言
指针是C语言的灵魂
地址:
# include
void f(int *p){
/**
(int *i):不是定义了一个名字叫做*p的形参,
而是定义了一个形参,该形参名字叫做p,他的类型是int *
int *:存放整形变量的地址
*/
*p = 100;
}
int main(void){
int i = 9;
f(&i);
printf("i=%d\n",i); //100
return 0;
}
通过被调函数修改主调函数中普通变量的值:
实参为相关变量的地址
形参为以该变量的类型为类型的指针变量
在被调函数中通过 *形参变量名 的方式就可以修改主函数
①数组名
②下标和指针的关系
a[i]等价于*(a+i)
*a + 3 == a[0]+3;
假设指针变量 的名字是 p;则p+i 的值是 p+i*(p所指的变量所占的字节数);
# include
void Show_Array(int *p,int len){
p[0] = -1; //p[0] == *p; p[2] == *(p+2) == *(a+2) == a[2]
}
int main(void){
int a[5] = {1,2,3,4,5};
Show_Array(a,5);
printf("%d\n",a[0]); //-1
return 0;
}
③指针变量的运算
①为什么会出现结构体
为了表示一些复杂的数据,而普通的基本类型变量无法满足要求
②什么是结构体
结构体是用户根据实际需要自己定义的数据类型
③使用结构体
两种方式:
struct Student st = {1000, “zhangsan”, 20}
struct Student * pst = &st;
1.st.sid
2.pst->sid
pst所指向的结构体变量中的sid这个成员
demo:结构体的第一种使用方法
# include
struct Student
{
int sid;
char name[200];
int age;
};
//分号不能省略
//结构体不是变量 结构体是一种新的数据类型
int main(void)
{
struct Student st = {1000,"zhangsan",20};
//用 前面的数据类型 定义一个st 变量。
printf("%d %s %d\n",st.sid,st.name,st.age);
//1000 zhangsan 20
//或者用以下方式赋值:
//st.sid=99;
//strcpy(st.name,"list");
//st.age=22;
return 0;
}
注意:以上字符串的赋值不可以直接使用st.name=”lisi”;此种写法是错误的。
注意事项
结构体变量不能加减乘除,但可以相互赋值
普通结构体变量和结构体指针变量作为函数传参问题
结构体的第二种使用方法
#include
struct Student{
int sid;
char name[200];
int age;
};
int main(void)
{
struct Student st = {100,"shangsan",20};
// st.age=99;第一种方式
struct Student * pst;
//第二种方式 是最常用的 数据结构中就是用的这种方式
pst = &st;
pst->sid = 99;
//pst -> sid 等价于 (*pst).sid
//(*pst).sid 等价于 st.sid;
}
普通结构体变量和结构体指针变量作为函数传参的问题:
在传递参数时 最好不要传递变量,最好传递指针
因为指针变量永远只占 4个字节,如果你传递了一个结构体变量,那么就可能需要占用几百上千个字节的内存,而你传递一个指针变量只需要四个字节就可以了。
例:
#include
#include
struct Student{
int sid;
char name[200];
int age;
};
void f(struct Student *pst)
{
(*pst).sid = 99;
strcpy(pst ->name,"zhangsan");
pst ->age = 22;
}
void p(struct Studet *pst)
{
printf("%d %s %d",pst.sid,pst.name,pst.age);
}
int main(void)
{
struct Student st ;
//系统已经为st ;分配了内存 但是还没有赋值
//使用一个函数来改变 st的值
f(&st);
p(st);
return 0;
}
假设动态构造一个int型的一位数组
int len;
int * pArr = (int *)malloc (sizeof(int) * len);
①本语句分配了两块内存,一块内存是动态分配的,总共len个字节;另一块是静态分配的,是pArr变量本身所占的内存,总共4个字节(指针变量只占4个字节)。
②malloc只有一个int型的形参,表示要求系统分配的字节数
③malloc函数的功能是请求系统分配len个字节的内存空间,如果分配成功,则返回第一个字节的地址,如果分配不成功,则返回NULL
④malloc函数能且只能返回第一个字节的地址,所以我们需要把这个无任何实际意义的第一个字节的地址(俗称干地址)转化为一个有实际意义的地址,因此,malloc函数前面必须加强制类型转换(数据类型 *)(传入的是什么样的数据类型就强制转换什么数据类型),表示把这个无实际意义的第一个字节的地址转化为相应类型的地址。
⑤free(* pArr)
表示把pArr所指向的内存给释放掉
pArr本身的内存是静态的,不能有程序员手动释放,只能在pArr变量所在的函数运行终止时有系统自动释放
⑥跨函数使用内存
静态内存不可以跨函数使用:
静态内存在函数执行期间可以被其它函数使用
静态内存在函数执行完毕之后就不能在被其它函数使用
动态内存可以跨函数使用
动态内存在函数执行完毕之后仍然可以被其它函数使用
例一:动态的构造一个int类型的数组:
#include
#include
int main(void)
{
int a[5] = {4,5,6,7,8};
int len;
printf("请输入你需要分配的数组长度:len = ");
scanf("%d",&len);
int *pArr = (int *)malloc(sizeof(int)*len);
//我们可以把pArr当作一个普通数组来使用。
int i;
for( i = 0;iscanf("%d",&pArr[i]);
}
for( i = 0;iscanf("%d\n",*(pArr+i));
}
free(pArr);//把pArr 所代表的动态分配的20个字节的内存释放了。
return 0;
}
例二:
#include
struct Student
{
int sid;
int age;
}
struct Student * CreateStudent(void)
{
struct Student * p = (struct Student *) malloc(sizeof(struct Student));
// p 所保存的 动态分配的 结构体数据类型的 变量的地址。
p -> sid = 99;
p -> age =88;
return p;
}
void ShowStudent(struct Student *pst)
{
printf("%d %d\n",pst -> sid,pst->age);
}
int main(void)
{
struct Student * ps;
ps = CraeteStudent();
ShowStudent(ps);
return 0;
}