郝斌老师——心中永远的神!
I 指针
1 什么是指针
#include<stdio.h>
int main(void)
{
int *p; //p是一个指针变量的名字, int* 表示该p变量只能储存int类型的地址,
//地址就是内存单元的编号,cpu可以直接访问内存,
//通过三根线:cpu——[地址线(访问内存编号),控制线(读还是写),数据线(传输数据)≡]——内存
//地址:内存单元的编号 是从0开始的非负整数 范围(0~4G-1)
//指针:指针就是地址,地址就是指针。指针变量是存放内存单元地址的变量
//指针的本质是一个操作受限的非负整数
int i = 10;
int j;
p = &i;
printf("%d\n",*p);
return 0;
}
2 空指针
#include<stdio.h>
int main(void)
{
int *p;
int i = 10;
int j;
j = *p; //p里面没有保存一个变量的地址,所以不能赋值
printf("%d\n",j);
return 0;
}——》崩溃
3 指针的使用
#include<stdio.h>
int main(void)
{
int *p;
int i = 10;
int j;
p = &i; //把i的地址发送给P p就指向了i 修改p的值不改变i的值,
//修改i的值也不改变p的值
//告诉我*p是谁? 回答是:i变量 *p就代表了i 他俩是一个东西
//int* p = i; 等价于 int *p; p = &i;
printf("%d\n",*p);
j = *p; //等价于 j = i;
printf("%d\n",j);
return 0;
}——》10 10
4 局部变量问题:如何通过被调函数修改主函数中变量的值
void f(int* p)
{
*p = 100;
}
#include<stdio.h>
int main(void)
{
int i = 9;
f(&i); //值传递 把i的地址值传递过去 *p = 100 就相当于 i = 100 重新对i赋值了
printf("%d\n",i);
return 0;
}——》100
如果不用上面方法:
void f(int p)
{
p = 100;
}
#include<stdio.h>
int main(void)
{
int i = 9;
f(i); //值传递 9 = 100; 不会造成任何影响
printf("%d\n",i);
return 0;
}——》9
5 用被调函数改变主调函数中变量的地址
void f(int* p)
{
*p = 99;
}
#include<stdio.h>
int main(void)
{
int i = 10;
int* p = &i;
printf("%d\n",i);
printf("%p\n",p);
f(&i);
printf("%d\n",i);
printf("%p\n",p);
return 0;
}——》10 0019FF2C 99 0019FF2C
——》可以看到这样操作并没有改变该变量所在地址
——》那么如何改变变量的地址呢
void f(int** q) //既然我们要修改变量的地址,那我们传进来的就是
//变量自身对应的地址变量的地址
//那么我们现在再从这个逻辑中抽身出来,如何通过被调函数
//修改主函数中普通变量的值 —— 上面自然已经说清了
{
*q = (int*)0xFFFFFFFF;
}
#include<stdio.h>
int main(void)
{
int i = 10;
int* p = &i;
printf("%d\n",i);
printf("%p\n",p);
f(&p); //函数的参数是地址的地址
printf("%d\n",i);
printf("%p\n",p);
return 0;
}——》10 0019FF2C 10 FFFFFFFF 被改变了
6 指针和数组
#include<stdio.h>
int main(void)
{
//一维数组的各个元素的地址是连续的(步长)
int a[5] = {
1,2,3,4,5}; //一维数组名是个指针常量,
//他存放的是一维数组第一个元素的地址,
//他的值不能被改变,
//一维数组名(这个指针)指向的是数组的第一个元素
//a[i] 等价于 *(a+i)
printf("%p\n",a);
printf("%p\n",a+1);
printf("%p\n",a+2);
printf("%p\n",a+3);
printf("%p\n",a+4);
//ps:看这个 他等价于 a[0]+3
printf("%d\n",*a+3); !!+3在严格意义上讲是+(3*相应步长) int 步长为4
return 0;
}——》0019FF1C 0019FF20 0019FF24 0019FF28 0019FF2C 4
7 局部变量问题(数组)
void Show_Array(int* p)
{
p[0] = -1; // p[0]等价于*p
p[2] = -2; // p[2]等价于*(p+2)
}
#include<stdio.h>
int main(void)
{
int a[5] = {
1,2,3,4,5};
Show_Array(a); //值传递 把数组的首地址传过去了 相当于重新赋值
printf("%d\n",a[0]);
printf("%d\n",a[2]);
return 0;
}——》-1 -2
8 用函数输出数组
void Show_Array(int* p ,int len)
{
int i = 0;
for(i=0; i<len; i=i+1)
{
printf("%d\n",p[i]);
}
}
#include<stdio.h>
int main(void)
{
int a[5] = {
1,2,3,4,5};
Show_Array(a,5);
return 0;
}——》1 2 3 4 5
9 无论指针指向的谁,指针变量本身只占四个字节
#include<stdio.h>
int main(void)
{
double* p;
double x = 66.6;
p = &x; //x占8个字节, 一个字节是8位,一个字节一个地址。
printf("%p\n",p);
double arr[3] = {
1.1,2.2,3.3};
double* q;
q = &arr[0];
printf("%p\n",q); //%p其实就是以十六进制输出
q = &arr[1];
printf("%p\n",q);
return 0;
}——》0019FF24 0019FF0C 0019FF14 0C(12) + 8 = 14(20)
浮点数是八个字节就间隔了八个地址(氦 就是一个步长是八字节嘛)
II 结构体
结构体
为什么会出现结构体:面向对象语言中包含类的概念:类包含属性和方法。而结构体没有方法
属性也不叫属性,而是叫成员。
结构体是用户根据实际需要自己定义的复合数据类型。结构体不是变量,而是数据类型。变量
在定义的时候是需要分配内存的。
结构体变量不可以加减乘除,但是可以相互赋值
1 “.”访问结构体
#include<stdio.h>
#include<string.h>
struct Student
{
int sid;
char name[200];
int age;
}; //这个分号是不能省略的
int main(void)
{
struct Student st1 = {
1000,"yunduo",20};
//对成员重新赋值
st1.sid = 9999;
//st1.name = "liyunduo"; //error
strcpy(st1.name,"liyunduo");
st1.age = 21;
printf("%d\n",st1.sid);
printf("%s\n",st1.name);
printf("%d\n",st1.age);
return 0;
}——》1000 yunduo 20
2 “地址”访问结构体
#include<stdio.h>
#include<string.h>
struct Student
{
int sid;
char name[200];
int age;
};
int main(void)
{
struct Student st1 = {
1000,"yunduo",20};
struct Student* adrst2 = &st1;
adrst2 -> sid = 888; // adrst2 -> sid 等价于 (*adrst2).sid
//这句话:表示adrst2所指向结构体变量中的这个 —》sid成员《—
strcpy(adrst2 -> name,"windoo");
adrst2 -> age = 18;
printf("%d\n",adrst2 -> sid);
printf("%s\n",adrst2 -> name);
printf("%d\n",adrst2 -> age);
return 0;
}——》888 windoo 18
3 通过被调函数更改主调函数中定义结构体的成员值
#include<stdio.h>
#include<string.h>
struct Student
{
int sid;
char name[200];
int age;
};
void f(struct Student* adrstx)
{
adrstx -> sid = 88; //或 (*pst).sid = 90;
strcpy(adrstx -> name,"888");
adrstx -> age =8888;
}
void f1(struct Student* adrst) //读取时候也要用指针,占内存少,快速。
{
printf("%d\n",adrst -> sid);
printf("%s\n",adrst -> name);
printf("%d\n",adrst -> age);
}
int main(void)
{
struct Student st;
struct Student* adrst2 = &st;
f(adrst2);
f1(adrst2);
return 0;
} ——》88 888 8888
III 内存
1 动态内存分配malloc
#include<stdio.h>
#include<malloc.h>
int main(void)
{
int len;
int a[5] = {
4,10,2,8,6}; //静态数组,数组的长度是死的,通过下标访问元素。
printf("请输入你需要的数组的长度 len = ");
scanf("%d",&len);
int* pArr = (int* )malloc(sizeof(int) * len);
//malloc:获取x字节的可访问内存,
//我们有x个字节,但是第一个字节没有实际含义,需要(int* )
//这样来强制转换,让编译器明白,我们第一个字节的地址是int类型地址
//那么当你下次操作时候,编译器就会明白pArr+1 的意思是想下走四个字节的地址
//动态数组和静态数组在使用上方法相同
*pArr = 4; //就类似于 a[0] = 4;
pArr[1] = 10; //就类似于 a[1] = 10;
printf("%d,%d\n",*pArr,pArr[1]);
free(pArr); //把pArr所代表的动态分配的x字节的内存释放,把控制权限归还给操作系统
return 0;
}
2 被调函数:调用时为其分配所需内存,调用后直接释放。
#include<stdio.h>
#include<malloc.h>
struct Student
{
int sid;
int age;
};
struct Student CreateStudent(void)
{
struct Student st1 = {
88,88};
return st1;
}
void ShowStudents(struct Student st)
{
printf("%d %d\n",st.sid,st.age);
}
int main (void)
{
struct Student s;
s = CreateStudent();
ShowStudents(s); //虽然得到结果 88 88 但是你在CreateStudent()使用过、
//的内存已然消失的无影无踪
return 0;
}——》88 88
3 如何跨函数使用内存呢
用malloc 然后你不去释放
#include<stdio.h>
#include<malloc.h>
struct Student
{
int sid;
int age;
};
struct Student* CreateStudent(void)
{
struct Student* p = (struct Student*)malloc(sizeof(struct Student));
p->sid = 88;
p->age = 88;
return p;
}
void ShowStudents(struct Student* pst)
{
printf("%d %d\n",pst->sid,pst->age);
}
int main (void)
{
struct Student* ps;
ps = CreateStudent();//这样ps就像接盘侠一样把p这块内存给接手了
ShowStudents(ps);
return 0;
}