c语言仅为开发unix而生 为高效而生
c++为面向对象语言而生 兼容已有的代码
系统一般由多种语言写成的
数据类型和变量:
数据类型是一个模子,操作变量就是操作空间
char short int
typedef用法:
typedef int INT32
typedef unsigned char BYTE
typedef struct _demo
{
short s;
BYTE b1;
BYTE b2;
INT32 i;
}DEMO;
属性关键词:
变量定义前可以加属性关键词
auto:
变量存储在程序的栈里 变量的值是变的
局部变量默认auto类型
static:
局部变量存储于程序的静态区
文件作用域标识符
修饰的全局变量作用域只在声明的文件当中
修饰的函数作用域只在声明的文件当中
register:
指明变量存储于寄存器里
只是请求寄存器变量 但不一定请求成功
register变量必须是CPU寄存器可以接受的值
不能用&获取register变量的地址
用在实时性能要求比较高的系统里
只会被初始化一次
语句用法:
if - else:(两路分支)
if的条件是逻辑上的值
bool型变量不能直接在条件里面进行比较
普通变量和0比较 0值应该出现在比较符号左边(避免BUG 防止写成赋值符号)
float不能直接进行0值比较 需要定义精度
switch:(多路分支)
case语句分支必须有break 否则会导致分支重叠
default语句有必要加上 以处理特殊情况
case 语句中的值只能是整型或字符型
顺序分析:
按照字母或者数字排列各条语句
正常情况放前边 异常情况放后面
default 语句只能用于处理真正的默认情况
if用于范围判断比较适合
循环语句:
do while for
do - while 至少执行一次
用do - while(0)在只执行一次的场合(申请内存 释放内存 避免内存泄漏)
使用while时一定要注意条件是什么
break 表示终止循环的执行 跳出块
continue 表示中值本次循环体 进入下次循环执行
关键字分析:
goto:
禁用goto 打入冷宫
void:
修饰函数的返回值和入口参数
不能定义变量类型
只有相同类型的指针才能相互赋值 void指针可以作为左值用于接收任意类型的指针
用void* 作为函数的入口类型可以接收任意类型的指针参数
extern:
声明外部定义的变量和函数
用于告诉编译器c方式编译 extern"C"
sizeof正名:
编译器内置的指示符 不是函数
计算相应的实体所占的内存大小
其值在编译期就已经知道结果了
sizeof int; 是不允许的
sizeof (int); 是允许的
所以统一写成了sizeof(); 被冤枉成为一个函数了
const:
const只对编译器有用 运行时无用
变量会占用内存空间 用指针操作地址还是可以改变值的 const类型的数组的值也会被改变
const修饰的变量是只读的 本质还是变量
const修饰指针:
const int* p;
int const* p;
int* const p;
const int* const p;
左数右指 不变
当const出现在*号左边时指针指向的数据为常量
当const出现在*号后右边时指针本身为常量
const修饰函数参数表示在函数体内不希望改变参数的值
const修饰函数返回值表示返回值不可变 多用于返回指针的情形
volatile:
编译器警告指示字
告诉编译器必须每次去内存中取变量值 不做优化
修饰可能被多个线程访问的变量
修饰可能被未知因数更改的变量
enum:
enum是一种自定义类型
enum默认常量在前一个值的基础上依次加1 第一个值默认为零
enum类型的变量只能取定义时的离散值
define宏只是简单的替换 无法被调试 是一种特定类型的常量
typedef:
给一个已经存在的数据类型重命名
没有产生新的类型
不能进行signed unsigned进行拓展
define只是字符串的替换 无别名的概念
符号的技巧:
注释:
编译器在编译的时候 会将注释删除同时用空格替代
编译器认为双引号括起来的内容都是字符串 双斜杠也不例外
/*......*/ 型的注释不能被嵌套
/*作为一段注释的开始 直到遇见*/
准确易懂简洁 提示准确 阐述原因
接续符和转义符:
接续符 \:
指示编译器行为的利器 继续下一行的内容
注意加空格
接续单词的时候注意不能加空格
define必须在一行内结束 用接续符实现定义代码块
转义字符 \:
表示无回显字符
\n 换行
\t tab
出现在字符或者字符串当中
单引号&双引号:
单引号 表示字符常量 代表整数
'a' 一字节
'a' + 1 = 'b'
双引号 表示字符串常量 表示一个指针
"a" 二字节
"a" + 1 = '\0' (指针操作)
不能将字符 和 字符串 赋值 和 比较
逻辑运算符:
短路规则:
||从左到右计算 为真停止
&&从左到右计算 为假停止
! :
"!" 只认得0 见到0 返回1
值不为0时 返回0
三目运算符:
逻辑判断之后 返回一个值 不是变量
位运算符:
避免位运算符 逻辑运算符 数学运算符同时出现在一个表达式里
尽量用()表达次序
左移n位 相当于乘于 2^n
右移n位 相当于除于 2^n
a ^ a = a;
操作符分析:
++:
i = 3; (++i) + (++i) + (++i) = 16 / 18;
i = 3; k = (++i, i++, i+10) = 15 //逗号表达式 从左到右计算 最后一个表达式的值
i +++ 1
编译器从左到右尽可能多的包含字符
优先级和类型转换:
----------------------------------------------------------
常见问题 | 表达式
----------------------------------------------------------
.的优先级高于* ->可以消除这个问题 | *p.f
[]的优先级高于* | int *app[]
函数()高于* | int *fp()
== != 优先级高于 位操作 | (val & mask != 0)
== != 优先级高于 赋值符 | c = getchar() != EOF
算术运算符高于位移运算符 | mab << 4 + lsb
逗号运算符在所有运算符优先级最低 | i = 1, 2;
----------------------------------------------------------
C语言的隐式类型转换:
低类型向高类型转换
实参向形参转换
赋值右边向赋值左边转换
return 表达式转换为返回值类型
char -> int -> unsigned int -> long -> unsigned long -> double
|
short -> float编译预处理
编译预处理:
编译过程:
预处理器 - 编译器 - 汇编器 - 链接器
gcc -E test.c -o test.i
gcc -S test.i -o test.s
gcc -C test.s -o test.o
单步编译技术 排除错误
链接器的工作就是把各个独立的模块链接为可执行程序
静态链接在编译器完成 动态连接在运行时完成
宏定义使用分析:
#define 从本行开始之后的代码可以使用这个宏常量
宏表达式像函数 却不是函数 比函数强大 容易出错
宏表达式只是简单的文本替换 有副作用
不要在宏参数里面写表达式
可以只充当一个语句 扩展C语言的用法
编译器不知道宏表达式的存在
用实参代替形参不进行任何计算 实参可以是只是数据类型
宏表达式没有调用开销
宏表达式不能出现递归的定义
#undef结束#define定义域
功能强大的内置宏
__FILE__ :打印文件名
__LINE__ : 不能放在函数里面来调用 可以使用宏代码块
日志宏 : 打印时间 包含<time.h>
宏代码块 多条语句 多行: do{} while(0) + 续行符
条件编译使用分析:
#if
#else
#endif :
对编译器起作用 执行时不起作用
gcc -DC=1 -E test.c -0 test.i //命令行时 预编译时指明C=1
#include:
将已经存在的文件内容嵌入到当前文件夹
间接包含同样会产生嵌入文件内容的动作
所以用到
#ifndef
#define
#endif
条件编译技术 只包含一次 防止多次展开 产生重定义的情况
应用:
不同的产品线用同一份代码
区分编译产品的调试版和发布版
#error 和 #line:
#error 编译指示字:
生成一个编译错误信息 停止编译
#error message 无需双引号
#warning message 生 成编译警告 不会停止编译
#line 编译指示字:
用于强制指定新的行号和编译文件名 并对源程序的代码重新编号
#line number filename filename可以省略
#pragma:
编译器指示字 用于指示编译器完成一些特定的动作
指示字是编译器和操作系统特有的
在不同的编译器之间是不可移植的
编译器将忽略不认识的指令
不同的编译器将以两种不同的方式解释同一条pragma指令
#pragma message
message 参数在大多数的编译器中有相似的功能
message 参数在编译时输出信息到编译输出窗口中
message 可用于代码的版本控制
知道编译的代码是不是自己想要的
#pragma pack
内存对齐 不同类型的数据在内存当中并不是一个一个顺序排放的
CPU对内存的读取并不是连续的 按块读取
只能读写偶地址
pragma(4) 告诉编译器的默认对齐方式
结构体成员的对齐参数为其所有成员使用的对齐参数的整数倍
#和##运算符使用解析:
预处理器的开始符
#用于在预编译期将宏参数转换为字符串
#存在于宏里面使用 打印调用的函数名
##用于在编译的时候粘连两个字符
##定义结构体类型
#define STRUCT(type) typedef struct _tag_##type type; struct _tag_##type