C初级知识(二)
星海
posted @ 2011年7月15日 22:24
in C学习
, 1374 阅读
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 | /* 改变计算机存储单元里的数据或者输入输出操作都算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指向未被识别的第一个字符 // |