C学习传记
C学习杂记
同一作用域下的同名变量在内存中指向同一空间。
'\0' 字符当作false值处理。
int a[7];
a[1]和 *(a + 1)是一样的
int b[2][5];
b[1][3]和 (*(b + 1))[3]一样的
变量的作用域:
外部变量或函数的作用域从声明它的地方开始,到其所在的(待编译的)文件的末尾结
束。例如,如果main、sp、val、push与pop是依次定义在某个文件中的5个函数或外部
变量,如下所示:
main() { ... }
int sp = 0;
double val[MAXVAL];
void push(double f) { ... }
double pop(void) { ... }
那么,在push与pop这两个函数中不需进行任何声明就可以通过名字访问变量sp与val,
但是,这两个变量名不能用在main 函数中,push与pop 函数也不能用在main 函数中。
另一方面,如果要在外部变量的定义之前使用该变量,或者外部变量的定义与变量的使
用不在同一个源文件中, 必须在相应的变量声明中强制性地使用关键字extern。
将外部变量的声明与定义严格 分开来很重要。变量声明用于说明变量的属性 (主要是
变量的类型),而变量定义除此以外还将引起存储器的分配。
静态变量:
C语言中的static限定包含Java中的private限定和static限定。可限定外部变量,函数。
static也可用于声明内部变量。static类型的内部变量同自动变量一样,是某个特定
函数的局部变量,只能在该函数中使用,但它与自动变量不同的是,不管其所在函数是否被
调用,它一直存在,而不像自动变量那样,随着所在函数的被调用和退出而存在和消失。换
句话说,static类型的内部变量是一种只能在某个特定函数中使用但一直占据存储空 的变
量。
寄存器变量
register 声明告诉编译器,它所声明的变量在程序中使用频率较高。其思想是,将
register 变量放在 器的寄存器中,这样可以使程序更小、执行速度更快。但编译器可以
略此选项。
register声明的形式如下所示:
register int x;
register char c;
register声明只适用于自动变量以及函数的形式参数。下面是后一种情况的例子:
f(register unsigned m, register long n)
{
register int i;
...
}
文件包含
文件包含指令 (即#include指令)使得处理大量的#define指令以及声明更加方便。
在源文件中,任何形如:
#include " 件名"
或
#include < 件名>
的行都将被替换为由文件名指定的文件的内容。如果文件名用引号引起来, 在源文件所在
位置查找该文件;如果在该位置没有找到文件,或者如果文件名是用尖括号<与>括起来的,
将根据相应的规则查找该文件,这个规 同具体的实现有关。被包含的文件 身也可包含
#include指令。
宏替换
宏定义的形式如下:
#define 名字替换 本 (末尾没有分号)
如果对各种类型的参数的处理是一致的, 可以将同一个宏定义应用于任何数据类型,
而无需针对不同的数据类型需要定义不同的max 函数。
可以通过#undef指令取消名字的宏定义,这样做可以保证后续的调用是函数调用,而不
是宏调用:
#undef getchar
int getchar(void) { ... }
形式参数不能用带引号的字符串替换。但是,如果在替换文 中,参数名以#作为前缀
结果将被扩展为由实际参数替换该参数的带引号的字符串。例如,可以将它与字符串连接运
算结合起来编写一个调试打印宏:
#define dprint(expr) printf(#expr " = %g\n", expr)
使用语句
dprint(x/y)
调用该宏时,该宏将被扩展为:
printf("x/y" " = &g\n", x/y);
其中的字符串被连接起来了,这样,该宏调用的效果等价于
printf("x/y = &g\n", x/y);
http://hi.baidu.com/shzhrui/blog/item/2755281751a3aa0c4a90a7bd.html
在用#define 定义时 , 斜杠("\")是用来续行的,
"#"用来把参数转换成字符串,是给参数加上双引号。
"##"则用来连接前后两个参数,把它们变成一个字符串,
"#@"是给参数加上单引号。下面的例子会使您很容易理解。
#define CAT(x,y) x##y /* CAT(1,"abc") => "1abc" */
#define TOCHAR(a) #@a /* TOCHAR(1) => '1' */
#define TOSTRING(x) #x /* TOSTRING(1) => "1" */
指针:
语句(*ip)++中的圆括号是必需的,否 ,该表达式将对ip进行加1 运算,
而不是对ip指向的对象进行加1 运算,这是因为,类似于*和++这样的一元运算符遵循从右
至左的结合顺序。
地址算术运算
如果p 是一个指向数组中某个元素的指针,那么p++将对p进行自增运算并指向下一个
元素,而p+=i将对p进行加i 的增量运算,使其指向指针p 当前所指向的元素之后的第i
个元素。 而不是使p指向下一个字节。
C 语言保证,0 永远不是有效的数据地址
指针与整数之 不能相互转换,但0 是惟一的例外:常量0 可以赋值给指针,指针也可
以和常量0 进行比较。程序中经常用符号常量NULL代替常量0,这样便于更清晰地说明常量
0 是指针的一个特殊值。符号常量NULL定义在标准头文件<stddef.h>中。
指针可以和整数进行相加或相减运算。例如,结构
p + n
表示指针p 当前指向的对象之后第n个对象的地址。无论指针p指向的对象是何种类型,上
述结论都成立。在计算p+n 时,n将根据p指向的对象的长度按比例缩放,而p指向的对象
的长度 取决于p 的声明。例如,如果int类型占4 个字节的存储空 ,那么在int类型的
计算中,对应的n将按4 的倍数来计算。
指针的减法运算也是有意义的:如果p和q指向相同数组中的元索,且p<q,那么q-p+1
就是位于p和q指向的元索之 的元素的数目。
UNIX 系统中的C 语言程序有一个公共的约定:以 号开头的参数表示一个可选标志或参
数。假定用-x (代表“除……之外”)表示打印所有与模式不匹配的文 行,用-n (代表“行
号”)表示打印行号,那么下列命令:
find -x -n 模式
将打印所有与模式不匹配的行,并在每个打印行的前面加上行号。
可选参数应该允许以任意次序出现,同时,程序的其余部分应该与命令行中参数的数目
无关。
在所有运算符中,下面4 个运算符的优先级最高:结构运算符“.”和“->”、用于函数
调用的“()”以及用于下标的“[]”,因此,它们同操作数之 的结合也最紧密。
千万不要认为结构的长度等于各成员长度的和。因为不同的对象有不同的对齐要
求,所以,结构中可能会出现未命名的“空穴“(hole)。例如,假设char类型占用一个字节,
int类型占用4 个字节, 下列结构:
struct {
char c;
int i;
};
可能需要8 个字节的存储空 ,而不是5 个字节。使用sizeof运算符可以返回正确的对象
长度。
同一作用域下的同名变量在内存中指向同一空间。
'\0' 字符当作false值处理。
int a[7];
a[1]和 *(a + 1)是一样的
int b[2][5];
b[1][3]和 (*(b + 1))[3]一样的
变量的作用域:
外部变量或函数的作用域从声明它的地方开始,到其所在的(待编译的)文件的末尾结
束。例如,如果main、sp、val、push与pop是依次定义在某个文件中的5个函数或外部
变量,如下所示:
main() { ... }
int sp = 0;
double val[MAXVAL];
void push(double f) { ... }
double pop(void) { ... }
那么,在push与pop这两个函数中不需进行任何声明就可以通过名字访问变量sp与val,
但是,这两个变量名不能用在main 函数中,push与pop 函数也不能用在main 函数中。
另一方面,如果要在外部变量的定义之前使用该变量,或者外部变量的定义与变量的使
用不在同一个源文件中, 必须在相应的变量声明中强制性地使用关键字extern。
将外部变量的声明与定义严格 分开来很重要。变量声明用于说明变量的属性 (主要是
变量的类型),而变量定义除此以外还将引起存储器的分配。
静态变量:
C语言中的static限定包含Java中的private限定和static限定。可限定外部变量,函数。
static也可用于声明内部变量。static类型的内部变量同自动变量一样,是某个特定
函数的局部变量,只能在该函数中使用,但它与自动变量不同的是,不管其所在函数是否被
调用,它一直存在,而不像自动变量那样,随着所在函数的被调用和退出而存在和消失。换
句话说,static类型的内部变量是一种只能在某个特定函数中使用但一直占据存储空 的变
量。
寄存器变量
register 声明告诉编译器,它所声明的变量在程序中使用频率较高。其思想是,将
register 变量放在 器的寄存器中,这样可以使程序更小、执行速度更快。但编译器可以
略此选项。
register声明的形式如下所示:
register int x;
register char c;
register声明只适用于自动变量以及函数的形式参数。下面是后一种情况的例子:
f(register unsigned m, register long n)
{
register int i;
...
}
文件包含
文件包含指令 (即#include指令)使得处理大量的#define指令以及声明更加方便。
在源文件中,任何形如:
#include " 件名"
或
#include < 件名>
的行都将被替换为由文件名指定的文件的内容。如果文件名用引号引起来, 在源文件所在
位置查找该文件;如果在该位置没有找到文件,或者如果文件名是用尖括号<与>括起来的,
将根据相应的规则查找该文件,这个规 同具体的实现有关。被包含的文件 身也可包含
#include指令。
宏替换
宏定义的形式如下:
#define 名字替换 本 (末尾没有分号)
如果对各种类型的参数的处理是一致的, 可以将同一个宏定义应用于任何数据类型,
而无需针对不同的数据类型需要定义不同的max 函数。
可以通过#undef指令取消名字的宏定义,这样做可以保证后续的调用是函数调用,而不
是宏调用:
#undef getchar
int getchar(void) { ... }
形式参数不能用带引号的字符串替换。但是,如果在替换文 中,参数名以#作为前缀
结果将被扩展为由实际参数替换该参数的带引号的字符串。例如,可以将它与字符串连接运
算结合起来编写一个调试打印宏:
#define dprint(expr) printf(#expr " = %g\n", expr)
使用语句
dprint(x/y)
调用该宏时,该宏将被扩展为:
printf("x/y" " = &g\n", x/y);
其中的字符串被连接起来了,这样,该宏调用的效果等价于
printf("x/y = &g\n", x/y);
http://hi.baidu.com/shzhrui/blog/item/2755281751a3aa0c4a90a7bd.html
在用#define 定义时 , 斜杠("\")是用来续行的,
"#"用来把参数转换成字符串,是给参数加上双引号。
"##"则用来连接前后两个参数,把它们变成一个字符串,
"#@"是给参数加上单引号。下面的例子会使您很容易理解。
#define CAT(x,y) x##y /* CAT(1,"abc") => "1abc" */
#define TOCHAR(a) #@a /* TOCHAR(1) => '1' */
#define TOSTRING(x) #x /* TOSTRING(1) => "1" */
指针:
语句(*ip)++中的圆括号是必需的,否 ,该表达式将对ip进行加1 运算,
而不是对ip指向的对象进行加1 运算,这是因为,类似于*和++这样的一元运算符遵循从右
至左的结合顺序。
地址算术运算
如果p 是一个指向数组中某个元素的指针,那么p++将对p进行自增运算并指向下一个
元素,而p+=i将对p进行加i 的增量运算,使其指向指针p 当前所指向的元素之后的第i
个元素。 而不是使p指向下一个字节。
C 语言保证,0 永远不是有效的数据地址
指针与整数之 不能相互转换,但0 是惟一的例外:常量0 可以赋值给指针,指针也可
以和常量0 进行比较。程序中经常用符号常量NULL代替常量0,这样便于更清晰地说明常量
0 是指针的一个特殊值。符号常量NULL定义在标准头文件<stddef.h>中。
指针可以和整数进行相加或相减运算。例如,结构
p + n
表示指针p 当前指向的对象之后第n个对象的地址。无论指针p指向的对象是何种类型,上
述结论都成立。在计算p+n 时,n将根据p指向的对象的长度按比例缩放,而p指向的对象
的长度 取决于p 的声明。例如,如果int类型占4 个字节的存储空 ,那么在int类型的
计算中,对应的n将按4 的倍数来计算。
指针的减法运算也是有意义的:如果p和q指向相同数组中的元索,且p<q,那么q-p+1
就是位于p和q指向的元索之 的元素的数目。
UNIX 系统中的C 语言程序有一个公共的约定:以 号开头的参数表示一个可选标志或参
数。假定用-x (代表“除……之外”)表示打印所有与模式不匹配的文 行,用-n (代表“行
号”)表示打印行号,那么下列命令:
find -x -n 模式
将打印所有与模式不匹配的行,并在每个打印行的前面加上行号。
可选参数应该允许以任意次序出现,同时,程序的其余部分应该与命令行中参数的数目
无关。
在所有运算符中,下面4 个运算符的优先级最高:结构运算符“.”和“->”、用于函数
调用的“()”以及用于下标的“[]”,因此,它们同操作数之 的结合也最紧密。
千万不要认为结构的长度等于各成员长度的和。因为不同的对象有不同的对齐要
求,所以,结构中可能会出现未命名的“空穴“(hole)。例如,假设char类型占用一个字节,
int类型占用4 个字节, 下列结构:
struct {
char c;
int i;
};
可能需要8 个字节的存储空 ,而不是5 个字节。使用sizeof运算符可以返回正确的对象
长度。