初学者脱离C语言学习20大误区

13254
回复
9699256
查看
    [复制链接]

 成长值: 19787

等级头衔

头衔 VIP

Rank: 55Rank: 55Rank: 55

积分成就
UID
70
威望
767
贡献
1037
在线时间
8 小时
注册时间
2014-2-26

VIP会员

精华 发表于 2024-2-4 08:31:37 | 显示全部楼层 |阅读模式
编程交流
编程交流: 100个常见编译错误及分析
一、初学者脱离C语言学习20大误区
F148.jpg

在学习单片机的时候才真正知道C语言是什么,它是来干什么的,但是C语言用到嵌入式只是它小小的一部分,他的应用还有很多地方呢,呵呵我们这里就不讨论这个了。我们是不是在写程序的时候错误很多就算编译通过了也达不到我们预期的结果,完了自己是不是也很难找到错在哪儿吧?我觉得语言之所以能称之为语言,它肯定是一种工具一种相互交流相互通信相互传达之间的意图的工具,作为语言那肯定得有自己的语法,要想相互交流肯定得先学。
在学习单片机的时候才真正知道C语言是什么,它是来干什么的~但是C语言用到嵌入式只是它小小的一部分,他的应用还有很多地方呢,呵呵我们这里就不讨论这个了。我们是不是在写程序的时候错误很多就算编译通过了也达不到我们预期的结果,完了自己是不是也很难找到错在哪儿吧?我觉得语言之所以能称之为语言,它肯定是一种工具一种相互交流相互通信相互传达之间的意图的工具,作为语言那肯定得有自己的语法,要想相互交流肯定得先学好它的语法吧(比如像表达式,函数,循环,指针)我称之为C语言的语法。C语言虽然很强大但是他也有不少陷阱,今天有两个目的一是:把C语言一些误用易错的地方总结一下,二是把C语言一些基本语法总结一下。
1.关于自增自减(即++i,i++)
要想给一个数加一或减一我们可以:
i += 1;
j -= 1;
而C语言还允许用++和--运算符,其实这里有误导,因为++和--可以作为前缀和后缀,这样的话他们就有可能改变操作数的值,下面让我们来看看:
i = 1;
printf("i is %d\n",++i); /* prints i is 2 */
printf("i is %d\n",i); /* prints i is 2 */
计算表达式i++的结果是i,但是会引发i随后进行自增:
i = 1;
printf("i is %d\n",i++); /* prints i is 1/ */
printf("i is %d\n",i); /* prints i is 2 */
第一个printf 显示了i自增前的原始值,第二个printf显示了i变化后的新值;当然 -- 类似我就不举例了~
但在同一个表达式中多次使用++和--往往很难理解我们看看下面的例子:
i = 1;
j = 2;
k = ++i + j++;
i,j,k最终值分别是2,3,4而++i是2 j++是2;
总结:不管是++i还是i++执行这条语句后i的值都加一了只是(++i)的值加一了而(i++)没变,
2 ypedef与#define
2.1 typedef
C语言除了直接使用标准的类型名(如 int char float double)和自己声明的结构体、共用体、指针、枚举类型外,还可以用typedef声明新的类型名来代替现有的类型名。
typedef unsigned char u8;
typedef unsigned int u16;
u8 count;
u16 time;
typedef struct
{
u8 month;
u8 day;
u16 year;
}DATE;
DATE brithday;
总结一下,声明新的类型名的方法:
1.先按定义变量的方法写出定义体(如 unsigned int i)
2.在变量名换成新的变量名(如将 i换成u16)
3.在最前面加上typedef (typedef unsigned int u16)
4.然后用新类型名去定义变量
2.2 #define
2.1.1不带参数的宏定义
#define 标识符 字符串
#define PI 3.1415926
注意:
1.它的作用是在本程序中用指定的标识符PI来代替3.1415926
2.宏定义是用宏来代替字符串也就是做简单的置换,不做正确性检查如果写成
#define PI 3.l4l6926
即把1写成了字母l但是预处理照常代入不做任何语法检查!!
2.1.2带参数的宏定义
#define 宏名(参数) 字符串
#define S(a,b) a*b
area = S(a,b);
#define MAX(x,y) (x)>(y) ? (x)y)
3 typedef和#define的区别
一般来说typedef 因为它能正确处理指针类型
typedef char *String1;
#define String2 char *
String1 s1,s2;
String2 s3,s4;
s1,s2,s3 被定义为了char* 但s4却被定义为了char型
3. static 变量
static变量大致分为三种用法
1. 用于局部变量中,成为静态局部变量. 静态局部变量有两个用法,记忆功能和全局生存期.
2. 用于全局变量,主要作用是限制此全局变量被其他的文件调用.
3. 用于类中的成员.表示这个成员是属于这个类但是不属于类中任意特定对象
1. 静态局部变量
静态局部变量属于静态存储方式,它具有以下特点:
(1)静态局部变量在函数内定义 它的生存期为整个源程序,但是其作用域仍与自动变量相同,只能在定义该变量的函数内使用该变量。退出该函数后, 尽管该变量还继续存在,但不能使用它。
(2)允许对构造类静态局部量赋初值 例如数组,若未赋以初值,则由系统自动赋以0值。
(3) 对基本类型的静态局部变量若在说明时未赋以初值,则系统自动赋予0值。而对自动变量不赋初值,则其值是不定的。 根据静态局部变量的特点, 可以看出它是一种生存期为整个源程序的量。虽然离开定义它的函数后不能使用,但如再次调用定义它的函数时,它又可继续使用, 而且保存了前次被调用后留下的值。 因此,当多次调用一个函数且要求在调用之间保留某些变量的值时,可考虑采用静态局部变量。虽然用全局变量也可以达到上述目的,但全局变量有时会造成意外的副作用,因此仍以采用局部静态变量为宜。
举例如下:
本帖隐藏的内容
void fun()
{
static int a = 1;
a++;
}
在第一次进入这个函数的时候,变量a被初始化为1!并接着自增1,以后每次进入该函数,a就不会被再次初始化了,仅进行自增1的操作;在static发明前,要达到同样的功能,则只能使用全局变量:
int a = 1;
void fun()
{
a++;
}
2.静态全局变量
全局变量(外部变量)的之前再加上static 就构成了静态的全局变量。全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。这两者的区别虽在于,非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用, 因此可以避免在其它源文件中引起错误。从以上分析可以看出, 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域, 限制了它的使用范围。因此static 这个说明符在不同的地方所起的作用是不同的。
3.static的类成员变量
static关键字有两种意思,你看上下文来判断
1.表示变量是静态存储变量,表示变量存放在静态存储区.
2.表示该变量是内部连接(这种情况是指该变量不在任何{}之内,就象全局变量那样,这时候加上static),也就是说在其它的.cpp文件中,该变量是不可见的(你不能用)。
C语言初学者存在的误区
本人通过对C的学习总结,积累了一些C编程时常犯的错误,写给各位学员以供参考。  
1.书写标识符时,忽略了大小写字母的区别 
  main()  
  { int a=5;printf("%d",A);}编译程序把a和A认为是两个不同的变量名,而显示出错信息。C认为大写字母和小写字母是两个不同的字符。习惯上,符号常量名用大写,变量名用小写表示,以增加可读性。  
2.忽略了变量的类型,进行了不合法的运算
  main()  
  { float a,b;printf("%d",a%b);} %是求余运算,得到a/b的整余数。整型变量a和b可以进行求余运算,而实型变量则不允许进行“求余”运算。  
3.将字符常量与字符串常量混淆 
  char c;c="a";在这里就混淆了字符常量与字符串常量,字符常量是由一对单引号括起来的单个字符,字符串常量是一对双引号括起来的字符序列。C规定以“\”作字符串结束标志,它是由系统自动加上的,所以字符串“a”实际上包含两个字符:“a'和‘\',而把它赋给一个字符变量是不行的。  
4.忽略了“=”与“==”的区别
  在许多高级语言中,用“=”符号作为关系运算符“等于”。如在BASIC程序中可以写
  if (a=3) then …  
  但C语言中,“=”是赋值运算符,“==”是关系运算符。如:  
  if (a==3) a=b;前者是进行比较,a是否和3相等,后者表示如果a和3相等,把b值赋给a.由于习惯问题,初学者往往会犯这样的错误。  
  5.忘记加分号 
  分号是C语句中不可缺少的一部分,语句末尾必须有分号。  
  a=1 b=2编译时,编译程序在“a=1”后面没发现分号,就把下一行“b=2”也作为上一行语句的一部分,这就会出现语法错误。改错时,有时在被指出有错的一行中未发现错误,就需要看一下上一行是否漏掉了分号。  
  { z=x+y;t=z/100;printf("%f",t);}对于复合语句来说,最后一个语句中最后的分号不能忽略不写(这是和PASCAL不同的)。  
 6.多加分号 
  对于一个复合语句,如:  
  { z=x+y;t=z/100;printf("%f",t);};复合语句的花括号后不应再加分号,否则将会画蛇添足。  
  又如:  
  if (a%3==0);I++;本是如果3整除a,则I加1.但由于if (a%3==0)后多加了分号,则if语句到此结束,程序将执行I++语句,不论3是否整除a,I都将自动加1.  
  再如:  
  for (I=0;I  
  本意是先后输入5个数,每输入一个数后再将它输出。由于for()后多加了一个分号,使循环体变为空语句,此时只能输入一个数并输出它。  
 7.输入变量时忘记加地址运算符“&”
  int a,b;scanf("%d%d",a,b);这是不合法的。Scanf函数的作用是:按照a、b在内存的地址将a、b的值存进去。“&a”指a在内存中的地址。  
  8.输入数据的方式与要求不符 
  ①scanf("%d%d",&a,&b);  
  输入时,不能用逗号作两个数据间的分隔符,如下面输入不合法:  
  3,4  
  输入数据时,在两个数据之间以一个或多个空格间隔,也可用回车键,跳格键tab.  
  ②scanf("%d,%d",&a,&b);  
  C规定:如果在“格式控制”字符串中除了格式说明以外还有其它字符,则在输入数据时应输入与这些字符相同的字符。下面输入是合法的:  
  3,4  
  此时不用逗号而用空格或其它字符是不对的。  
  3 4 3:4  
  又如:  
  scanf("a=%d,b=%d",&a,&b);输入应如以下形式:  
  a=3,b=4  
9.输入字符的格式与要求不一致  
  在用“%c”格式输入字符时,“空格字符”和“转义字符”都作为有效字符输入。  
  scanf("%c%c%c",&c1,&c2,&c3);  
  如输入a b c  
  字符“a”送给c1,字符“ ”送给c2,字符“b”送给c3,因为%c只要求读入一个字符,后面不需要用空格作为两个字符的间隔。  
 10.输入输出的数据类型与所用格式说明符不一致 
  例如,a已定义为整型,b定义为实型  
  a=3;b=4.5;printf("%f%d\n",a,b);编译时不给出出错信息,但运行结果将与原意不符。这种错误尤其需要注意。  
  11.输入数据时,企图规定精度 
  scanf("%7.2f",&a);这样做是不合法的,输入数据时不能规定精度。  
 12.switch语句中漏写break语句 
  例如:根据考试成绩的等级打印出百分制数段。  
  switch(grade)  
  { case 'A':printf("85~100\n");case 'B':printf("70~84\n");case 'C':printf("60~69\n");case 'D':printf("由于漏写了break语句,case只起标号的作用,而不起判断作用。因此,当grade值为A时,printf函数在执行完第一个语句后接着执行第二、三、四、五个printf函数语句。正确写法应在每个分支后再加上“break;”。例如  
  case 'A':printf("85~100\n");break;13.忽视了while和do-while语句在细节上的区别。  
  (1)main()  
  {int a=0,I;scanf("%d",&I);while(I(2)  
  main()  
  {int a=0,I;scanf("%d",&I);do {a=a+I;I++;}while(I可以看到,当输入I的值小于或等于10时,二者得到的结果相同。而当I>10时,二者结果就不同了。因为while循环是先判断后执行,而do-while循环是先执行后判断。对于大于10的数while循环一次也不执行循环体,而do-while语句则要执行一次循环体。
二、100个常见编译错误及
欢迎加入学客联盟
等级头衔

头衔 高级会员

Rank: 35Rank: 35Rank: 35Rank: 35Rank: 35

积分成就
UID
6201300
威望
84
贡献
84
在线时间
0 小时
注册时间
2023-7-19

发表于 2024-2-4 08:31:38 | 显示全部楼层
不敢你学习怎么样,但是不学习肯定完蛋
欢迎加入学客联盟
回复 支持 反对

使用道具 举报

等级头衔

头衔 高级会员

Rank: 35Rank: 35Rank: 35Rank: 35Rank: 35

积分成就
UID
6031109
威望
100
贡献
100
在线时间
0 小时
注册时间
2023-6-13

发表于 2024-2-4 08:31:38 | 显示全部楼层
YYDS
欢迎加入学客联盟
回复 支持 反对

使用道具 举报

等级头衔

头衔 高级会员

Rank: 35Rank: 35Rank: 35Rank: 35Rank: 35

积分成就
UID
5149153
威望
104
贡献
104
在线时间
0 小时
注册时间
2022-3-31

发表于 2024-2-4 08:31:38 | 显示全部楼层
感谢学客联盟提高的技术分享
欢迎加入学客联盟
回复 支持 反对

使用道具 举报

等级头衔

头衔 高级会员

Rank: 35Rank: 35Rank: 35Rank: 35Rank: 35

积分成就
UID
3797841
威望
95
贡献
95
在线时间
0 小时
注册时间
2019-4-18

发表于 2024-2-4 08:31:45 | 显示全部楼层
值得大家了解的网站
欢迎加入学客联盟
回复 支持 反对

使用道具 举报

等级头衔

头衔 高级会员

Rank: 35Rank: 35Rank: 35Rank: 35Rank: 35

积分成就
UID
3696303
威望
96
贡献
96
在线时间
0 小时
注册时间
2019-1-9

发表于 2024-2-4 08:31:53 | 显示全部楼层
要超过别人就需要自己刻苦学习
欢迎加入学客联盟
回复 支持 反对

使用道具 举报

等级头衔

头衔 高级会员

Rank: 35Rank: 35Rank: 35Rank: 35Rank: 35

积分成就
UID
4180861
威望
96
贡献
96
在线时间
0 小时
注册时间
2021-11-27

发表于 2024-2-4 08:32:00 | 显示全部楼层
走过路过千万不要错过
欢迎加入学客联盟
回复 支持 反对

使用道具 举报

等级头衔

头衔 高级会员

Rank: 35Rank: 35Rank: 35Rank: 35Rank: 35

积分成就
UID
4225500
威望
99
贡献
99
在线时间
0 小时
注册时间
2021-11-30

发表于 2024-2-4 08:32:07 | 显示全部楼层
相信有位置在
欢迎加入学客联盟
回复 支持 反对

使用道具 举报

等级头衔

头衔 中级会员

Rank: 25Rank: 25Rank: 25Rank: 25Rank: 25

积分成就
UID
5340475
威望
82
贡献
82
在线时间
0 小时
注册时间
2022-4-19

发表于 2024-2-4 08:32:07 | 显示全部楼层
不经意已经在这里学习好多内容了
欢迎加入学客联盟
回复 支持 反对

使用道具 举报

等级头衔

头衔 高级会员

Rank: 35Rank: 35Rank: 35Rank: 35Rank: 35

积分成就
UID
4034033
威望
96
贡献
96
在线时间
0 小时
注册时间
2021-10-28

发表于 2024-2-4 08:32:14 | 显示全部楼层
不管什么样的界面,我们都支持
欢迎加入学客联盟
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

电脑技术交流学习
132-3591-5703
周一至周五 9:00-18:00
意见反馈:admin@cnxklm.com

扫一扫访问手机版

Powered by NST! X3.4© 2001-2021技术支持( 闽ICP备14006427号 闽公网安备35090202000370号)|意见建议