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 ̄)づ╭❤~
关注我和我一起共勉加油吧!
如果文章有错误,还望不吝指教!

目录 1. C 语言中的指针和内存泄漏 5 2. C语言难点分析整理 10 3. C语言难点 18 4. C/C++实现冒泡排序算法 32 5. C++中指针和引用的区别 35 6. const char*, char const*, char*const的区别 36 7. C中可变参数函数实现 38 8. C程序内存中组成部分 41 9. C编程拾粹 42 10. C语言中实现数组的动态增长 44 11. C语言中的位运算 46 12. 浮点数的存储格式: 50 13. 位域 58 14. C语言函数二维数组传递方法 64 15. C语言复杂表达式的执行步骤 66 16. C语言字符串函数大全 68 17. C语言宏定义技巧 89 18. C语言实现动态数组 100 19. C语言笔试-运算符和表达式 104 20. C语言编程准则之稳定篇 107 21. C语言编程常见问题分析 108 22. C语言编程易犯毛病集合 112 23. C语言缺陷与陷阱(笔记) 119 24. C语言防止缓冲区溢出方法 126 25. C语言高效编程秘籍 128 26. C运算符优先级口诀 133 27. do/while(0)的妙用 134 28. exit()和return()的区别 140 29. exit子程序终止函数与return的差别 141 30. extern与static存储空间矛盾 145 31. PC-Lint与C\C++代码质量 147 32. spirntf函数使用大全 158 33. 二叉树的数据结构 167 34. 位运算应用口诀和实例 170 35. 内存对齐与ANSI C中struct内存布局 173 36. 冒泡和选择排序实现 180 37. 函数指针数组与返回数组指针的函数 186 38. 右左法则- 复杂指针解析 189 39. 回车和换行的区别 192 40. 堆和堆栈的区别 194 41. 堆和堆栈的区别 198 42. 如何写出专业的C头文件 202 43. 打造最快的Hash表 207 44. 指针与数组学习笔记 222 45. 数组不是指针 224 46. 标准C中字符串分割的方法 228 47. 汉诺塔源码 231 48. 洗牌算法 234 49. 深入理解C语言指针的奥秘 236 50. 游戏外挂的编写原理 254 51. 程序实例分析-为什么会陷入死循环 258 52. 空指针究竟指向了内存的哪个地方 260 53. 算术表达式的计算 265 54. 结构体对齐的具体含义 269 55. 连连看AI算法 274 56. 连连看寻路算法的思路 283 57. 重新认识:指向函数的指针 288 58. 链表的源码 291 59. 高质量的子程序 295 60. 高级C语言程序员测试必过的十六道最佳题目+答案详解 297 61. C语言常见错误 320 62. 超强的指针学习笔记 325 63. 程序员之路──关于代码风格 343 64. 指针、结构体、联合体的安全规范 346 65. C指针讲解 352 66. 关于指向指针指针 368 67. C/C++ 误区一:void main() 373 68. C/C++ 误区二:fflush(stdin) 376 69. C/C++ 误区三:强制转换 malloc() 的返回值 380 70. C/C++ 误区四:char c = getchar(); 381 71. C/C++ 误区五:检查 new 的返回值 383 72. C 是 C++ 的子集吗? 384 73. C和C++的区别是什么? 387 74. 无条件循环 388 75. 产生随机数的方法 389 76. 顺序表及其操作 390 77. 单链表的实现及其操作 391 78. 双向链表 395 79. 程序员数据结构笔记 399 80. Hashtable和HashMap的区别 408 81. hash 表学习笔记 410 82. C程序设计常用算法源代码 412 83. C语言有头结点链表的经典实现 419 84. C语言惠通面试题 428 85. C语言常用宏定义 450
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值