您的位置:首页 > 其它

c深入剖析跨函数调用指针(多级指针)问题

2017-03-19 19:40 218 查看
在c语言中,如果想要通过函数调用来改变值,有两种方式,第一种是通过指针的传递来改变值(这种可以一次改变多个变量的值),第二种是通过方法的返回值来传递值。第一种,中传递的时候其实只是地址的传递,相对第二种的值传递来说,第一种的效率要高不少,因为第一种传递的是地址,四个字节(部分计算机)大小的地址。特别,是在c中做字符串的处理时,这种第一种情况用的非常的多,我当时也是在做字符串处理的时候遇到这些问题,然后下面我会举例,还有一些思想方法。

一、通过函数调用来交换值(一级指针)

void exchangeValue(int* a,int* b){
//交换的时候,还是要通过第三个变量来交换两个变量的值
int temp = *a;//a的值,也就是x的值,因为我们是将x的地址传给了a,所以a就指向了x,同理b也是如此
*a = *b;
*b = temp;
}

int main(){
int x = 10;
int y = 20;
exchangeValue(&x,&y);
cout << x << endl;//这两句话是c++中的输出,为了方便就没写printf了
cout << y << endl;//输出结果是20和10
system("pause");
return 0;
}

上面的程序可以实现x和y值的交换,不知道大家,有没有这样想过,在exchangeValue方法中,为什么不能这样写

int* temp = a;

a = b;

b = temp;

这样能改变x和y的值吗?下面我们就来探讨一下这个问题

void exchangeValue(int* a,int* b){
//交换的时候,还是要通过第三个变量来交换两个变量的值
int* temp = a;//a的值,也就是x的值,因为我们是将x的地址传给了a,所以a就指向了x,同理b也是如此
a = b;
b = temp;
cout << "a地址:" << a << endl;//a的地址:00E4FB64
cout << "b地址:" << b << endl;//b的地址:00E4FB70
}

int main(){
int x = 10;
int y = 20;
cout << "x地址:" << &x << endl;//x的地址:00E4FB70
cout << "y地址:" << &y << endl;//y的地址:00E4FB64
exchangeValue(&x,&y);
cout << x << endl;//这两句话是c++中的输出,为了方便就没写printf了
cout << y << endl;//输出结果是10和20
system("pause");
return 0;
}


上面的程序并不能改变x和y的值,他们其实是把a和b进行交换了,x和y并没有交换,可以从地址的值中可以看出来。 00E4FB70这个地址里面存放的就是10,而00D4FB64这个地址中存放的就是20。地址是一个8位16进制的数,因为我的电脑一个指针时占4个字节也就是32位,而8位十六进制,一个十六进制相当于4个二进制,所以说,8位十六进制其实也就是32位二进制。通过exchangeValue函数传过去的是两个变量的地址,而上一个方法是对两个地址所指向的值进行了交换,而第二个交换的是地址,实际上它们是交换了a和b的值。虽然说,a和x指向的是同一个值,但是并不代表它们是同一个变量,不要误以为我们把x和y的地址进行了交换。整形x和y的地址是不能被改变的,只能改变它们的内容。

下面的这个方法也可以改变x和y的值,但是不是通过方法调用改变的,只是为了深入分析一下指针问题

int main(){
int* x = (int *)malloc(4);//这里必须的为指针变量x分配空间
*x = 10;
int* y = (int *)malloc(4);
*y = 20;
//交换x和y的内容(并不是交换他们的值所指向的内容)
int *temp;
temp = x;
x = y;
y = temp;
cout << *x << endl;
cout << *y << endl;
system("pause");
return 0;
}

上面的这个程序其实是交换了两个指针变量x和y里面的内容,本来指针变量x里面存放的是10的地址,而指针变量y中存放的是20的地址,后面通过temp指针变量,将指针变量x和y中的内容进行了交换,从而将两个指针变量所指向的int类型的值交换了。为什么指针变量x和y需要用malloc函数为其开辟空间呢,而temp指针却不需要呢?因为,如果你不给指针变量x开辟空间而是直接int* x = 10,这样会报x被使用而未初始化的异常,为什么呢?其实是因为,int* x=
10的意思是说,将10赋值以x中的内容为地址的int类型的变量,而你却没有给这个变量开辟空间,所以导致报错。而int* temp;temp = x;不需要用malloc为其开辟空间那是因为,你是将 指针变量x里面的内容赋值给了temp指针,从而让temp指针变量指向了10。

二、跨函数使用内存之二级指针

