下面的内容主要是个人的学习笔记,以便日后复习使用,内容比较简单,大牛们可以略过了,初学者可以互相交流。
1.关于/* */注释符的使用
注释符不能嵌套,在一段代码中,第一个/*和第一个*/直接的所有内容都是注释,包括注释符/*。所以假如有一段代码:
int a;
/*
int b;
*/
int c;
我们想注释这段代码,于是在int a;和int c;之前之后分别加上/* */,会发现注释是错的。解决办法可以用预处理指令#if,改为:
#if 0
...
#endif
另外和注释符相关的一个问题是运算符问题,int a=b/*p;本意是b/(*p),结果由于词法分析的贪心原则,导致/*结合为注释符,因此在编程时候需要注意下。上述这两个问题在编辑器里面写错了都可以直接看出来,就算不用注意也不会导致错误的发生,除非编辑器的字体没有颜色标识。
2.在使用函数getchar() getc() fgetc()时候,接收返回值的变量应该声明为int,即int a=getchar();因为getchar()的返回值为int类型,且结束符EOF也是值为-1的int类型,使用char类型变量来接收返回结果时,如果编译器没有做处理容易出现错误。
3.关于指针的const声明
int const *p;//p是指向整型常量的指针,可以修改p的值,不能修改p指向内容的值
int * const p;//p是一个常量指针,可以修改p指向内容的值,不能修改p的指向
int const * const p;//这种声明也是正确的,效果参考上述
4.关于static的声明
当static用于修饰代码块之外的变量或者函数时,static用于修改标识符的链接属性,由external改为internal,被修饰标识符只能在其声明的源文件中被访问。
当static用于修饰代码块内部的变量时,static修改的是标识符的存储类型,为静态存储,不影响变量的链接属性和作用域。用这种方式声明的变量在程序执行之前创建,在程序的执行期间一直存在。
5.sizeof 的一个问题,sizeof是判断表达式的长度,因此并不需要对表达式求值,因此sizeof(a=b+1)并没有对a赋值。
6.关于运算符的求值顺序
a*b+c*d+e*f 在这个表达式中,编译器只保证乘法操作在其相邻的加法操作之前进行,并不保证先算a*b还是先算c*d,因此c+(--c)这样的表达式很容易出现错误。
另外,在表达式f()+g()+k()中,编译器只保证左边的加号先于右边的加号执行,但并不能保证三个函数的调用顺序,因此如果三个函数间存在数据耦合,那么这样的书写方式是存在风险的。
7.关于移位符的一个应用 点击打开链接
8.C中函数的可变参数列表实现方法点击打开链接
9.strlen()返回的是size_t类型的值,它是一个无符号整数类型,因此表达式
if( strlen(x) - strlen(y) >=0) ...中,if的判断值始终为真,因为两个无符号整数做运算结果还是无符号整数,不会为负数
10.char *strncpy(char *dst ,char *src, size_t len),该函数总是正好向dst写入len个字符,src长度如果不够len,就用NUL填充,如果大于等于len,就写入len个字符,但是不会在复制结尾插入NUL。因此在程序中可以在len-1处赋值'\0'。使用此函数时要注意确定好len的值,不要越界。
char *strncat(char *dst ,char *src, size_t len),该函数至多向dst添加len个字符并在结尾添加'\0',如果超出dst的剩余空间了仍会在dst尾部添加字符,产生越界,因此len的大小也需要确定好。
11.字符串查找函数:
char *strchr(char const *str, int ch);//从左向右
char *strrchr(char const *str, int ch);//从右向左
char *strpbrk(char const *str, char const *group);//匹配组中任何一个
char *strstr(char const *str1, char const *str2);//如果str2为空字符串,则返回str1
size_t strspn(char const *str, char const *group);//返回str开头部分属于group的元素个数,即返回第一个不在group中的元素的下标
size_t strcspn(char const *str, char const *group);//返回str开头部分不属于group的元素个数
char *strtok(char *str, char const *sep);//根据sep分隔str,会修改str
12.如果要初始化union类型变量,初始值必须是union变量的第一个成员的类型。struct类型变量作为函数参数使用时大多数情况下传递一个struct类型的指针更为合适,因为C的函数参数是传值的,如果传递一个struct类型的变量,那么需要把整个变量拷贝到栈中,效率很低。
13.所有用于对数值表达式进行求值的宏定义都应该加上括号,避免在使用宏时,由于参数中操作符之间产生不可预料的相互作用。例如 #define DOUBLE(x) ((x) + (x))
14.条件编译,可以用来调试程序。在程序中如果有如下语句:
#if expr
statements
#endif
expr是常量表达式,一般通过#define 定义,该表达式的值在预处理时进行求值,如果为真,则statements部分被编译,否则被忽略。