注释符号
一个好的注释
好的注释(摘自网络)
- 注释应该准确易懂,防止二义性,错误的注释有害
- 注释是对代码的提示,避免臃肿和喧宾夺主
- 一目了然的代码避免加注释
- 不要用缩写来注释代码,这样可能会产生误解
- 注释用于阐述原因而不是用来描述程序的运行过程
- 不要给用户起奇怪的名字。否则像虾米工程师一样
exp:初探注释规则
#include <stdio.h>
int main()
{
int/*...*/i;//编译器对注释的处理不是简单的删除,而是用空格来代替
char* s = "abcdefgh //hijklmn";//注释符号是不能在双引号之间的
//Is it a \//'\'反斜杠可以当作换行符号
valid comment?
in/*...*/t i;
return 0;
}
结论
- 编译器对注释的处理不是简单的删除,而是用空格来代替
- 注释符号是不能在双引号之间的
- ''反斜杠可以当作换行符号
一个不安宁的反斜杠(接续符号和转义符号)
接续符号:\(反斜杠)
最佳实例
#include\
<stdio.h>
int main(){
printf\
("hello");
}
编译预处理后
int main(){
printf("hello");
}
很明显可以看出来,编译预处理的时候将接续符去掉,将下一行补上来。这种用法常用在宏函数
接续符号的作用
- 编译器会将反斜杠剔除,跟在反斜杠后面的字符自动解到前一行
- 在接续单词时,反斜杠之后不能有空格,反斜杠的下一行之前也不能有空格
- 接续符号适合在宏定义代码块时使用
举个栗子
#include<stdio.h>
void swap(int a,int b)
{
int temp=a;
a=b;
b=temp;
}
//下面就是宏函数
#define SWAP(a,b) \
{ \
int temp=a; \
a=b; \
b=temp; \
}
void main()
{
int a=10,b=20;
SWAP(a,b);
printf("a=%d\nb=%d\n",a,b);
}
转义符号
转义符号用来表示无回显的符号
小结
- C语言中的反斜杠同时具有接续符号和转义符号的作用
- 当反斜杠作为接续符号使用时可直接出现在程序中
- 当反斜杠当转义符号使用时需出现在字符或字符串中
单身狗与双生人(单引号和双引号)
最佳示例
#include<stdio.h>
void main()
{
char * p1=1; //指向内存地址为一的位置(野指针)
char * p2='1'; //指向1的ASCALL码的位置也就是内存中49为 (野指针)
char * p3="1"; //真正指向一字符串的位置
printf('\n');//printf的参数是字符类型的首地址指针,也就是调用\n所在位置的地址的指针
}
从上面的注释和例子可以得到这样几个结论
- 本质上单引号括起来的一个字符代表整数,就是ASCALL码
- 双引号括起来的字符代表一个指针
- C编译器接收字符和字符串的比较,会有warning
- C编译器容许字符串对字符变量赋值,但是没意义
逻辑运算符使用分析
&& 和||
最佳示例
#include<stdio.h>
void main()
{
int i=0;
int j=0;
if(++i>0 || ++j>0)
{
printf("%d\n",i);
printf("%d\n",j);
}
}
输出的结果是1,0
.下面来解释原因
程序的短路规则
- ||从左到右开始计算,当遇到为真的条件时停止计算,整个表达式为真:所有条件为假时才为假
- 这道题很明显的符合这个条件,程序运行完
++i
的时候i>0
成立,那么条件的后半段不会被执行
- 这道题很明显的符合这个条件,程序运行完
- &&从左到右开始计算,当遇到为假的时候停止计算,整个式子为假;所有条件都为假的时候才为假
!
最佳示例
#include<stdio.h>
void main()
{
printf("%d\n",!0);
printf("%d\n",!1);
printf("%d\n",!100);
printf("%d\n",!-1000);
}
答案 1,0,0,0
结论:
C语言中的逻辑“!”只认得“0”,只知道0取非为1,若果不为0,结果都为0
三目运算符
最佳示例
假设有a,b三个变量,如果a>b,那么给a赋值为3。
我脑中第一个闪过的程序是a<b? &a=3:&b=3;
然后emmmmmm,报错,于是,就觉得C语言的三目运算符有点搞头。查了一些blog
才知道,真正的写法是*(a<b? &a:&b)=3;
#include<stdio.h>
void main()
{
int a=0;
int b=2;
int c=1;
*(a<b? &a:&b)=3;
printf("%d\n",a);
printf("%d\n",b);
printf("%d\n",c);
}
总结
不要被其他语言带翻车
位运算符号分析
位运算种类
运算法则(和数学一样)
- 结合律
- 交换律
防错准则
- 避免位运算符号,逻辑运算符号和数学运算符号同时出现在一个表达式中
- 当位运算,逻辑运算符和数学运算符号同时出现的时候最好加括号来表达计算的次序(加减法的优先级高于位运算)
运算小技巧
技巧一
- 左移n位相当于乘以2的n次方,但是效率比数学运算符高
- 右移n位相当于除以2的n次方
最佳示例
使用左移的方法
#include<stdio.h>
#include<time.h>
void main(){
int h,e;
h=clock();
printf("%d\n",256>>4);
printf("%d\n",256>>4);
printf("%d\n",256>>4);
printf("%d\n",256>>4);
printf("%d\n",256>>4);
printf("%d\n",256>>4);
printf("%d\n",256>>4);
printf("%d\n",256>>4);
printf("%d\n",256>>4);
printf("%d\n",256>>4);
e=clock();
printf("%d毫秒\n",e-h);
}
结果113毫秒
使用除法
#include<stdio.h>
#include<time.h>
void main(){
int h,e;
h=clock();
printf("%d\n",256/16);
printf("%d\n",256/16);
printf("%d\n",256/16);
printf("%d\n",256/16);
printf("%d\n",256/16);
printf("%d\n",256/16);
printf("%d\n",256/16);
printf("%d\n",256/16);
printf("%d\n",256/16);
printf("%d\n",256/16);
e=clock();
printf("%d毫秒\n",e-h);
}
结果141毫秒
结论
我这里只用十次除法。虽然看似毫秒只差,但是一旦到项目中运算量大的时候,失之毫秒,差之千秒
,至于原因,是因为计算机是二进制的玩意。它似乎更喜欢处理位运算
技巧二
最佳示例
#include<stdio.h>
#define SWAP1(A,B)\
{ \
int temp=A;\
A=B; \
B=temp; \
}
#define SWAP2(a,b)\
{\
a=a+b;\
b=a-b;\
a=a-b;\
}
#define SWAP3(a,b)\
{\
a=a^b;\
b=a^b;\
a=a^b;\
}
int main()
{
int a=10,b=20;
SWAP1(a,b);
SWAP2(a,b);
SWAP3(a,b);
printf("a=%d,b=%d\n",a,b);
}
一共三种方法进行位置交换
优缺点表
方法 | 优点 | 缺点 |
---|---|---|
SWAP1 | 无论什么类型的都可以 | 效率十分的底下 |
SWAP2 | 效率相对较高 | 容易溢出,效率一般,类型只能是整型 |
SWAP3 | 效率非常高 | 类型只能是整型,推荐使用 |
最佳实战
题目:2,3,5,7,2,2,2,5,3,7,1,1,1找出基数个的数字(也就是1)
思路:用亦或的方法最好,因为偶数个亦或等于零,所以,所有的数字亦或的结果剩下的数字就是我们要找的数字
#include<stdio.h>
#define DIM(a)\
(sizeof(a)/sizeof(*a))
void main()
{
int a[]={2,3,5,7,2,2,2,5,3,7,1,1,1};
int i=0;
int answer=0;
for(i=0;i<DIM(a);i++)
{
answer=a[i]^answer;
}
printf("the result is %d\n",answer);
}
注意
如果移位超过他最大的位数,那么结果为0
,例如:1<<32
的结果为0
移动位数为负数的结果是0
,例如:1<<-1
的结果为0
++,- -操作符号的分析(头疼地带)
int i=3;
(++i)+(++i)+(++i)=?
这里有两种解释:
- ++i是平等的一共有3个,需要加三次,所以是18(3x6)
- 先处理两个,然后再加后一个里面的内容,前两个加完为10,再加一个的话就为16(如果是
Linux
开发是这种,准没错。在gcc
和g++
测试通过)
逗号表达式如何计算
规则:从左算到右顺序求值,最后一个表达式的值就是逗号表达式的值
最佳示例
int x=4;
int k=(++i,i++,i+10);
结果16
,没什么好说的
如何检验程序是否正确?贪心法
- 编译器处理的每个符号应该尽可能多的包含字符
- 编译器以从左到右的顺序一个一个尽可能多的读入字符
- 当即将读入的字符不可能和已读入的字符组成合法的符号为止
分析++i+++i
第一步:++i,到第二步++i+,到第三步++i++,很显然如果用贪心法分析,程序是错的 .事实证明程序确实是错的.
优先级和类型转换分析
C语言中的优先级(常产生的错误)
优先级转化
#include<stdio.h>
#include<malloc.h>
typedef struct _dome{
int * pInt;
float f;
} DOME;
int func(int i,int j)
{
return (i&j !=0);//因为比较符号的优先级高于按位符号所以,这里的输出结果为先判断m!=0然后,再按位与
}
void main()
{
DOME *pD=(DOME*)malloc(sizeof(DOME));
int *p[5];//这里相当于int* p[5];
int *f();//这里相当于 int* f();
int i=0;
i=1,2;//逗号表达式应当加括号
*pD.f=1;//这里相当于pD.f=1后的指针
free(pD);
}
注释里把需要解释的都注释好了,一看就懂
C语言隐式类型转换
隐式优先级表
- 算术运算表达式中,低类型转换为高类型
short c=1;
char b=0;
c+b 的类型?
c+b为short类型
-
赋值表达式中,表达式的值转换为左边变量的类型,例如
short a=1; char b=2; a=b;//这里的b会隐式转化为short类型
函数调用时,实参转化为形式参数的类型
函数返回值,return表达式转化为返回值的类型
最佳示例
#include<stdio.h>
void main()
{
int i=-2;
unsigned int j=1;
if((i+j)>0)//看上面的图片可以知道int类型先变成unsigned类型进行计算
{
printf("i+j>0");
}else
{
printf("<0");
}
printf("%d",i+j);
}