1.文章背景:
阅读《C陷阱与缺陷》后,受益匪浅。下面我将结合自己的理解,帮助C初学者和C基础不是很好的小伙伴更加深入的理解C编程的底层逻辑。希望大家在阅读这篇文章后能够十分自信的写出自己的代码。(本人第一次写博客,如有错误或是表达不准确的地方,欢迎各位技术大佬前来指正!)
2.字符,字符串,printf函数与指针的关系
2.1 字符&字符串
在C语言中,单引号与双引号含义天差地别。在某些情况下如果把两者弄混淆,在一些编译器中并不会报错,从而在运行是产生的结果与预期值有很大的出入。
2.1.1 字符
用单引号引起来的字符实际上代表一个整数,这个整数值是该字符对应的ASCII值。我们可以把它放在char类型的变量中,当然也可以放在int类型的变量中。因为这两种类型都可以接受整型值,但是我不提倡把一个字符放在int类型的变量中。因为作为一个合格的程序员要保证自己所写程序的严谨性,把一个字符放在int类型的变量中会显得类型不统一。在printf函数中以%d(十进制)打印,得到该字符的ASCII值。以%c打印,得到该字符。
2.1.2 字符串
用双引号引起的字符串,代表的是一个指向无名数组起始字符的指针,该数组被双引号之间的字符以及一个额外的二进制值为零的字符'\0'初始化。
既然一个字符串代表了一个指针,那么就要把它放在字符指针类型的变量中。当然,字符串不可更改,需要用const来保护它。如果不加const,在某些编译器中会报警告。
这个是不加const的情况:
2.2 字符串&printf函数
谈到这里,不知道大家有没有一个疑问。除了字符串,在printf函数参数中也经常出现双引号,那么这两个双引号的含义一样吗?
笔者认为它们的含义是完全一样的。例如printf("love world,love bit\n");这里printf函数参数就是一个字符串,换句话说是一个指向无名数组起始字符的指针。既然这个字符串代表了一个字符指针,我们是否可以把一个字符指针
直接给printf调用呢?当然可以!
以上的两种写法是完全等价的。
补充一点,任何数组的数组名就是指向该数组首元素的指针。关于数组与指针的讲解在下文会讲到。这也是C语言中最难以理解,也是最令人头疼的地方。
到这里相信大家对字符,字符串和printf函数的理解有了新的高度。
那么像 char* ch='a'; printf('\n');的这类写法显然是错误的。以下是更正后的写法:const char* ch=“a"; printf("\n");
3.C编译器识别代码的方法:贪心法
当C编译器读入一个字符'/'后又跟了一个字符'*',那么编译器就必须做出判断:是将其作为两个分别的符号对待,还是合起来作为一个符号对待。C语言对这个问题的解决方案可以归纳为一个很简单的规则:每一个符号应该包含尽可能多的字符。也就是说,编译器将程序分解成符号的方法是,从左到右一个字符一个字符地读入,如果该字符可能组成一个符号,那么再读入下一个字符,重复上述判断,直到读入的字符组成的字符串已不再可能组成一个有意义的符号。这个处理策略有时被称为”贪心法“。需要注意的是,除了字符串与字符常量,符号的中间不能嵌有空白(空格符,制表符和换行符)。例如,==是单个符号,而= =则是两个符号。
下面这个表达式 a+++b的含义与表达式a++ +b相同。而与a+ ++b不同。还有在类似y=x/*z中,程序员要表达的含义是y=/ *z,而C编译器会把它识别成y= /* z。而/*是注释的起始符号。所以这段代码需要打括号 y=/(*z)。
。
再如a+++++b这段代码在逻辑上讲得通的解释方式是a++ + ++b,但是根据贪心法,C编译器会将其理解为(a++)++ +b。而a++不可以作为左操作数。这样在语法上又没有意义,得出的结果自然也是超出预期的。所以在以后的编程中应避免此类结构。
再补充一下,相信大家在编程中一定写过这样的代码:if(x=y)...... 程序员可能要表达的含义是if(x==y)...... 尽管此类错误很明显,但我还是要说一下。因为在某些编译器中并不会报错,代码运行不出结果,而且在无意识中这样的错误就犯下了。笔者建议在自己编写的程序有问题的时候,有意检查一下判断语句中有没有此类错误。
4.结语
当然关于C初学者如何有效避免程序BUG这个话题的讨论还远远没有结束。在今后我会持续更新。欢迎各位小伙伴提出宝贵的意见或建议!