C语言指针初探 一 指针与函数

1.、通过指针参数申请动态内存。

2、利用函数返回值申请动态内存。

3、返回栈内存的(或静态存储区的)指针或引用。

以上知识请参考   内存管理

4、函数参数的三种传递方式

(1)、值传递

这个应该简单吧,来看个经典程序

#include <iostream>
#include <stdio.h>
using namespace std;
void Swap(int a,int b)
{
    int temp=a;
    a=b;
    b=temp;
}
int main()
{
    int a=10;
    int b=20;
    Swap(a, b);
    cout<<a<<" "<<b<<endl;//10 20
    return 0;
}
这个好理解,函数Swap 为 a,b制作副本,然后交换副本,然而实参没有改变,所以实际上没有交换。值传递需要进行数据的复制,制作副本,如果不是内部数据类型,那么会降低效率。

(2)、引用传递

通过引用传递,在调用函数时,不会再为形参分配内存空间因此此时形参就是实参的本身,在函数中对形参的任何操作,本质上都是对实参进行操作。

#include <iostream>
#include <stdio.h>
using namespace std;
void Swap(int &a,int &b)
{
    int temp=a;
    a=b;
    b=temp;
    cout<<"形参a的地址:"<<&a<<endl;
}
int main()
{
    int a=10;
    int b=20;
    cout<<"变量a的地址:"<<&a<<endl;
    Swap(a, b);
    
    cout<<a<<" "<<b<<endl;
    return 0;
}

(3)、指针传递

#include <iostream>
#include <stdio.h>
using namespace std;
void Swap(int *a,int *b)
{
    int temp=*a;
    *a=*b;
    *b=temp;
    cout<<"形参a的地址:"<<a<<endl;
}
int main()
{
    int a=10;
    int b=20;
    cout<<"变量a的地址:"<<&a<<endl;
    Swap(&a, &b);
    cout<<a<<" "<<b<<endl;
    return 0;
}

在调用函数时,int 型 变量temp和形参(指针变量a和b)被创建并用主函数中的变量a,b的地址给 形参a,b复制,这时,间接引用*a和*b实际上等价于直接对实参进行操作,虽然在函数swap执行完毕后,形参和函数内的变量都会被撤销,对应的内存也会被回收,但是函数在执行过程中已经通过指针, 改变了实参的值了。

#include<stdio.h>
#include <iostream>
using namespace std;
void swap(int *p1,int *p2)
{
    int *p;
    p=p1;
    p1=p2;
    p2=p;
    
    cout<<"函数内"<<endl;
    cout<<"p1存的地址:"<<p1<<endl;
    cout<<"p2存的地址:"<<p2<<endl<<endl;
}
int main()
{
    int a=10,b=20;
    int *p1,*p2;
    p1=&a;
    p2=&b;
    cout<<"函数前:"<<endl;
    cout<<"p1存的地址:"<<p1<<endl;
    cout<<"p2存的地址:"<<p2<<endl;
    cout<<"a地址:"<<&a<<endl;
    cout<<"b的地址:"<<&b<<endl<<endl;
    swap(p1,p2);
    
    cout<<"函数后"<<endl;
    
    cout<<"p1存的地址:"<<p1<<endl;
    cout<<"p2存的地址:"<<p2<<endl;
    cout<<"a地址:"<<&a<<endl;
    cout<<"b的地址:"<<&b<<endl;
    cout<<a<<" "<<b<<endl;
    cout<<*p1<<" "<<*p2<<endl;
    return 0;
}

看看怎么a,b没有交换成功呢,仔细看看,明明在函数体内,二者的地址就发生了变化的,但是为什么退出来又还原了呢???

分析:请看指针传递中红色字体部分---------指针也被制作了副本,函数会制作副本,使_p1=p1 ,_p2=p2,,让两个指针(_p1,_p2)的值进行交换,但是这是副本,对真实的指针不起作用的。只用通过副本指针改变了变量的值,那么对真实的指针才有用。所以在退出函数后p1,p2又还原了。

swap函数这样改过后:

void swap(int **p1,int **p2)
{
    int *p;
    p=*p1;
    p1=p2;
    p2=&p;
    
    cout<<"函数内"<<endl;
    cout<<"p1存的地址:"<<p1<<endl;
    cout<<"p2存的地址:"<<p2<<endl<<endl;
}