1、跨函数调用通过二级指针来给一个指针变量赋值,跨函数分配内存

void test(int **a){
*a = (int*)malloc(sizeof(int));//这里其实在给指针变量x开辟空间,相当于在main方法中的int* x = (int*)malloc(sizeof(int));
**a = 10;
}

int main(){
int *x;
test(&x);
cout << *x << endl;
system("pause");
return 0;
}


2、通过二级指针实现字符串的分割

//根据第一个分割的字符串分割str字符串,返回分割字符串之前的部分和之后的部分
void str_split(char *str,char * strsubstring,char ** str_before,char ** str_after){
*str_before = (char*)calloc(strlen(str),1);//为以str_before的内容为地址的字符串开辟空间并赋初值
//malloc和calloc都是为指针变量开辟内存,malloc不会为开辟的空间初始化保留的是垃圾数据,而calloc会初始化
*str_after = (char*)calloc(strlen(str),1);//用calloc的原因是为了避免产生乱码
char * s = strstr(str,strsubstring);//根据第一个strsubstring进行分割,返回的是从strsubstring开始的一个首地址
//如果字符串str中没有strsubstring这个被分割的字符串
if (s==NULL){
**str_before = '\0';
**str_after = '\0';
}else{
//如果被分割的字符串刚好位于字符串的开头部分
if (strlen(s) == strlen(str)){
* str_after = s;//如果不想要分割的字符串可以这样写,* str_after = s+strlen(strsubstring);
**str_before = '\0';
//被分割的字符串刚好位于字符串的结尾部分
}else if (strlen(s)==strlen(strsubstring)){
int i = 0;
int flag = (strlen(str) - strlen(s));
while (i < flag){
*(*str_before + i) = *(str + i);//如果能理解这句代码的含义,说明真正弄懂了二级指针的含义
i++;
}
**str_after = '\0';
//被分割的字符位于字符串的字符串的中间
}else{
* str_after = s;
int i = 0;
int flag = (strlen(str) - strlen(s));
while (i < flag){
*(*str_before + i) = *(str + i);
i++;
}
}

}
}


接下来,我们主要讨论一下这句代码的含义*(*str_before + i) = *(str + i),当时为了写出这句代码,我花了一下午的时间去理解跨函数使用二级指针。当时,就一直在想使用二级指针的时候**str = 'a';可以这样赋值,那我能不能通过数组去赋值呢?当时,我是这样写的**(str_before+i) = *(str+i),然后就会一直报异常,最后通过调试发现*str_before[0]可以正常赋值,而到了*str_before[1]的时候就会报异常,这是为什么呢?后面,我就仔细想了一想这句代码的含义。二级指针str_before,下面画个图方便理解



上面的图就是str_before的一个二级指针图,对于指针问题,如果不是很清楚的时候,画图理解是一个很好的方法。其中以0X开头的是内存单元的一个8位十六进制的地址。我上面的图画的不是很标准,因为在计算机中内存是一个字节大小的存储单元,也就是8位。而像0XAABB33DD这个值需要四个字节来存储,而地址一般是存的首地址。通过上面的图,我们再思考一下为什么*str_before[1]会报异常呢?因为,在c语言中[]的优先级要高于*,这样的话,*str_before[1]的意思就是,0XAABB33DD这个单元的第二个数据,而str_before[0]等于0XAABBEE11,str_before[1]这个值根本就不存在,所以导致报异常,如果改成这样(*str_before)[1]就没有问题了。现在,再来解释一下*(*str_before+i),*str_before指的就是0XAABB33DD这个单元,*str_before指向的就是0XAABBEE11中的第一个
字符,*str_before+1指向的就是0XAABBEE11中的第二个字符。而**str_before就等于字符a,*(*str_before+1)就等于字符b。最后,说一下个人感悟,作为一个程序员肯定是会遇到问题,在遇到问题的时候,怎么去高效的解决问题,这就体现了一个程序员的能力。我当时,遇到这个问题的时候,就只是想着在网上一顿乱搜,最后也没能找到答案,时间也浪费了,问题还没解决。我觉得,当我们遇到问题的时候,首先应该静下来仔细考虑想想整个程序的逻辑是怎么样的,理解每句代码究竟是什么意思。然后,再去找问题在哪,这样才能提高一个程序员解决问题的能力。最后,如果实在想不出来的话,可以请教他人,再想想自己还有那些知识点没弄明白,然后再去补短,这样才能有所提高。上面纯属个人见解,如有问题尽请留言。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: