C初级知识(二)
星海
posted @ 2011年7月15日 22:24
in C学习
, 1359 阅读
/* 改变计算机存储单元里的数据或者输入输出操作都算Side Effect Implementation - defined: C标准没有明确定义,但要求编译器必须对此作出明确规定 unspecified:编译器自己可以决定,不必写在编译器文档中。 undefined:完全不缺定。C标准和编译器可能都没又规定怎么处理,甚至没有作出错误检查。例如数组访问越界。 Usual Arithmetic Conversion 如果一边是无符号数,另一边是有符号数。如果无符号数的Rank不低于有符号数,则把有符号数转换成另一边的无符号类型。 如果无符号数的Rank低于有符号数的Rank。如果有符号数类型能够覆盖这个无符号数类型的取值范围,则将无符号数类型转换成另一边的有符号类型。否则,也就是有符号数类型不足以覆盖这个无符号数类型,则将两边都转换成有符号数的Rank对应的无符号数类型。例如32位平台上的unsigned int和long做算数运算,将两边都转换成unsigned long。 如果一个表达式隐含着多个Side Effect,发生顺序未确定。 Sequence Point: , && || : ? 的第一个操作数求值之后是Sequence Point。 在一个完整的声明末尾是Sequence Point。int a[10], b[20]; a[10]的末尾是,然后是b[20]。 运算优先级: 1,标识符,常量,() 2,后缀运算符,包括[],函数调用(),结构体取成员.,->, 后缀++, 后缀--, (从左到右依次计算) a.name++,先求a.name 3,单目运算符,包括前缀++,前缀--,sizeof,类型转换(),取址&,指针取值*,按位取反~,逻辑非!。如果一个操作数有多个前缀,则从右到左依次计算。例如~!a,先计算!a。 4, + - * / % 从左到右。 5,移位运算符 << , >> 6,关系运算符 > < <= >= 7,相等运算符 == , != 8,按位与 & 9,按位异或 ^ 10,按位或 | 11,逻辑与 && 12,逻辑或 || 13,条件运算符 14,条件 : ? 15,赋值 = 和各种复合赋值 *= , -= , /= , |= 。在双目运算符中只有这种是右结合的。 16,逗号运算符。 */ #include <stdio.h> const int A = 10; /* .rodata段,只读变量 */ int a = 20; /* data段,global符号 */ static int b = 30; /* data段,local符号 */ /* 静态变量未初始化,则放在.bss段,初始化为0。 static 变量未初始化,也放在.bss段 .bss段在文件中不占用存储空间,在加载时这个段用0填充*/ int c; int main ( int argc, char *argv[] ) { static int a = 40; /* data段 */ char b[] = "hello world"; register int c = 50; printf("Hello World %d\n", c); /* .rodata段,只读变量 字符串字面值 */ return 0; } /* * 作用域(Scope): * 1,函数作用域(Function Scope), 只有语句标号属于函数作用域 * 2,文件作用域(File Scope),全局变量,main,全局中的static * 3,函数原型作用域(Function Prototype Scope) */ /* * 标识符链接属性(Linkage) * 外部链接(External Linkage) * 内部链接(Internal Linkage) 全局变量,全局中的static变量 * 无链接(No Linkage) * Previous Linkage extern */ /* * 链接库 * * * 打包成静态库 * ar rs libxxx.a aaa.o bbb.o ccc.o * 以上命令等价于ar r lib.............. && ranlib libxxx.a * * gcc main.c -L. -lstatck -Istack -o main * -L.告诉编译器在当前目录找库文件 * -lstatck告诉编辑器要链接libstack库,-I选项告诉编译器去哪个目录找头文件 * -static告诉编译器只链接静态库。链接器在链接静态库时,会把静态库中的目标文件取出来和可执行文件真正链接在一起。链接器可以从静态库中只取出需要的部份来做链接。 * * * 共享库在编译时要加-fPIC选项,PIC表示生成位置无关代码(Position Independent Code)。 * * 程序运行时,要注意共享库位置。 * 共享库搜索路径顺序: * 1,环境变量 LD_LIBRARY_PATH * 2,缓存文件/etc/ld.so.cache。这个缓存文件是ldconfig读取配置文件/etc/ld.so.conf之后生成的 * 3,默认的系统路径 /usr/lib /lib * * * * 共享库的命名惯例:real name, soname 和 linker name * real name包含完整的共享库版本。例如 libcap.so.1.10 * soname是一个符号链接的名字,只包含共享库的主版本号。主版本号一致即可保证接口一致。例如libcap.so.1 * linker name只在编译链接时使用。有的link name是一个符号链接的名字,有的是一个符号链接 * * gcc -shared -WL,-soname,libstatck.so.1 -o libstack.so.1.0 aaa.o bbb.o * -WL之后是GCC传递给链接器的选项。本例中 * libstack.so.1.0是realname,libstack.so.1是soname */ /* 1,虚拟内存管理控制物理内存的访问权限 * 2,最主要的作用是让每个进程都有独立的地址空间。是指 * 不同进程中的同一个VA被MMC映射到不同的PA,并且某一个进程中访问任何地址都不能访问到另外一个进程的数据。 * 3,VA到PA的映射会给分配和释放内存带来方便。不连续的PA可映射为连续的VA。 */ /* define * 如果一个程序文件中重复定义一个宏,C语言规定这些重复的宏定义必须一模一样 * * #运算符用于创建一个字符串,#后应该跟一个形参 * 注意,实参中如果包含字符串或字符常量,则宏展开之后,字符串的界定符" * 要替换成\",其中的\要替换成\\ * * 宏定义中运算符##把前后两个预处理Token连接成一个Token。根据定义,##不能出现在宏定义开头或结尾 * __VA_ARGS__ * gcc有一种扩展语法,如果 * ##运算符用在__VA_ARGS__前面,当__VA_ARGS__是空参数时,##前面的,被吃掉了 */ #define sh(x) printf("n" #x "=%d, or %d\n", n##x,alt[x]) #define sub_z 26 sh(sub_z); /* 展开后为 printf("nsub_z = %d, or %d\n",nsub_z, alt[26] */ /* #pragma预处理指示供编译器实现一些非标准的特性。 * __FILE__展开为当前源文件名的字符串, * __LINE__展开为当前代码行的行号。 * GCC引进了一个特殊的标识符 * __func__,是一个变量名而不是标识名,不属于预处理。当前函数名的字符串。 */ /* * Makefile * * 如果make执行的命令前加了@命令,则不显示命令本身而只显示它的结果。命令如果出错,就立刻终止。 * 如果命令前加了-号,即使这条命令出错,make也会继续执行后续命令。 * * 把clean声明为一个伪目标。 .PHONY: clean * make隐含规则(Implicit Rule) * #号表示单行注释, * $(CC)取变量CC的值, * $@ 的取值为规则中的目标, * $< 为规则中的第一个条件, * $? 为规则中所有比目标新的条件,组成一个列表,以空格分隔。 * $^ 表示规则中的所有条件。 * 用=号定义变量的延迟展开特性,有可能写出无限递归的定义,例如 CFLAGS = * $(CFLAGS) -O。如果希望make遇到变量定义时立即展开,可以用:=运算符。 * 如果要定义一个变量的值是空格,可以 NULLSTR := SPACE := $(NULLSTR) # 注释前面有个空格。 */ /* foo ?= $(bar)的意思是,如果foo没有定义过, foo = * $(bar),如果foo已经定义,则什么也不做 * * += 运算符可以给变量追加值。+=根据前面是=还是:=再展开。 * 例如: * objects := main.o * objects += $(foo) * 前面是 :=,则 如果foo没有定义,则 objects = * main.o。如果前面foo已经定义,则objects = main.o foo的值 * */ /* 数组名做右值时自动转换成指向首元素的指针 * 指向const变量的指针或者const变量的地址不可以传给指向非const变量的指针。 * 如果要定义一个指针指向字符串字面值,这个指针应该是const char * 类型。 * 错误例子: * char *p = "abcd"; * *p = 'A'; * p指向 .rodata段,不允许改写,但编译器不会报错,在运行时会出现段错误 */ int a[10]; int (*pa)[10] = &a; /* &a[0]表示数组a的首元素的首地址,而&a表示数组a的首地址。数值相同,类型不同。 */ typedef int F(void); /* 定义函数类型F */ F f, g; /* 正确,int f(void) */ //F h(void); /* 错误,不能返回函数类型 */ F *e(void); /* 正确,返回一个F*类型的函数指针 */ /* 传入参数,传出参数,Value-result参数 */ /* stdarg.h的一种实现。 * 原理:可变参数中的参数从右向左依次压栈,第一个参数靠近栈顶,最后一个参数在栈底。对齐!! */ #ifndef _STDARG #define _STDARG typedef char *va_list; #define va_arg(ap,T) \ (* (T*)(((ap) += _Bnd(T, 3U)) - _Bnd(T, 3U))) #define va_end(ap) (void)0 #define va_start(ap, A) \ (void)((ap) = (char *)&(A) + _Bnd(A, 3U)) /*_Bnd使之对齐,注意后面的运算符优先级,是 ((sizeof(x) + (bnd)) & (~(bnd)) */ #define _Bnd(X, bnd) (sizeof(X) + (bnd) & ~(bnd)) #endif void *memset(void *s, int c, size_t n); void *memcpy(void *dest, const void *src, size_t n); void *memmove(void *dest, const void *src, size_t n); /* memmove可以拷贝两个重叠的内存区域。*/ char *strtok(char *str, const char *delim); char *strtok_r(char *str, const char *delim, char **saveptr); /* 可重入posix ,saveptr是个 Value-result参数*/ // "r" 只读,文件必须存在 // "w" 只写,文件不存在就创建,截0 // "a" 追加,不存在就创建 // "r+" 读写,文件必须存在 // "w+" "a+",读写,文件不必存在 // //从终端设备输入时有两种方法,可以表示文件结束。一种是开头输入EOF或行中输入两次EOF,一种是用Shell的Heredoc语法。 ./a.out << endif // printf %a.b // a为宽度,格式化后的最小长度。b为精度,对于字符串来说指定了格式化后的最大长度,对于浮点数来说指定了格式化后小数点右边的位数,对于整数来说指定了格式化后的最小位数。 // // long int strol(const char *nptr, char **endptr, int base); double strtod(const char*nptr, char **endptr, int base); // endptr指向未被识别的第一个字符 //