目录
1.指针
指针的重要性:指针是C语言的灵魂
1.1指针定义:
- 地址:内存单元的编号,从0开始的非负整数
- 指针:指针就是地址,地址就是指针;
- 指针变量:是存放内存单元地址的变量;
- 指针的本质:是一个操作受限的非负整数。
# include <stdio.h>
int main(void)
{
int *p;//p是一个指针变量(变量名字),int*表示该P变量只能存储int类型变量的地址
int i=10;
int j;
p = &i;
//p=10 错误因为10不是一个地址,仅仅只是一个数值
j = *p;
printf("i=%d, j=%d, *p =%d\n",i,j,*p);
return 0;
//结果:i=10, j=10, *p =10
}
- 指针变量也是变量,只不过它存放的不能是内存单元的内容,只能存放内存单元的地址。普通变量前不能加*,常量和表达式前不能加&。
- p保存i的地址,则p指向i。②修改p的值不影响i的值,修改i的值不影响p的值。③p就是表示i,等价于i ,i与p可以在任何地方互换。
//通过指针来交换两个变量
#include <cstdio>
void swap(int *a,int *b){
int temp = *a;
*a = *b;
*b = temp;
}
int main(){
int a = 1, b = 2;
int *p1 = &a,*p2 = &b;
swap(p1,p2);
printf("a = %d, b = %d\n",*p1,*p2);
return 0;
}
错误写法一:
void swap(int *a,int *b){
int * temp;//temp没有被初始化,是一个随机分配的指针,地址指向可能会是系统工作空间,那这样容易出错。 改正:int x;int *temp = &x;
*temp = *a;
*a = *b;
}
错误写法二:
void swap(int *a,int *b){
int * temp = a;
a = b;
b = temp; //指针变量p本身的改变并不会影响原指针变量,就相当于修改p的值不会影响i的值一样。
}
补充:
- *的含义:指针运算符,放在已经定义好的指针变量的前面;如果p是一个已经定义好的指针变量,那 *p表示以p的内容为地址的变量。
- Int * p &p(就相当于int **类型)只有两个类型相同的参数,才能互相调用函数。
即int f(int ** q) int **q相当于指向指针的指针变量,这样调用f时需要写成f(&p)
1.2指针的应用:
1.2.1通过被调函数修改主调函数中普通局部变量的值:
- 实参必须为该普通变量的地址
- 形参必须为指针变量
- 在被调用函数中通过:*形参名=……的方式就可以修改主调函数相关变量的值。
void change (int *p){
*p = 233;
}
int change2 (int t){
t =233;
return t;
}
int main(){
int a = 1;
int *p=&a;
change(p);
printf(%d\n",a);//a=233,
change2(a);
printf(%d\n",a);//a=233,
//利用指针的方法更加优于直接用形参的方法,因为指针的占用内存资源少
return 0;
补充知识: 指针与数组的关系:
一维数组名是个指针常量,它存放的是一维数组第一个元素的地址。
下标和指针的关系:
- 如果p是个指针变量,则p[i]永远等价于*(p+i)
#include <stdio.h>
void Show_Array(int * p,int len){//也可以写成 int p[]的形式,也表示一个数组,
p[0]=-1;//p[0] == *p
}
int main(void){
int a[5] = {1,2,3,4,5};
//a[3]== *(3+a) ; *a+3 = a[0]+3
printf("%p\n",a+1);//0061fef0
printf("%p\n",a+2);//0061fef4
printf("%p\n",*(a+2));//3
for(int *p = a;p<a+5;p++){
printf("%d",*p);}//使用指针变量可以使用自增操作,这样来枚举数组
Show_Array(a,5);//p[2] = *(p+2)=(a+2)=a[2],p[i]就是主函数的a[i]
return 0;
}
重点:因此数组作为参数时,在函数中对数组元素的修改就等同于是对原数组元素的修改(这与普通的局部变量不同)
指针调用指针:
# include <stdio.h>
void f(int **q )
{
*q = (int *)0xFFFFFF;
}
int main()
{
int i = 9;
int *p =&i; //int *p ,p = &i;
printf("%p\n",p);
f(&p);
printf("%p\n",p);
return 0;
}
1.3一个指针变量占四个字节
无论这个指针变量指向的变量占几个字节,该指针变量本身只占四个字节。
原因:
- 四个字节由计算机的地址总线大小决定:
地址总线的宽度决定了CPU的寻址能力;
数据总线的宽度决定了CPU单次数据传输的传送量,也就是数据传输速度;
控制总线决定了CPU对其他控件的控制能力以及控制方式。
- 我们平时所说的计算机是64位、32位、16位,指的是计算机CPU中通用寄存器一次性处理、传输、暂时存储的信息的最大长度。即CPU在单位时间内(同一时间)能一次处理的二进制数的位数。
- 假如,某计算机的地址总线是32位,那么其一次可以处理的信息是32条,每一条地址总线有0或1两种可能,那么32根地址总线一共有232种可能,也就是其描述的地址空间为0x0000 0000 0000 0000 ~ 232-1。我们一般需要32个0或1的组合就可以找到内存中所有的地址,而32个0或1的组合,就是32个位,也就是4个字节的大小,因此,我们只需要4个字节就可以找到所有的数据。所以,在32位的计算机中,指针占4个字节。同理,在64位的计算机中,指针占8个字节。
1.4指针与引用
c++中特有的引用语法:引用相当于给原变量取别名,则对引用变量的操作就是对原变量的操作。
#include <cstdio>
void chang(int & x){
x = 1;
}
int main(){
int x = 10;
change(x);
printf("%d\n",x); //x = 1;
return 0;
}
//对于上题的错误的示例,可以引用来进行修改
#include <cstdio>
void swap(int * &a,int * &b){
int *temp = a;
a = b;
b = temp;
}
int main(){
int a = 1, b = 2;
int *p1 = &a,*p2 = &b;
swap(p1,p2);
printf("a = %d, b = %d\n",*p1,*p2);
return 0;
}
2.break 和continue
- break
- break如果用于循环是用来终止循环
- break如果用于switch,则是用来终止switch
- break不能直接用于If,除非if属于循环内部的一个子句
- 在多层循环中,break只能终止最里面包裹它的那个循环
- 在多层switch中,break只能终止距离它最近的switch
- continue
- 用于跳过本次循环余下的语句,转去判断是否需要执行下次循环
3.结构体
• 为什么会出现结构体:为了表示一些复杂的数据,而普通的基本类型变量无法满足要求;
• 定义:结构体是用户根据实际需要自己定义的复合数类型;
3.1如何使用结构体
//定义结构体
#include <string.h>
struct Student
{
int sid;
char name[200];
int age;
};
struct Student st = {1001,"zhangsan",18};//整体赋值,类似于Java中new类的构造函数
st.id=1001;//单个赋值
strcpy(st.name,"zhangsan");
st.age=18;
struct Student *pst = &st;//通常使用指针的方式赋值
//pst所指向的结构体变量中的sid这个成员
pst->sid=1001;//==(*pst).sid==st.sid
strcpy(pst->name,"lisi");
pst->age=19;
结构体在定义一个对象后,系统为自定进行初始化,数值类型为0,字符类型为空。
• 注意事项 结构体变量不能算术计算,但是可以赋值;
3.2普通结构体变量和结构体指针变量
普通结构体变量和结构体指针变量作为函数传参的问题,推荐使用传递结构体指针的方式,这样效率高节约内存。
#include <stdio.h>
#include <string.h>
struct student
{
int id;
char name[200];
int age;
};
void f(struct student *sp);
void g2(struct student *st);
void g(struct student st);
int main()
{
struct student st;//已经为st分配好了内存
f(&st);
g2(&st);
g(st);
printf("%d %s %d\n",st.id,st.name,st.age);
return 0;
}
//这种方法耗内存,耗时间,不推荐
void g(struct student st)
{
printf("%d %s %d\n",st.id,st.name,st.age);
}
//这种方法永远所消耗地址只有4字节
void g2 (struct student *st)
{
printf("%d %s %d\n",st->id,st->name,st->age);
}
void f(struct student *sp)
{
sp->id = 5;
strcpy ((*sp).name,"sdasf");
(*sp).age = 22;
}
3.3typedef函数的使用
1 typedef int INT; // 相当于给int起了一个别名 INT
2 typedef struct Student
3 {
4 int sid;
5 char name[100];
6 char sex;
7 } ST; //ST st 就相当于 struct Student st,给struct Student 起了别名ST,这样简洁了代码
8 typedef struct Student
9 {
10 int sid;
11 char name[100];
12 char sex;
13 } * PST,STU; //STU就相当于struct Student ,* PST就相当于struct Student *
int main(void)
{
struct Student st;//等价于STU st
ST *ps =&st;//等价于struct Student st
PST ps =&st//等价于struct Student *ps
3.4指针与结构体的结合使用
1 # include <stdio.h>
2 # include <malloc.h>
3
4 struct Student
5 {
6 int sid;
7 int age;
8 };
9
10 struct Student * CreateStudent(void);
11 void ShowStudent(struct Student *);
12
13 int main(void)
14 {
15 struct Student * ps;
16 ps = CreateStudent();
17 ShowStudent(ps);
18 return 0;
19 }
20
21 void ShowStudent(struct Student * pst)
22 {
23 printf("%d %d",pst->sid,pst->age);
24 }
25
26 struct Student * CreateStudent(void)
27 {
28 struct Student * p = (struct Student *)malloc(sizeof(struct Student));
29 p->sid = 1001;
30 p->age = 18;
31 return p;
32 }
4.动态内存的分配和释放
动态内存的分配只能自己手动释放内存,如果不手动释放则内存就会泄露(内存会越来越少)。
4.1如果使用malloc函数进行动态内存的分配和释放
样例代码:
#include <stdio.h>
#include <malloc.h>
int main(void)
{
int a[5] = {4,5,2,8,6};
int len;
printf("请输入你需要分配的数组的长度:len=");
scanf("%d",&len);
int *pArr = (int *)malloc(sizeof(int)*len);
//表示我们的操作系统,要为我们的程序分配20个存储空间,可以进行读写
// malloc函数只返回第一个字节的地址,这样无法区分数组的类型,干地址
//因此需要用一个强制类型转换
//*pArr =4;//类似于a[0] = 4;
//pArr[1] = 10; //类似于a[1] = 10;
//printf("%d %d",*pArr,pArr[1])
//我们可以把pArr当做一个普通数组来使用
for (int i=0;i<len;++i)
scanf("%d",&pArr[i]);
for (int i=0;i<len;++i)
printf("%d",*(pArr+i));
free(pArr);//把pArr所代表的20个动态分配的20个字节的内存释放
return 0;
}
4.2跨函数使用内存
#include <stdio.h>
//跨函数使用内存的方法
int main(void)
{
int i = 10;
i=f(); //当我们调用f是就为j分配空间
//由于j是一个静态变量,因此当f调用完后j的空间就会被释放
printf("i = %d\n" ,i);
i=f2(&i); //则即使f2调用终止后,p内存空间还在。
return 0;
}
int f()
{
int j =20;
return j;
}
int f2(int *p)
{
p = (int *)malloc(sizeof(int));
}
勉励
永远善良、纯真、可爱就好!(*╹▽╹*)
申明:
本文参考资料:
- 谭浩强主编《c语言程序设计(第四版)》
- 郝斌老师教学视频(B站上可以搜索)
建议:读者如果有时间的话,最好自己码码。
如果觉得我的文章对你有所帮助与启发,点赞给我个鼓励吧(づ ̄3 ̄)づ╭❤~
关注我和我一起共勉加油
吧!
如果文章有错误,还望不吝指教!