Skip to content

Instantly share code, notes, and snippets.

@lixingcong
Last active Apr 14, 2021
Embed
What would you like to do?
C各种指针:数组指针,函数指针,回调函数,指针的引用,指针的指针
#include <iostream>
#include "math.h"
// 数组指针
using namespace std;
const int COL = 2;
void printRowData(int* p)
{
for (int i = 0; i < COL; ++i)
cout << *(p + i) << ",";
}
int main()
{
int ROW;
int(*ptr)[COL]; // 数组指针(行指针),若自增1,则跳到COL个元素后(下一行)
cout << "column count = " << COL << "\n";
while (cout << "input row count:\n" && cin >> ROW) {
ptr = new int[ROW][COL];
for (int i = 0; i < ROW; ++i) {
for (int j = 0; j < COL; ++j) {
cout << "input a number of (row, col) = (" << i << "," << j << "):\n";
cin >> *(*(ptr + i) + j);
}
}
// 按照ptr访问方式
cout << "output via ptr:" << endl;
for (int i = 0; i < ROW; ++i) {
for (int j = 0; j < COL; ++j) {
cout << *(*(ptr + i) + j) << ",";
}
cout << endl;
}
// 按照传参数方式打印(此时数组指针退化成普通指针,再传入到函数中)
cout << "output via calling function:" << endl;
for (int i = 0; i < ROW; ++i) {
printRowData(*(ptr + i));
cout << endl;
}
delete[] ptr;
cout << "----------------" << endl;
}
return 0;
}
数组指针和指针数组的区别
数组指针(也称行指针)
定义 int (*p)[n];
()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。
如要将二维数组赋给一指针,应这样赋值:
int a[3][4];
int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
p=a; //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
p++; //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]
所以数组指针也称指向一维数组的指针,亦称行指针。
指针数组
定义 int* p[n];
[]优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1是错误的,这样赋值也是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。
如要将二维数组赋给一指针数组:
int* p[3];
int a[3][4];
for(i=0;i<3;i++)
p[i]=a[i];
这里int* p[3] 表示一个一维数组内存放着三个int*指针变量,分别是p[0]、p[1]、p[2]
所以要分别赋值。
这样两者的区别就豁然开朗了,数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。
还需要说明的一点就是,同时用来指向二维数组时,其引用和用数组名引用都是一样的。
比如要表示数组中i行j列一个元素:
*(p[i]+j)、*(*(p+i)+j)、(*(p+i))[j]、p[i][j]
优先级:()>[]>*
http://www.cnblogs.com/hongcha717/archive/2010/10/24/1859780.html
// http://stackoverflow.com/questions/4295432/typedef-function-pointer
#include <iostream>
using namespace std;
#if 1
typedef char* (*my_handler)(char *str);
// 指针函数
char* print_a(char* str){
return str+1;
}
// 函数指针
void foo(my_handler handler, char* str){
cout<<(*handler)(str)<<endl;
}
int main(int argc, char** argv) {
char a[]="fuckyou";
my_handler h = &print_a;
cout<<(*h)(a)<<endl;
foo(h,a);
return 0;
}
#else
typedef void (*my_handler)(char* str);
void print_a(char* str){
cout<<str<<endl;
}
void foo(my_handler handler, char* str){
(*handler)(str);
}
int main(int argc, char** argv) {
char a[]="fuckyou";
my_handler h = &print_a;
(*h)(a);
foo(h,a);
return 0;
}
#endif
1、指针函数是指带指针的函数,即本质是一个函数。函数返回类型是某一类型的指针.
首先它是一个函数,只不过这个函数的返回值是一个地址值。函数返回值必须用同类型的指针变量来接受。所以经常使用在返回数组的某一元素地址上。
返回值类型* 函数名 (参数)
int* f (int x,int y);
2、函数指针是指向函数的指针变量,即本质是一个指针变量。
其实下面的“函数名”不能称为函数名,应该叫做:指针的变量名。这个特殊的指针指向一个返回整型值的函数。指针的声明笔削和它指向函数的声明保持一致。
返回值类型 (*函数名) (参数)
int (*f) (int x,int y);
3、使用typedef定义函数指针,常用于callback函数声明。
typedef is a language construct that associates a name to a type.
You use it the same way you would use the original type, for instance
typedef int myinteger;
typedef char* mystring;
typedef void (*myfunc)();
using them like
myinteger i; // is equivalent to int i;
mystring s; // is the same as char* s;
myfunc f; // compile equally as void (*f)();
4、给函数指针赋值,就是为函数指针指定一个函数名称,赋值的取地址符号"&"是可选的,却是推荐使用的。使用时取值"*"也是可选的,推荐使用。
#include <iostream>
int foo(int a){
return a;
}
int main(){
typedef int (*FuncPtr)(int);
FuncPtr p1=foo;
FuncPtr p2=&foo;
std::cout<<p1(200)<<std::endl;
std::cout<<(*p2)(200)<<std::endl;
return 0;
}
注意C++中的成员函数指针。对于赋值,你必须要加“&”;对于使用,你还得指定类实例,真麻烦。
class C1{
public:
int foo(int a){
return a;
}
};
int main(){
typedef int (C1::*FuncPtr)(int);
FuncPtr p=&C1::foo;
C1 c1;
std::cout<<(c1.*p)(1);
return 0;
}
#include <iostream>
using namespace std;
// 指向指针的指针
int main(){
int i = 5, j = 6, k = 7;
// ----------------------------------------------------
cout<<"case 1\n";
int* ip1 = &i;
int* ip2 = &j;
/*
* i: [5] j: [6] k: [7]
* ^ ^
* | |
* ip1: [*] ip2: [*]
*/
cout<<"ip1="<<ip1<<endl;
cout<<"ip2="<<ip2<<endl;
cout<<"*ip1="<<*ip1<<endl;
cout<<"*ip2="<<*ip2<<endl;
// ----------------------------------------------------
cout<<"\ncase 2\n";
int **ipp = &ip1; // 指向指针的指针
/*
* i: [5] j: [6] k: [7]
* ^ ^
* | |
* ip1: [*] ip2: [*]
* ^
* |
* ipp: [*]
*/
cout<<"*ip1="<<*ip1<<endl;
cout<<"*ipp="<<*ipp<<endl; // 输出ip1的地址
cout<<"**ipp="<<**ipp<<endl;
// ----------------------------------------------------
cout<<"\ncase 3\n";
*ipp = ip2;
/*
* i: [5] j: [6] k: [7]
* ^ ^
* | |
* ----------- |
* | |
* | |
* ip1: [*] ip2: [*]
* ^
* |
* ipp: [*]
*/
cout<<"*ip2="<<*ip2<<endl;
cout<<"*ipp="<<*ipp<<endl; // 输出ip2的地址
cout<<"**ipp="<<**ipp<<endl;
return 0;
}
如何理解指向指针的指针?
http://blog.jobbole.com/60647/
让我们暂时忘掉那些关于指针的各种类比。指针实际上存放的是内存的地址。
& 符号的意思是取地址,也就是返回一个对象在内存中的地址。
* 符号的意思是取得一个指针所指向的对象。 也就是如果一个指针保存着一个内存地址,那么它就返回在那个地址的对象。
所以当你这么写时 *ipp = ip2,实际上是把 ipp 存的地址所对应的对象,也就是 ip1 取到,然后把 ip2 存的值赋值给 ip1,也就是 j 的地址。
简单点就是:
&:取址。
* :取值。
#include <iostream>
// std::tr1::function
#include <tr1/functional> // gcc header
//#include <functional> // Visual Studio header
#include <string>
#include <sstream>
using namespace std;
// 定义几种类型的callback原型,一种不带参,另一种带参
typedef tr1::function< int () > FUNC_PARAMS_0;
typedef tr1::function< int (const string&) > FUNC_PARAMS_1;
typedef tr1::function< int (int, int) > FUNC_PARAMS_2;
void InterfaceFunc( const string& a , const string& b , FUNC_PARAMS_1 f )
{//测试用接口函数,将a+b得到的字符串转成整数并打印出来,f是回调函数
cout << f(a+b) << endl;
}
int f1( const string& str )
{//正常函数
cout << "int f1( const string& str )" << endl;
stringstream ss;
ss << str;
int result;
ss >> result;
return result;
}
class F2
{
public:
int operator()( const string& str )
{//仿函数
cout << "int F2::operator()( const string& str )" << endl;
stringstream ss;
ss << str;
int result;
ss >> result;
return result;
}
};
class F3
{
public:
int f3( const string& str )
{//类内非静态成员函数
cout << "int F3::f3( const string& str )" << endl;
stringstream ss;
ss << str;
int result;
ss >> result;
return result;
}
};
class F4
{
public:
static int f4( const string& str )
{//类内静态成员函数
cout << "static int F4::f4( const string& str )" << endl;
stringstream ss;
ss << str;
int result;
ss >> result;
return result;
}
};
class F5
{
FUNC_PARAMS_0 func_p0;
FUNC_PARAMS_1 func_p1;
FUNC_PARAMS_2 func_p2;
public:
int f5_params_0()
{
cout << "static int F5::f5_params_0( )" << endl;
return 0;
}
int f5_params_1(const string& str )
{
cout << "static int F5::f5_params_1( const string& str )" << endl;
cout << str << endl;
return 0;
}
int f5_params_2(int a, int b)
{
cout << "static int F5::f5_params_2( int a, int b )" << endl;
cout << a << '+' << b << '=' << a+b << endl;
return 0;
}
void setCallBack()
{//在类内绑定一个非静态成员函数作为callback
// 若cb函数无参数,bind不需第三个参数
func_p0=tr1::bind( &F5::f5_params_0 ,this);
// 若cb函数带有至少一个参数,bind需要增加相应的函数参数tr1::placeholders::_1..2...3...以此类推
func_p1=tr1::bind( &F5::f5_params_1 ,this, tr1::placeholders::_1);
func_p2=tr1::bind( &F5::f5_params_2 ,this, tr1::placeholders::_1, tr1::placeholders::_2);
}
void runCallBack()
{
func_p0();
func_p1("100");
func_p2(100,200);
}
};
int main()
{
string a = "123";
string b = "456";
//FUNC接受正常函数指针
InterfaceFunc( a , b , f1 );
cout << endl;
//FUNC接受仿函数
InterfaceFunc( a , b , F2() );
cout << endl;
//FUNC接受类内非静态成员函数
F3 f;
InterfaceFunc( a , b , tr1::bind( &F3::f3 , &f , tr1::placeholders::_1 ) );
cout << endl;
//FUNC接受类内静态成员函数
InterfaceFunc( a , b , F4::f4 );
cout << endl;
//FUNC存在于类里面
F5 f5;
f5.setCallBack();
f5.runCallBack();
cout << endl;
cin.ignore(); // 回车键结束
return 0;
}
《tr1::function对象作回调函数技术总结》
原文地址:http://blog.csdn.net/this_capslock/article/details/38564719
使用C++的TR1中中包含一个function模板类和bind模板函数。使用它们可以实现类似函数指针的功能,但是比函数指针更加灵活。
对于tr1::function对象可以这么理解:它能接受任何可调用物,只要可调用物的的签名式兼容于需求端即可,比如函数指针,仿函数对象,成员函数指针
先自定义了一种function类型FUNC,它接受所有参数为const string&并且返回值是 int的签名对象。函数InterfaceFunc第三个参数是一个FUNC对象,当作回调函数使用
这里需要特别注意下,第三次让FUNC接受类内非静态成员函数时,使用了tr1::bind( &F3::f3 , &f , tr1::placeholders::_1 )这样东西作为参数,
它的含义是:让&f作为F3::f3函数中的第1个参数,因为对于类内非静态成员函数,它有一个隐含的第一参数:this指针,因为成员函数是存储位置是在对象之外的,只根据F3::f3的地址无法得知具体对哪个对象操作,tr1::bind的作用正是告诉F3::f3,它隐含的this参数指向的是f这个已经定义好的对象,剩下的事情就和其它情况一样了。
// 指针的引用
#include <iostream>
void function1(int** pp)
{
**pp=100;
*pp=NULL;
}
void function2(int*& ref) // compile fail in C, but ok in C++
{
*ref=200;
ref=NULL;
}
void function3(int* p) // p: pass by value
{
*p=300;
p=NULL; // could not change the value of p
}
int main()
{
int a=999;
int* ptr;
int** ptr_ptr;
std::cout<<"a="<<a<<std::endl;
ptr=&a;
ptr_ptr=&ptr;
function1(ptr_ptr);
std::cout<<"p1: a="<<a<<", ptr="<<ptr<<std::endl;
ptr=&a;
ptr_ptr=&ptr;
function2(ptr);
std::cout<<"p2: a="<<a<<", ptr="<<ptr<<std::endl;
ptr=&a;
ptr_ptr=&ptr;
function3(ptr);
std::cout<<"p3: a="<<a<<", ptr="<<ptr<<std::endl;
return 0;
}
《指针的指针和指针的引用》
原文地址:http://blog.csdn.net/luoshenfu001/article/details/8601494
在下列函数声明中,为什么要同时使用*和&符号?以及什么场合使用这种声明方式?  
void func1( MYCLASS *&pBuildingElement );   
先来看“int **pp”和“int *&rp”区别。前者是一个指向指针的指针;后者是一个指针的引用。如果这样看不明白的话,变换一下就清楚了:
typedef int* LPINT;
LPINT *pp;
LPINT &rp;
而指针的指针和指针的引用作为传递参数时,如demo5.cpp的两个函数function1和2在被调用时,编译器编译的二进制代码都将传递一个双重指针,只不过两者的调用方法不同,达到的效果都是能修改指针指向的地址
但是demo5.cpp的函数function3在被调用时,无法修改指针的地址。
可见,“引用”仅仅是为了给重载操作符提供了方便之门,其本质和指针是没有区别的。所以只要你碰到*&,就应该想到**。也就是说这个函数修改或可能修改调用者的指针,而调用者象普通变量一样传递这个指针,不使用地址操作符&。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment