在c语言中,所谓左值指的是存储位置,而右值就是指值。例如:
a = b + 5;
其中, a是个左值,因为他标识了一个可以存储结果值的地点(相当于门牌号),而b + 5 是一个右值,他是一个值(相当于屋内的东西)。
结论:凡是可以清晰表示地址概念的都可以作为左值,凡是可以清晰表示值的概念的,都可以作为右值。
c语言可通过stdarg.h中定义的方法实现可变参数列表,这个头文件声明了一个va_list的类型和三个宏操作:
(1)va_start:初始化va_list,将va_list变量设置为指向可变参数部分的第一个参数。
(2)va_arg:访问指定参数的值,并且使指针指向下一个可变参数。
(3)va_end:访问结束。在访问最后一个参数后,调用该函数,可变参数访问结束。
(4)va_list:用于访问参数列表的未确定部分
数组名和指针都具有指针值,都可以进行间接访问和数组下标访问,但是,声明数组和指针时,其内存状态存在很大的不同,这一点需要重点理解。比如下面的例子:
int a[5]
int *b
声明a时,编译器首先为数组a开辟5个int大小的内存空间,然后创建数组名a为常量,并将其指向这段空间的起始内存地址;而声明指针b时,则只是为指针变量本身开辟一个内存空间,其值可能不会被初始化,不会指向任何有意义的地址空间;但是b++合法,b不合法,而a++不合法(a是常量),a合法。
int matrix[3][10];
int (*p)[10] = matrix;
p是一个指针,是一个指向拥有10个整型元素的数组的指针。
对于多维数组做函数参数的情况来讲,要特别注意其函数原型的声明形式。
func(int (*p)[10]);
func(int matrix[][10]);
以上两个参数的语义是一样的,都是指:指向包含10个整型元素的数组的指针,其中,一维以后的数组长度在形参中不可省略;而不能将二维数组的形参声明为func(int **p),该参数的语义是声明一个指向整型指针的指针,与指向数组的指针不是一个概念,要特别注意。
(1)计算字符串长度函数--strlen
strlen函数的返回值为size_t,其在stddef.h定义为无符号整数,因此使用时可能会出现意想不到的结果,比如无符号整数和0比较,将不会得到正确的结果,因此在使用其返回值时要将其强制转换为int类型。
(2)不受限制的字符串函数--strcpy\strcat\strcmp
这些函数在使用时不会自动检查目标数组的长度,因此程序员在使用时必须时刻牢记检查目标数组的长度,以免越界产生不可预想的错误。
(3)长度受限的字符串函数--strncpy\strncat\strncmp
strncpy:若长度小于dst数组长度,剩余的自动补NUL字符;若长度大于或等于dst长度,此时结果不会以NUL字符结尾。
strncat:该函数总是在结果字符串后面添加一个NUL字符。
(4)字符串分割函数--strtok
strtok可以将一个字符数组根据不同的分隔符分割成不同的字符子串,第一个参数为NULL时,表示要查找下一个分割符。但是有一点需要注意,strtok会修改原来的字符数组。
内存操作函数对应于字符串操作函数,只是内存操作函数不对NUL字符进行识别。
主要包括:memcpy、memmove、memcmp、memchr、memset
双链表的优势:
(1)可以以任何方向或忽前忽后的遍历链表
(2)允许从任何一端开始遍历
在c语言中,字符串数组的存储实现只有两种方式:
(1)字符指针数组,即char buffer[10] = {"123","abc",...},该种方式一般只用于存储已知的常量字符串,不用于程序运行时赋值的情况。
(2)字符型二维数组,即char[][10],该方式可用于运行时字符串的赋值,注意:可以使用char (p)[10]指针按行访问二维数组,效果就是用指针p来分别访问每个字符串。
(1)int f; //一个整型变量
(2)int *f; //一个指向整型变量的指针
(3)int *f,g; //f是指针,g是普通变量
(4)int f(); //函数声明,返回值为int
(5) int *f(); //函数声明,返回值为int型指针
(6)int (*f)(); //函数指针,指向函数调用起始点在内存中的位置
(7) int* (*f)(); //函数指针,函数返回值为int*
(8)int f[]; //数组声明
(9)int *f[]; //指针数组声明
(10)int f()[]; //非法声明,因为函数只能返回标量,不能返回数组
(11)int f[](); //非法声明,函数数组非法,因为函数代码长度不一,不能存于数组中
(12)int (*f[])(); //函数指针数组
(13)int *(*f[])(); //函数指针数组,函数返回值为int*类型
(14)int (*f)(int, float); //ANSI C标准中的函数指针声明,加入参数使声明更加明确
(15)int *(*g[])(int, float); //函数指针数组
注意:函数指针在使用时,一定要先声明函数原型,然后将函数指针初始化(使其指向一个具体函数),之后便可以向函数名一样使用该函数指针。
解引用,其实就是解除引用,关于它的类型,可以根据声明一步步上推。例如:
对于char a; 解引用 *a的类型就是char;
对于char b;解引用 b的类型就是char ,也就是一个字符串,而 b的类型则为char,是一个字符。 十七、字符串常量的进阶理解
实质上,字符串常量出现在表达式中的时候,实际上是一个char型指针。比如:
(1)"xyz" + 1 => y
(2) "xyz"[2] => z
(3) "0123456789ABCDEF"[value % 16] => 二进制转换为字符,并以16进制打印
十八、预处理指令理解一个概念:什么是宏?允许把参数替换到文本中,这种实现通常称为宏或定义宏。
(1)#define:两种形式
a. #define name stuff:stuff可以是任何文本,不局限于整型常量
b. #define name(parameter-list) stuff: 宏
(2)宏和函数的优劣比较
a. 函数的调用和返回需要一定的开销(函数调用栈),而宏只是替换,没有函数栈的变化