C和指针
指针和引用的区别
- 引用可以说是别名,指针有自己的存储空间,里面存储的是所指对象的地址
- 引用必须初始化,指针不必须初始化
- 引用初始化后不能更改引用的对象,指针初始化后可以更改指向的对象
- 两者在汇编层面没有区别
- 使用sizeof运算符看指针是4字节(32位机器)或8字节(64位机器),而用sizeof运算符看引用则取决于被引用对象的大小
- 指针可以进行自增或自减操作,可以访问原对象相邻存储空间的内容,引用只能固定引用
- 返回动态内存分配的对象或内存,必须用指针,用引用有可能内存泄露
- 从面向对象的角度,引用不是对象,指针是对象
数组和指针的联系、区别
- 指针是单个空间,存储的是所指对象的地址;数组可以有若干单位的空间,存储对象本身;可以有指向数组的指针,也可以有每个单元都是指针的数组
- 指针间接访问对象,得先解引用,指针可以直接访问
- 当指针指向数组时,可以用自增自减在数组元素上移动,但若要访问还是需要解引用
- 当数组作为函数参数传入时,会自动变化为指针,指向数组首位
- 不能对数组名直接复制,但可以对指针直接复制
- 用运算符sizeof可以得到数组字节数(数组大小),但只能得到指针类型本身的字节数
- 内置的下标运算符的索引值不是无符号类型,这与vector这种STL容器不一样,所以内置的下标运算符支持负值
1 | void testArray(int a[]) { |
多维数组
- 严格来说,没有多维数组,应该是数组的数组
1 | int ia[3][4]; // 大小为3的数组,每个元素是含有4个整数的数组 |
- 初始化,嵌套花括号是非必需的
1 | int ia[3][4] = { |
函数指针
- 函数指针是指向函数的指针,声明如:
int (*p)(int a, int b); - 每个函数在编译时都有一个入口地址,这在汇编代码里面看的十分清楚,函数指针存储的就是这种入口地址的值,可以直接用函数指针调用函数
- 声明:
int (*a)();,注意不能写成int *a();,否则编译器会认为一个名为a的函数的返回值是int* - 赋值:直接将一个已经定义的函数名,赋值给函数指针就可以:
a = function; - 调用:
a();,不能直接像定义一个函数一样定义一个函数指针,必须先声明,再给它赋值一个已经定义好的函数名 - 函数名传递作为函数参数时,会自动退化成一个函数指针
- 示例:精准解析,在命令行工具如sed、awk中,为了精准解析命令,在结构体里面搞一个函数指针,匹配不同参数,就找不同的函数来执行不同的功能,这比大段的ifelse优雅多了
- 示例:回调函数,如linux下的signal函数,捕捉某个信号,执行某个信号处理函数,这里要执行的信号处理函数就是用的函数指针
- 示例:反射,通过字符串直接调用函数,提高代码灵活性和扩展性,Java提供,C++本身不提供,一些类库可能会提供,比如Qt的
QMetaObject::invokeMethod("function_name"); - C++11后多用
std::function,可读性大大提升 - C++11可以直接用auto关键字来声明函数指针,
auto f = funcname
1 | int function() { // 正确的函数声明 |
函数指针有可能会出现很复杂的情况,循序渐进的理解一下
1 | // 最简单的函数及其对应的函数指针: |
这样显然太过抽象,还好我们可以用typedef关键字把函数指针简化,像普通的int double那样去操作
1 | void (*f_ptr)(); // 这是定义了一个名为f_ptr的函数指针「变量」 |
或者使用尾置返回类型
1 | auto f1(int) -> int(*)(int*, int); //f1函数返回一个函数指针 |
或者结合decltype
1 | string::size_type sumLength(const string&, const string&); |
再把数组扯进来
1 | void (*f_ptr[10])(); // 定义一个长度为10的数组,数组中的元素类型是函数指针 |
类的静态成员函数指针,因为静态成员函数存储方式与普通函数一样,可以取得该函数在内存中的实际地址
- 声明:
void (*static_fptr)(); - 调用:
static_fptr(); - 赋值:
void (*static_fptr)() = &Test::staticFunc;
类的成员函数指针,普通成员函数必须提供this指针
- 声明:
void (Test::*fptr)();,类成员函数指针的声明,就必须加上类名限定,这就声明了一个函数指针变量fptr,他只能指向Test类的成员函数。 - 赋值:
fptr = &Test::function - 调用:类的成员函数是无法直接调用的,必须要使用对象或者对象指针调用(这样函数才能通过对象获取到this指针)。
(t.*fptr)();,t是Test类的一个实例,通过对象调用。(pt->*fptr)();,pt是一个指向Test类对象的指针,通过指针调用。
虚函数指针,同上,但是虚函数是面向多态的,所以基类的成员函数指针可以赋值给派生类的成员函数指针,坑很多,详见虚函数指针揭秘
没法搞出指向构造函数和析构函数的函数指针,因为构造和析构函数不能被取地址
char*与char[]的区别
- char []定义的是一个字符数组,注意强调是数组,数组内容可以改变
- char * 定义的是一个字符串指针,注意强调是指针,指针内容是可以改变的,即可以指向其他地址,但不能改变指针所指向的内容!
关于它们的字节大小与相互之间的转换,仔细体会以下例子(已测试):
1 | char* str = "hello there"; // "hello there"是字面值常量,不可修改! |
1 | const char * arr = "123"; // 字符串123保存在常量区,本来就是常亮,const加不加都可以 |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 一知半解!