函数指针
基本概念
程序运行期间,每个函数都会占用一段连续的内存空间。而函数名就是该函数所占内存区域的起始地址(也称“入口地址”)。 可以将函数的入口地址赋给一个指针变量,使该指针变量指向该函数。然后通过指针变量就可以调用这个函数。这种指向函数的指针变量称为“函数指针”。
定义形式
类型名 ( 指针变量名)(参数类型1, 参数类型2,…);*
如:int (*pf)(int ,char);
pf
是一个函数指针,它所指向的函数,返回值类型应是int
,该函数应有两个参数,第一个是int
类型,第二个是char
类型。
使用方法
可以用一个原型匹配的函数的名字给一个函数指针赋值。 要通过函数指针调用它所指向的函数,8写法为:
**函数指针名(实参表); **
例:
|
main函数的输出结果为“4”
函数指针和qsort库函数
函数指针的作用举例:C语言中的快排库函数
void qsort(void *base, int nelem, unsighed int width, |
表示可以对任意类型的数组进行排序,其中参数(*pfCompare)(const void *, const void *)
为函数指针,其指向的是一个返回类型为int
的函数,这个函数有两个参数,类型都为const void *
。
对数组排序,需要知道
a[0] | a[1] | … | a[j] | … | a[n-1] |
---|---|---|---|---|---|
*base | 1 | … | j | … | n-1 |
- 数组的起始地址
- 数组长度
- 每个元素的值
- 元素排序规则
调用qsort
函数时
- 第一个参数
base
:待排序数组的起始地址 ; - 第二个参数
nelem
:待排序数组的元素个数; - 第三个参数
width
:待排序数组的每个元素的大小(以字节为单位); - 最后一个参数
pfCompare
:函数指针,指向比较函数的地址
比较函数的作用, 就是告诉qsort,如何去判定两个元素,到底哪一个应该排在前面, 哪一个应该排在后面,所以pfCompare
指向比较函数的地址,需要程序猿自己编写。它必须遵循这样一种格式:返回值一定是int
,必须有两个类型为const void*
的参数。
排序的过程,实际上就是在不断地比较数组元素,并且交换它们的位置。qsort
函数在执行期间要比较两个数组元素时, 会通过pfCompare
指针,调用 程序员自己编写的”比较函数“。调用”比较函数“时候,会把待比较的两个元素的地址,作为参数,传递给”比较函数“。 然后qsort
根据”比较函数“的执行结果,即返回值, 判断待比较的两个元素的前后顺序。
当比较a[0]和a[1]两个元素时,pfCompare(e1, e2);
比较函数编写规则为:
- 如果
* elem1
应该排在* elem2
前面,则函数返回值是负整数 - 如果
* elem1
和* elem2
哪个排在前面都行,那么函数返回0 - 如果
* elem1
应该排在* elem2
后面,则函数返回值是正整数
实例
下面的程序,功能是调用qsort
库函数,将一个unsigned int
数组按照个位数从小到大进行排序。比如 8,23,15三个数,按个位数从小到大排序,应该是 23,15,8 。
|
输出结果为:
10 11 123 4 8 |
比较时需要对elem1
和elem2
进行强制类型转换。因为,elem1
是void * 指针,如果只写
*elem1,编译器不知道
elem1指向的元素到底是多少个字节,因此直接写
elem1是非法的。所以需要对
elem1进行强制类型转换, 把它强制转换成一个
unsigned int *的指针,再赋值给
p1,
p1为
unsigned int *`类型变量。
命令行参数
基本概念
将用户在CMD窗口输入可执行文件名的方式启动程序时,跟在可执行文件名后面的那些字符串,称为“命令行参数”。命令行参数可以有多个,以空格分隔。
在C++中:
int main(int argc, char * argv[]) { |
- argc:启动程序时,命令行参数的个数。
C/C++语言规定,可执行程序程序本身的文件名,也算一个命令行参数,因此,argc的值至少是1。
- argv:指针数组,其中每个元素都是一个
char*
类型的指针。指针指向存放命令行参数的字符串。
例如,argv[0]指向的字符串就是第一个命令行参数,即可执行程序 的文件名,argv[1]指向第二个命令行参数,argv[2]指向第三个命令 行参数…
例子
|
上面的例子循环遍历了argv数组,该数组共有argc个元素,将每一个元素指向的字符打印出来。每一个元素指向的字符串都是一个命令行参数。当把程序编译为sample.exe,然后在控制台输入:
sample para1 para2 s.txt 9 "hello world" |
其打印结果为:
sample |
要打印空格时,使用字符串作为参数即可。
位运算
基本概念
用于对整数类型(int,char, long 等)变量中的某一位(bit),或者若干位进行操作。如:
- 判断某位是否为1
- 只改变其中某位,保持其他不变
c++中有六种位运算符:
& | | | ^ | ~ | << | >> |
---|---|---|---|---|---|
按位与 | 按位或 | 按位异或 | 按位取反 | 左移 | 右移 |
双目 | 双目 | 双目 | 单目 | 双目 | 双目 |
按位与“&”
将参与运算的两操作数各对应的二进制位进行与操作,只有对应的两个二进位均为1时,结果的对应二进制位才为1,否则为0。
例如:表达式“21 & 18 ”的计算结果是16
21 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0001 | 0101 |
---|---|---|---|---|---|---|---|---|
18 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0001 | 0010 |
21&18 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0001 | 0000 |
作用
- 将某变量中的某些位清0且同时保留其他位不变;
- 获取某变量中的某一位
- 需要将int型变量n的低8位全置成0,而其余位不变,则可以执行:
n = n & 0xffffff00; |
若n为short型,执行
n &= 0xff00; |
- 判断一个int型变量n的第七位(从右往左,从0开始数 )是否为1
(n & 0x80) == 0x80; |
按位或“|“
将参与运算的两操作数各对应的二进制位进行或操作,只有对应的两个二进位都为0时,结果的对应二进制位才是0,否则为1。
例如:表达式“21 | 18 ”的计算结果是23
21 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0001 | 0101 |
---|---|---|---|---|---|---|---|---|
18 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0001 | 0010 |
21|18 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0001 | 0111 |
作用
- 将某变量中的某些位,置1,且保留其他位不变
如:需要将int型变量n的低8位全置成1,而其余位不变,则可以执行:
n |= 0xff; |
按位异或“^”
将参与运算的两操作数各对应的二进制位进行异或操作,即只有对应的两个二进位不相同时,结果的对应二进制位才是1,否则为0。
例如:表达式“21 ^ 18 ”的计算结果是7
21 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0001 | 0101 |
---|---|---|---|---|---|---|---|---|
18 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0001 | 0010 |
21^18 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0111 |
作用
- 将某变量中的某些位取反,且保留其他位不变
如:需要将int型变量n的低8位取反,而其余位不变,则可以执行:
n ^= 0xff; |
特点
- a^b=c,那么就有 c^b = a以及c^a= b。
此规律可以用来进行简单的加密和解密。
- 能实现不创建临时变量,就能交换两个变量的值。
int a = 5, b = 7; |
按位非“~”
按位非运算符“~”是单目运算符。其功能是将操作数中的二进制位0变成1,1变成0。
例如:表达式“~21”的值是整型数 0xffffffea
21 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0001 | 0101 |
---|---|---|---|---|---|---|---|---|
~21 | 1111 | 1111 | 1111 | 1111 | 1111 | 1111 | 1110 | 1010 |
左移运算符“<<”
表达式: a << b 的值是:将a各二进位全部左移b位后得到的值。左移时,高位丢弃,低位补0。a 的值不因运算而改变。
例如: 9 << 4 结果为144
9 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0001 | 1001 |
---|---|---|---|---|---|---|---|---|
9<<4 | 0000 | 0000 | 0000 | 0000 | 0000 | 0001 | 1001 | 0000 |
实际上,m左移1位,就等于 $m \times 2 $ ,左移n位,就相当于 。而左移操作比乘法操作快得多。
右移运算符“>>”
表达式:a >> b 的值是:将a各二进位全部右移b位后得到的值。右移时,移出最右边的位就被丢弃。 a 的值不因运算而改变。
对于有符号数,如long,int,short,char类型变量,在右移时,符号位(即最高位)将一起移动,并且大多数C/C++编译器规定,如果原符号位为1,则右移时高位就补充1,原符号位为0,则右移时高位就补充0。
例如:-25 >> 4 = -2 、-2 >> 4 = -1 、18 >> 4 = 1
-25 | 1111 | 1111 | 1111 | 1111 | 1111 | 1111 | 1110 | 0111 |
---|---|---|---|---|---|---|---|---|
-25 >> 4 | 1111 | 1111 | 1111 | 1111 | 1111 | 1111 | 1111 | 1110 |
例子
|
输出结果为:
1=3, n2=fffffffe, n3=ffe, c=1 |
n1 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 1111 |
---|---|---|---|---|---|---|---|---|
n1>>2 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0011 |
n2 | 1111 | 1111 | 1111 | 0001 | ||||
n2>>3 | fffffffe | 1111 | 1111 | 1111 | 1110 | |||
n3 | 1111 | 1111 | 1110 | 0000 | ||||
n3>>4 | ffe | 1111 | 1111 | 1111 | 1110 | |||
c | 0000 | 1111 | ||||||
c>>3 | 1 | 0000 | 0001 |
编程题目
题目:有两个int型的变量a和n(0 <= n <= 31),要求写一个表达式,使该表达式的值和a的第n位相同。
答案一:
(a >> n) & 1 |
分析:
a | …… | …… | ||||
---|---|---|---|---|---|---|
a>>n | …… | …… | ||||
(a>>n) & 1 | 0 | 0 | …… | 0 | …… |
答案二:
(a & (1 << n )) >> n |
引用
基本概念
下面的写法定义了一个引用,并将其初始化为引用某个变量。
int n = 4; |
某个变量的引用,等价于这个变量,相当于该变量的一个别名 。
int n = 7; |
- 定义引用时一定要将其初始化成引用某个变量;
- 初始化后,它就一直引用该变量,不会再引用别的变量了;
- 引用只能引用变量,不能引用常量和表达式。
应用
交换两个整型变量值的函数
在C语言中:
void swap(int * a, int * b) { |
在C++中使用引用:
void swap(int & a, int & b) { |
引用作为函数的返回值
int n = 4; |
此时可以对一个函数的返回结果赋值。
常引用
定义引用时,前面加const关键字,即为“常引用”
int n; |
- 不能通过常引用修改其引用的内容:
int n = 100; |
- 常引用和非常引用的转换
const T &
和T &
是不同的类型T &
类型的引用或T
类型的变量可以用来初始化const T &
类型的引用const T
类型的常变量和const T &
类型的引用则不能用来初始化T &
类型的引用,除非进行强制类型 转换。
const关键字和常量
const用法
- 定义常量
const intMAX_VAL= 23; |
- 定义常量指针
- 不能通过常量指针修改其指向的内容。
int n, m; |
- 不能把常量指针赋值给非常量指针,反过来可以
const int * p1; |
- 函数参数为常量指针时,可以避免函数内部不小心改变参数所指地方的内容
void MyPrinf(const char *p){ |
strcpy
函数第一个参数类型为char*
。参数p
的类型是const char*
。 不能用const char*
类型的指针给char*
类型的指针赋值。所以p
的类型跟char*
不匹配的,编译出错。 因此它就可以避免在函数内部不小心写出了能改变参数指针所指向的地方的内容。
- 定义常引用
不能通过常引用修改其引用的变量
int n; |
动态内存分配
new运算符
new分配变量
int *pn; |
new分配数组
int *pn; |
delete释放动态分配的内存
delete释放变量
new
动态分配的内存空间,一定要用 delete
运算符进行释放 。delete
指针必须指向new
出来的空间。
int *p = new int; |
delete释放数组
用delete
释放动态分配的数组,要加[]
。delete []
指针必须指向new
出来的数组。
int *p = new int[20]; |
位运算测试
printf("%d\n", 34 & 27);
的输出结果是:2(按位与)
34 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0010 | 0010 |
---|---|---|---|---|---|---|---|---|
27 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0001 | 1011 |
34&27 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0010 |
printf("%x\n",-12 >> 2 );
的输出结果是:fffffffd(右移)
-12 | 1111 | 1111 | 1111 | 1111 | 1111 | 1111 | 1111 | 0100 |
---|---|---|---|---|---|---|---|---|
-12>>2 | 1111 | 1111 | 1111 | 1111 | 1111 | 1111 | 1111 | 1101 |
f | f | f | f | f | f | f | d |
printf("%d\n",26 | 14);
的输出结果是:30(按位或)
27 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0001 | 1010 |
---|---|---|---|---|---|---|---|---|
14 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 1110 |
26|14 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0001 | 1110 |
printf("%d\n",18 ^ 22);
的输出结果是:4(按位异或)
18 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0001 | 0010 |
---|---|---|---|---|---|---|---|---|
22 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0001 | 0110 |
18^22 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0100 |
int * p = new int[12];
动态分配了多少字节的空间?
48; int为4字节,new了一个int数组,长度为12
- 下面程序段:
int main(){ |
其输出结果是:5
- 以下说法正确的是:
- [x] 类的成员函数之间可以互相调用
- [ ] 每个对象内部都有成员函数的实现代码
- [ ] 一个类的私有成员函数内部不能访问本类的私有成员变量
- [ ] 编写一个类时,至少要写一个成员函数
- 以下对类A的定义,哪个是正确的?
- [ ] A
class A { |
- [ ] B 缺少“;”
class A { |
- [X] C
class A{ |
- [ ] D
class A{ |
- 假设有以下类A:
class A{ |
以下程序片段,哪个是不正确的?
- [x]
A a,b; if( a != b) a.func(5);
- [ ]
A a; a.func(5);
- [ ]
A * p = new A; p->func(5);
- [ ]
A a; A & r = a; r.func(5);