一、 数据类型及运算
求补码
- 原码的基础上, 符号位不变, 其余各位取反, 最后+1
- 原码转补码不考虑符号位
- 补码转原码,符号位不参与运算
- 取反后 + 1 == 取反前 - 1
科学计数法表示
- 1.8 * 10^11 --> 1.8E11
- 9.34 * 10^-3 --> 9.34E-3
相关细节
- sizeof()是一个运算,而非函数
- ++运算不能用在实数上
- 判断一个整数是否是2^n(2,4,6,8,16...)
- !(x & (x - 1))
- 三目条件运算符代码更优
- 编译器能产生比if...else...更优的代码
运算符优先级、结合方向规则
- 单目 > 双目
- 算术 > 关系 > 位 > 逻辑 > 条件(三目)> 赋值 > 逗号
- 算术: + - * /
- 关系: > < >= <=
- 位: & | ^
- 单目: ~
- 逻辑: && ||
- 单目: !
- 自右向左的三种运算符
- 单目
- 赋值
- 条件
数据输入与输出
- printf()语句从右向左计算输出表达式的值
i = 1;
printf("%d, %d\\\\n", i++, i--);
//res: 0,1
//先执行i--,再执行i++
常用输出函数
- printf()
- putchar()
- 输出一个字符
- 必须是字符型变量或常量
- puts()
- 输出一个字符串
- 必须是字符串或常量
常用输入函数
scanf()
-
gets()
- 每次读取一个字符串
-
getche()
- conio.h中
- 读取字符不用按回车
-
getchar()
- stdio.h中
- 完成后须按回车
getche() & getchar():
每次读取一个字符scanf() & gets()区别:
scanf不能输入含空格字符串,gets可以
二、选择语句和循环语句
switch:case 常量表达式
- 常量表达式只能为整型、字符型
- 不允许浮点型
三、数组
定义
- 定义数组未赋初值
- Turbo C会给数组置0
- VC则取随机值
- 定义静态数组,则系统自动赋0
比较字符串数组中的值
- C: strcmp(str1,str2)
- C++: str1 == str2
- JAVA: str1.equals(str2)
- java中,str1 == str2 比较的是地址
四、指针
指针运算
- 指针相减: 表示两指针所指地址之间的数据个数
- 指针相加: 没有意义,错误
数组与指针
1、一维数组首地址
int a[10], *p;
p = &a[0];
p = a
//等价,将数组首元素的首地址赋给指针p
-
表示:
&a[0], a: 数组首元素的首地址
&a: 数组首地址 对比:
a == &a[0]
a != &a //地址值相同,含义不同
2、二维数组首地址
int a[10][10];
地址值相同,含义不同:
a:
- 二维数组首元素首地址
- 代表一维数组元素的首地址
&a:
- 数组首地址
&a[0]:
- 二维数组首元素首地址
&a[0][0]:
- &a[0][0] != a
- a[0] == &a[0][0]
3、二维数组指针
-
int (*p)[3]:
- 指向含3个元素的二维数组的行指针
- 数组每列有3个元素
-
int \p[3] & int \(p[3]):
- 指针数组,每个元素均是一个指针
指针与引用的区别
- 非空区别
- 引用必须总是指向某些对象
- 不能使用指向空值的引用
- 不存在指向空值的引用
- 效率比使用指针高
- 指针可以指向空值
- 引用必须总是指向某些对象
- 合法性区别
- 使用引用前,无需测试其合法性
- 使用指针总是需要判空
- 可修改区别
- 指针可被重新赋值,以指向另一对象
- 引用
总指向初始化时被指定的对象
以后都不能改变
但指定对象的内容可以改变
- 应用区别
- 指针场景
存在不指向任何对象的情况
不同的时刻指向不同对象的情况
- 引用场景
指向一个对象后就不会改变指向的情况
- 指针场景
ps:声明引用 / const常量 的同时,必须初始化
函数指针
-
float(**def)[10];
二级指针
指向一个一维数组的指针
数组元素都是float
-
double\(\gh)[10];
指针gh,指向一个一维数组
该数组元素的类型均为double *
-
double(*f[10])();
-
f是一个数组,含10个元素
- 元素都是函数指针
-
指向的函数
没有参数
返回double类型的值
-
-
int \( (\b)[10] );
- 和int \(\b)[10]一样
-
Long (* fun)(int)
- 函数指针
五、类型转换
(int &)相关
float a = 1.0f;
(int)a实际上是以浮点数a为参数构造了一个整型数,该整数的值是1。
(int&)a则是告诉编译器将a当作整数看(并没有做任何实质上的转换)。
unsigned int
unsigned int a = 0xFFFFFFF7;
-
unsigned char i = (unsigned char)a;
i: 000000f7
-
char \*b = (char *)&a;
*b: fffffff7
隐式类型转换
算术运算式中,低类型能够转换为高类型
-
赋值运算式
- 右边表达式的值自动隐式转换为左边变量的类型,并赋值给他
函数调用中参数传递时,系统隐式地将实参转换为形参的类型后,赋给形参
函数有返回值时,系统将隐式地将返回表达式类型转换为返回值类型,赋值给调用函数
六、位运算相关
取两数的平均值:
(x & y) + [(x ^ y) >> 1]
另类取两数较大值:
max = [(a + b) + abs(a - b)] / 2
三数取中间数:
t1 = max(a, b);
t2 = max(b, c);
t3 = max(a, c);
min( t1, min(t2, t3) )
七、函数
静态函数: 不可被其他文件调用的函数
函数重载:
参数类型不同
参数个数不同
对返回类型没有要求
八、#define & const & sizeof
#define实例
* #define SEC (60 * 60 * 24 * 365)UL
* #define MIIN(A, B) ( (A) <= (B) ? (A) : (B) )
const,#define的区别
-
const
有数据类型
可进行类型安全检查
可对其进行调试
-
\#define
没有数据类型
仅进行字符替换,没有类型安全检查
无法调试
-
c中const
- 被当做一个不能被改变的普通变量
error
const bufsize = 100;
char buf[bufsize];
字节对齐
-
数据对齐规则
结构的首地址必须是结构内最宽类型的整数倍地址
结构体的每一个成员起始地址必须是自身类型大小的整数倍
结构体的整体大小必须可被对齐值整除
结构体的整体大小必须可被本结构内的最宽类型整除
sizeof
- 结构体或类内的静态变量
struct s{
int a;
static int b;
};
s ss;
sizeof(ss)
结果:4
静态变量存放在全局数据区
sizeof计算栈中分配的大小
任何类型指针大小相同:4(32位)
对函数使用sizeof
在编译阶段会被函数返回值的类型取代
-
空类大小
- 单继承:1
- 多继承:1
- 虚继承:4
- 涉及虚表(虚指针)
内联函数 vs. 宏
- 内联
- 相比普通函数: 加快程序运行速度
- 直接嵌入目标代码
- 要做参数类型检查
- 宏
- 简单的替换
- 不做参数类型检查
九、 C++面向对象
1、类和对象
类对象的存储空间
- 只为每个对象的数据成员和函数地址分配内存空间
- 类中所有成员函数只生成一个副本
- 该类每个对象执行相同的函数成员
拷贝构造函数
- 功能
- 用一个已知的对象来初始化一个被创建的同类的对象
- 特点
- 函数只有一个参数,并且是对某个对象的引用
- 每个类都必须有一个拷贝初始化构造函数
- 格式
- 类名::拷贝初始化构造函数名(const 类名 &引用名)
静态成员
- 静态数据成员
- 特点
- 类的所有对象共享
- 必须初始化,且要在类外初始化
- 引用格式
- 类名::静态数据成员名
- 特点
- 静态成员函数
- 特点
- 类的所有对象共享
- 只能使用类的静态成员和非数据成员
- 引用格式
- 类名::静态成员函数名
- 特点
类成员指针
const成员函数
定义: 任何不修改成员数据的函数都应声明为const函数
原型: int GetY() const;
细节:
- const函数想修改成员变量
- 在相应变量定义处加上mutable
- mutable int m_Count;
2、友元函数
定义
- 需在类体内声明
- 可访问类的私有成员
- 不是类的成员函数
优点: 提高程序运行效率
缺点: 破坏类的封装性和隐藏性
特点: 可以是多个类的友元
3、继承和派生
公有继承
- 派生类成员函数可访问基类中的公有成员和保护成员
- 派生类的对象仅可访问基类中的公有成员
派生类
-
构造函数执行顺序
- 基类构造函数
- 子对象类的构造函数(如果有的话)
- 派生类构造函数
-
析构函数执行顺序
- 派生类的析构函数
- 基类的析构函数
虚基类