关于二级指针请参看  指针与数组

a ,b实际上还是没有交换,但是两个指针的值却发生了交换(p1指向了b,p2指向了a),可以看出,指针也是一个普通的变量,也存在指针式的”值传递“方式,如果需要改变指针的值,那么需要对指针采用”指针传递“或"引用传递"的方式,说白了就是需要传入指针的地址。

void swap( int **p1, int **p2)
{
    int *p3,*p4,temp;
    p3=*p1;
    p4=*p2;
    temp=*p3;
    *p3=*p4;
    *p4=temp;
}
这种同上,其实意思都是一样,p1指向的是一个存变量地址的指针(*p1),p3=*p1,让指针p3指向了*p1,然后交换了*p3,*p4,也是就交换了*p1与*p2的值,所以改变了它们的指向。

#include<stdio.h>
#include <iostream>
using namespace std;
void Swap(int **p1, int **p2)
{
    int temp;
    temp=**p1;
    **p1=**p2;
    **p2=temp;
}
int main()
{
    int a=10,b=20;
    int *p1,*p2;
    p1=&a;
    p2=&b;
    int **p3=&p1;
    int **p4=&p2;
    int temp;
    temp=**p3;
    **p3=**p4;
    **p4=temp;
    //或者swap(&p1,&p2);
    cout<<a<<" "<<b<<endl;
    cout<<*p1<<" "<<*p2<<endl;
    return 0;
}
这种就可以改变实参的值,而没有改变指针的指向。

再看看这个肯定就更清楚了

#include <stdio.h>
void func(int* ptr, int &value)
{
	ptr = &value;
}
int main()
{
	int i = 10, j = 5;
	int *ptr = &i;
	func( ptr, j);
	printf("%d", *ptr);
	return 0;
}
结果是好多呢-----------自己运行一下吧

5、缺省参数调用

在函数声明语句中预先初始化一些参数的值,在调用语句中相应地参数可以缺省。

#include <iostream>
#include <stdio.h>
using namespace std;
void show(char *str,int n=1)
{
    for (int i=0; i<n; i++)
    {
        cout<<str[i];
    }
    cout<<endl;
}
int main()
{
    char str[]="hello";
    show(str,4);
    show(str);
    return 0;
}
缺省参数调用规则:

(1)参数缺省必须按照从后向前的顺序,下面这个函数声明是不合法的

void show(char *str,int n=1,int m)

换句话说,在函数声明时,如果对第n个参数进行初始化,那么 它后面的所有参数都应该进行初始化,所以,在函数定义时,应该合理安排形参列表的顺序。

(2)和函数声明一样,在函数调用时省略某个参数,必须省略其后面所有的参数。

    void show(char *str,int n=1,int m=4);
    show(str,,4);//非法

7、指针函数

先看下面的函数声明,注意,此函数有返回值,返回值是指针类型的

char *GetMerory()
用法举例:

<span style="font-size:14px;">#include <iostream>
#include <stdio.h>
using namespace std;
char *GetMerory()
{
    char *p=(char*)malloc(sizeof(char)*10);
    return p;
}
int main()
{
    char *str;
    str=GetMerory();
    strcpy(str, "hello");
    cout<<str<<endl;
    free(str);
    return 0;
}</span>

8、函数指针

顾名思义函数指针就是指向函数的指针

通过函数指针可以很方便的调用函数。

声明方式(必须要括号哟):

int (*Compare)(const char*,const char*);
举例:

#include <iostream>
#include <stdio.h>
using namespace std;
int (*Fun)(const char*,const char*);
int Display(const char *s1,const char *s2)
{
    cout<<s1<<endl;
    cout<<s2<<endl;
    return 0;
}
int main()
{
    char s1[]="hello";
    char s2[]="hello word";
    Fun=strcmp;
    int result=Fun(s1,s2);
    if(result==0)
    {
        cout<<"两个字符串不相等";
    }
    else if(result==1)
    {
        cout<<"第一个字符串大";
    }
    else
    {
        cout<<"第二个字符串大";
    }
    cout<<endl;
    Fun=Display;
    Fun(s1,s2);
    cout<<endl;
    return 0;
}

通过函数指针将函数作为另一个函数的参数(注意返回类型要一致)

