C语言程序设计 详细注意事项

C语言程序设计(谭浩强版本)-考研版注意事项-适合考研和C语言入门小白食用


前言

计算机考研人数的剧烈增多,博主也是考研er大军中的一员,自命题的C语言考试正在逐年转为统考408,所以大家需要抓紧时机,积极备考!趁着初试结束,打算每周更新一下C语言程序设计(谭浩强版)这本小红书,结合着小红书和自己的经验,写一下其中有比较多的细节和干货以及挖坑的注意事项,欢迎大家收看,如有错误的地方,请客官大人及时批评指正~


一、指针是什么?其实指针=地址

首先我们要明确,C语言所说的指针,就是地址,提到指针,它就是地址!!,两者完全相同!
至于为什么地址也叫做指针,请看下面的解释:
理解指针,首先要理解 变量变量的存储单元,以及**地址(指针)**的关系:
请添加图片描述
我这里的展示的是一段内存用户数据区,其中2000-2004这一个区域叫做存储单元,当然你可以把2004-2008这个也叫做存储单元,因为我们没有给这两部分取名字,所以叫谁存储单元都是正确的。

那么现在我们开始创建一个变量:

int a;//这里的a是一个int型的变量,占4个字节

当我们创建一个变量的时候,计算机会自动帮我们寻找一个4字节的存储单元的空间,假设计算机此时正好寻找了2000-2004这一区域(实际上是随机寻找的),那么2000-2004这一存储单元叫做变量a;
那么2000-2004这一块区域,叫做变量a的存储单元首地址2000叫做变量a的存储单元的地址

二、指针变量

刚刚介绍完毕上面三者的关系,我们知道,变量是用来存储对应的数据的,例如int类型的变量只能存储int类型的数据,float类型变量只能存储float类型数据…
那么现在C语言有一种能存储地址的变量(例如有一种专门存储变量a的存储单元首地址的变量),我们是不是叫做地址变量呢?emmm当然不是,因为c语言已经规定了这种变量叫做指针变量,而指针变量存储的内容,就是地址,(而且一定是地址,不能是其他数据),由于叫做指针变量,所以它存储的内容,人们也习惯叫做指针。
所以指针=地址,只不过是不同的称呼罢了。
所以P222页有一段代码是一个经典的错误:
因为C语言把这个整数100它只会当作int型数据去处理,而不是你自以为的把100认为是地址;
程序是不能用一个数值代表地址,对于地址只能用& 得到并赋值给一个指针变量

int *pointer_1;
* pointer_1=100;//这个赋值是错误的
int a,*pointer_1;
* pointer_1=&a;//这个赋值是正确的

三、指针变量作为函数参数 -值传递和地址传递区分

本书对于函数的参数传递方式一共就介绍了两种:值传递和地址传递
P224页
从此页开始介绍指针变量作为函数的参数的情况,在这之前,本书的参数传递方式是值传递,也就是参数不涉及到所谓的指针类型,一般函数参数都是变量:

void add(int a,int b);//参数a b都是int类型的变量,不是指针类型,判断为值传递;

何为值传递? 先给大家分析一下值传递:

请大家看一下代码:

1.	void swap1(int x,int y)
2.	{
3.		int temp;
4.		temp = x;
5.		x = y;
6.		y = temp;
7.		printf("x = %d,y = %d\n",x,y);	
8.	}
9.	int main() 
10.	{
11.		int a = 12,b = 24;
12.    swap1(a,b); // 值传递 
13.    printf("a = %d b = %d",a,b);
14.	}


结果如图所示:
在这里插入图片描述

分析:

首选我们都知道main实际上也是一个函数,它的作用范围是 第10行-第14行
swap1也是一个函数,它的作用范围是1-8行
main函数中的变量与swap函数区域中的变量互不冲突的:
程序未执行前:
在这里插入图片描述
当程序执行到第12行时候:
main函数中的变量a把12这个int型数据赋值给swa1中的变量x(变量b对y也是同理),之后后续的在swap1函数中的操作,全是针对swap1函数中的变量x和变量y进行的,与Main函数中的变量a和p没有关系了! ,a和b仅仅起到了赋值的作用!! ! !

在这里插入图片描述
所以swap1中的x和y的交换,实际上是swap1函数中的x和y交换,与main函数中的变量a和b没有关系,而且我们是在main函数中的是 printf(“a = %d b = %d”,a,b); ,当然要输出的是main函数中的变量a和b,

何为地址传递?

在前面一和二章节的基础之上,我们分析下面的例子:

1.void swap2(int* x,int* y)
2.{
3.	int temp;
4.	temp = *x;
5.	*x = *y;
6.	*y = temp;
7.	printf("x = %d y = %d\n",*x,*y);
8.}
9.int main(int argc, char *argv[]) 
10.{
11.	int a = 12,b = 24;
12.	swap2(&a,&b); //地址传递
13.	printf("a = %d b = %d",a,b); 
14.}


**

1.程序未执行前的状态:

这一次我们标注好了Main函数中变量a和变量b的地址,(下面那个打错了,应该是swap2的函数作用区域在这里插入图片描述

.
2. 当程序执行到第12行时候:由于&a和&b代表的是地址,即参数是地址,程序执行的是地址传递的方式,地址传递不同于值传递,此刻的变量a和变量b会把自己的地址传递给变量x和变量y:
在这里插入图片描述
3.那么后续中对swap2函数中的操作:

1.void swap2(int* x,int* y)
2.{
3.	int temp;
4.	temp = *x;// *是代表把指针变量x所存储地址(x当中存储的是2000  也就是main函数中变量a的地址) 的对应内容(地址2000(即变量a的地址)对应的内容是12)交给temp;
5.	*x = *y;//   把指针变量y存储的地址   对应的内容 交给  指针变量x存储的地址  对应的空间
6.	*y = temp;//同上理
7.	printf("x = %d y = %d\n",*x,*y);
8.}

这样实际上在swap2函数中对地址的操作,实际上是在对main函数中的变量内容进行操作,所以最后的结果为:
在这里插入图片描述

四、 通过指针引用数组

这一章节,介绍新的引用数组元素的方法:指针法
引用数组元素一般有两种方法:1.下标法 2.指针法;
看下面一段代码:

int a[7]={1,2,3,4,5,6,7};//定义一个数组,名字为a;
int *p;
p=&a[0];//&表示把取首地址的值,这里把a【0】的首地址2000取了出来,交给指针变量P存储

数组空间如下图所示:

请添加图片描述

从图中我们可以看出,a[0]的首地址为2000;指针变量p的内容也是2000,C语言规定,数组名不代表整个数组,而是数组名会代表数组首元素的地址,即数组名字a是一个指针型的常量,这里数组的首元素为a[0],首元素的地址为2000,所以单独拿出来a这个名字,就代表2000这个地址:
所以P=&a[0] 和p=a是等价的:
由于数组名字a是个指针型的常量(即地址的值固定)不能出现对它进行赋值的操作:
这就好比一个常量2, 你还要进行2=2+1的操作;下面的操作全是语法错误;

数组名可以赋值给别的变量,但是自己不能作为被赋值的变量,因为它是常量常量!指针型常量!!!!

a++;//错误 a++等价于a=a+1;
a=a+1;//错误
p=a++;//错误  等价于p=a;a=a+1;

p=a,程序解释为把数组a的首元素的地址赋值给指针变量P,和p=&a[0]的解释是一个道理,所以两者是等价的;
总结一下,下面四个是等价的:

书中的等价
一维数组int *p=&a[0]; 和int *p =a; 和 int *p; p=&a[0]和int *p; p=a;

在指针已经指向一个元素的前提之下:
我这里P是指向a【0】的,因为P存储的地址和a【0】的相同。
规定:
P+1指向的是数组的下一个元素;也就是p存储的地址本来是和a【0】地址相同即2000,p+1之后,p存储的地址会和a【1】的地址相同,即2004,不难看出,P+1 实际上是增加了4个字节(2004-2000),这是由于P的基类型所决定的,忘记基类型是什么的可以去翻书看一下~
同理,P-1指向的是数组的上一个元素;
所以a[i]的地址和a+i表示的地址是相同的;
所以指针法表示数组:* (a+i)和 *(p+i)表示的是a[i];
第二个等价:

书中的等价
一维数组int *p=&a[0]; 和int *p =a; 和 int *p; p=&a[0]和int *p; p=a;
一维数组*(a+i)等价于a[i] ;因为a[i]和a+i的地址是相同的

五、自增自减运算符区分 以 ++n和n++示例

一、基础变量类型的自增自减

自增自减运算符,当我们去查看书或者在网上搜寻区别的时候,最多的回答就是:
n++是先赋值,n再加1
++n是n先加1,再去赋值

这样的解释把,其实是很正确的,但是,对于新手小白却不友好,因为说出这样话的人,已经能够深刻的理解到,他们能够区分出来两者到底不同于什么地方,以至于传授经验的时候,不容易被小白同学们消化,下面我会多举例子来验证一下两者的区别:

示例1:单独使用

int main()
{
	int a=0,b=0;
	a++;
	++b;
	cout<<"a的值为:"a<<"b的值为:"<<b<<endl;
}

运行结果:
a=1;b=1;

这种情况下的的a++和b++,当程序执行到a++的时候,执行的操作就是对a的值进行+1;也就是a=1;
同样的情况下++b也是如此,当执行到++b的时候,同样程序进行的操作直接对b进行+1,也就是b=1;
那么这样看过来,好像a++和b++的结果也没啥区别?,是的,结果确实是没啥区别,最后a和b的值都为1;

示例2:用在赋值表达式当中

int main(){
	int a=0,b=0,num_1,num_2;
	num_1=a++;
	num_2=++b;
	printf("%d,%d,%d,%d",num_1,num_2,a,b);
}

运行结果:
num_1=0;num_2=1;a=1;b=1;

那么这一次,a++和++b不像示例1作为一个单独的运算出现,而是参与到了赋值运算当中,将值需要赋值给num_1和num_2;
这个时候我们可以看出来a++和++b之间的区别了,对它进行详细分析:
当程序执行num_1=a++的时候:
实际上按照运算符的优先级的基础上,程序会对于num_1=a++,这一段代码,进行分两步进行:
1.由于a++的特性是先赋值再运算,刚开始时候a=0,所以会把num_1通过变量a的值,赋值为0;
2.然后在对a进行+1运算,这个时候a+1,a从0->1;
当程序执行num_2=++b的时候:
实际上按照运算符的优先级的基础上,程序会对于num_2=++b,这一段代码,进行分两步进行:
1.由于++b的特性是先运算再赋值,,刚开始的时候程序读到这一段,会首选对num_2=++b的变量b进行+1,也就是b=1;
2.然后b的值现在为1,再对num_2进行赋值操作,这个时候num_2就获取到了1这个值,程序结束.

示例3:用在条件循环表达式当中

int main(){
	int a=1;
	while(a++<5);
	printf("%d",a);//输出a的值
	int b=1while(++b<5);
	printf("%d",b);//输出b的值
	
}

运行结果:
a=6;b=5

这里自增运算符不仅可以用到赋值表达式当中,也可以用到条件表达式当中;
我们应该有这举一反三的思想,既然在赋值表达式里面a++应该是先赋值在+1,那么在条件循环表达式当中,应该是先执行循环条件然后再+1,下面请看分析:
例子:while(a++<5);其中a++<5称为循环条件;
程序执行过程依分为两步走:

  1.  a=1,1<5 while表达式为T ;
    
  2.  a+1;即a=2;
    

然后去执行while循环体中的内容;我们这里没有内容,所以就会再一次进行判断:

  1.  此时a=2,2<5;while表达式为T;
    
  2.  		a+1;即a=3;  ...
    

然后去执行while循环体中的内容;我们这里没有内容,所以就会再一次进行判断:
当执行到:

  1.  a=5,5<5;while表达式为F,
    
  2.  a+1	;a=6;
    

这个时候由于while为F,程序结束,所以a=6;
同理,++b<5的情况完全是上面的情况反着来,程序会对b先+1,然后在执行判断条件b<5;最后结束循环的时候,b=5;

二.指针类型的自增自减的使用

关于指针类型的自增自减,其实和基本变量类型的处理方式差不多:
无论a++是在哪一个表达式当中,记住要先****,在a+1;
++a则是先a+1,在进行****;

示例1:用在*运算符中

int main(){
	char *s="abcdef";
	char *p=s;
	printf("%c",*p++);
	printf("%c",*++p);
}

运行结果;
a
c
这里p指向的是字符串中字符a的首地址;
printf("%c",p++);
1.先printf("%c",p);由于p指向的是字符a,所以打印出来了a
2.p+1,此时p会指向b;
printf("%c",
++p);
1.先p+1,此时p从指向b变为指向字符c;
2…先printf("%c",p);由于p指向的是字符c,所以打印出来了c

总结

由于我只是挑出来一些重点和易错易混淆的知识点去讲解,并不是全书的内容哦,所以想要真正的去学号C语言的入门,还是需要结合那个小红书(谭浩强版本C语言)踏踏实实的去看,不建议大家抱着一个C primer 那个大厚书去学习,那个是用来随时查询某些方法或者知识点用的,入门的话还是选个讲解简单通透的书就可以,当你对C语言有了一定基础,再去看那本大厚书,来看由于我还要准备刷题备战等原因,更新得会慢一些,如果对读者朋友们有所帮助,不要忘记给个赞哦~你的点赞是我更新的最大动力!祝愿大家都顺利上岸!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
C语言编程注意事项 1. 每个程序中一定包含main()函数, 尽管C语言中对函数命名没有限制。 2. printf函数永远不会自动换行, 只能用\n来实现, 回车键进行的换行在编译中会出现错误信息。 3. 在vs2008等平台中编译、测试要加 system("pause"); 来暂停dos自动退出引起的printf无法显示。 4. 所有自定义变量必须声明才能使用。 5. 每行一般只写一条语句, 在运算符两边加一个空格, 便于阅读。 6. 整数除法将会自动舍位, 不进行四舍五入的操作。 7. for(初始化部分;条件部分;增长部分) 比while 更适用于初始化和增长步长都是单条语句的情况。 8. 使用 #define 名字替换文本对部分"幻数" 赋予意义便于阅读 #define结尾无需;号来结束。 9. EOF(end of file)表示没有字符输入时定义在stdio.h 头文件中 EOF不等于\n 换行等。 10. 由于!= 的优先级大于 = ,因此如果对判断中存在变量赋值时 应对赋值加() 例如: while((c = getchar()) != EOF)。 11. getchar() 用于用户输入直至键入回车键。 12. 变量名以字母和数字组成, (下划线"_"被默认为字符, 以下划线为首写字母的为库类变量名), 变量常以小写字母开头,内部变量名前31位有效, 外部变量名至少前6位保持唯一性。 13.在C语言中是区分字母大小写的。 14. 一个字符常量为一个整数, 用''单引号括起来。例如: '0' 为48, 它与0没有任何关系。 15. ANSI C语言的转义符: \a 响铃符; \b 回退符; \f 换页符; \n 换行符; \r 回车符; \t 横向制表符 ; \v 纵向制表符; \\ 反斜杠; \? 问号; \' 单引号; \" 双引号; \ooo 八进制数; \xhh 十六进制数; '\0' 表示0, 即(null)。 16. 'x'与"x"的区别: 'x'表示一个整数, 字母x在其字符集中对应的数值, "x"表示包含一个字符x 以及一个结束符'\0'。 17. const在声明变量中起限制作用, 该限定变量的值不能被修改。 18. %运算符不能应用于float或double类型。 19. char类型转换为int型时, 因为无法判断它是signed还是unsigned,这样其结果有可能为负数, 所以转换时尽量指定signed和unsigned限制符。 20. ++i与i++ 的不同之处: ++i是先 +1再使用i, i++ 是先使用i再 +1。 21. 三元运算符"?:" 第一个表达式的括号不是必须的, 但是由于三元表达式的优先级非常低, 因此我们还是将第一个表达式用()括起来以便于阅读和理解。 22. C语言中可以使用递归 (即函数调用自身函数), 这样做并不节省储存空间也不加快执行速度, 只是使程序紧凑便于理解。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵙嵙程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值