c语言重点与难点

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=18struct 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))}

勉励
永远善良、纯真、可爱就好!(*╹▽╹*)
申明:
本文参考资料:

  1. 谭浩强主编《c语言程序设计(第四版)》
  2. 郝斌老师教学视频(B站上可以搜索)

建议:读者如果有时间的话,最好自己码码。

如果觉得我的文章对你有所帮助与启发,点赞给我个鼓励吧(づ ̄3 ̄)づ╭❤~
关注我和我一起共勉加油吧!
如果文章有错误,还望不吝指教!

猜你喜欢

转载自blog.csdn.net/qq_43992949/article/details/105972569