#include <iostream>
#include <stdio.h>
using namespace std;
void (*fun)(int);
void Display(int i)
{
    cout<<i<<" ";
}
void Call(void(*fun)(int))
{
    int a[4]={1,2,3,4};
    for (int i=0; i<4; i++)
    {
        fun(a[i]);
    }
    cout<<endl;
}
int main()
{
    fun=Display;
    Call(fun);
    return 0;
}


函数指针数组

指向函数的指针还可以组成指针数组,称为函数指针数组。

#include <iostream>
#include <stdio.h>
using namespace std;
void (*fun[4])(int);
void Sub_Print(int i)
{
    cout<<i-1<<" ";
}
void Add_print(int i)
{
    cout<<i+1<<" ";
}
void Print(int i)
{
    cout<<i<<" ";
}
void Call(char *name[],void(*fun[])(int))
{
    int a[4]={1,2,3,4};
    for (int k=0; k<3; k++)
    {
        cout<<name[k]<<endl;
        for (int i=0; i<4; i++)
        {
            fun[k](a[i]);
        }
        cout<<endl;
    }

}
int main()
{
    char *name[]={"输出","减1输出","加1输出"};
    fun[0]=Print;
    fun[1]=Sub_Print;
    fun[2]=Add_print;
    Call(name,fun);
    cout<<endl;
    return 0;
}


采用typedef简化函数指针

#include <iostream>
#include <stdio.h>
using namespace std;
typedef void (*f)(int);
void Sub_Print(int i)
{
    cout<<i-1<<" ";
}
void Add_print(int i)
{
    cout<<i+1<<" ";
}
void Print(int i)
{
    cout<<i<<"";
}
void Call(const char *name,f fun )
{
    int a[4]={1,2,3,4};
    cout<<name<<endl;
    for (int i=0; i<4; i++)
    {
        fun(a[i]);
    }
}
int main()
{
    char *name[]={"输出","减1输出","加1输出"};
    f fun[]={Print,Sub_Print,Add_print};
    for (int i=0; i<3; i++)
    {
        Call(name[i], fun[i]);
        cout<<endl;
    }
    return 0;
}


返回函数指针的函数

和普通指针一样,函数指针也可以作为另一个函数的返回值

#include <iostream>
#include <stdio.h>
using namespace std;
typedef int (*f)(int,int);
int Add(int m,int n)
{
    return m+n;
}
int Multiplay(int m,int n)
{
    return m*n;
}
f lookup(int choice)
{
    if(choice==0)
    {
        return Add;
    }
    else
    {
        return Multiplay;
    }
}
int main()
{
    int num1,num2,x,result;
    cout<<"请输入两个整数以选择要进行的操作:";
    cin>>num1>>num2;
    cout<<"相加(0),相乘(其它):";
    cin>>x;
    f fun=lookup(x);
    result=fun(num1,num2);
    cout<<"结果:"<<result<<endl;
    return 0;
}


带参主函数

实际上main函数也可以带参数,main函数的操作是由操作系统来传的,c++规定main函数的参数只能有两个,agrc,argv.

第一个参数agrc必须是整型变量,称作参数计数器,其值包括命令名在内的参数的个数,第二个参数argv必须是指向字符指针数组,存放命令名和参数字符串的地址。

#include <iostream>
#include <stdio.h>
using namespace std;
int main(int argc,char *argv[])
{
    for (int i=argc-1; i>=0; i--)
    {
        cout<<argv[i]<<endl;
    }
    return 0;
}
编译链接上面代码文件,假设名字为example.exe,复制到c盘根目录下,在DOS环境下输入命令。

附上在Mac上的测试数据(命令不一样):


数组名做参数(这里说说多维数组做参数):

多维数组做函数参数,第一维的大小可以省略,但是其它维大小不能省略。

#include <iostream>
#include <stdio.h>
using namespace std;
int a[2][3][4];
void Display(int a[][3][4],int n)
{
    for (int i=0; i<n; i++)
    {
        for (int j=0; j<3; j++)
        {
            for (int k=0; k<4; k++)
            {
                cout<<a[i][j][k]<<" ";
            }
        }
    }
}
int main()
{
    for (int i=0; i<2; i++)
    {
        for (int j=0; j<3; j++)
        {
            for (int k=0; k<4; k++)
            {
                a[i][j][k]=i+j+k;
            }
        }
    }
    Display(a,2);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值