C语言的指针传递,指针的指针的问题,谁能帮我分析分析这个问题?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言的指针传递,指针的指针的问题,谁能帮我分析分析这个问题?相关的知识,希望对你有一定的参考价值。

首先是一段源码
#include<stdio.h>
void main()
int a[5]=1,9,7,3,11;
int *num[5]=&a[0],&a[1],&a[2],&a[3],&a[4];
int n=5,i=0;
void sort(int *num[],int n);
void print(int *num[],int n);
sort(num,n);
print(num,n);
printf("原数列是:");
for(i=0;i<n;i++)
printf("%d ",*(a+i));
printf("\n");

void sort(int *num[],int n)

int i,j,k;
int *temp=0;
for(i=0;i<n-1;i++)
k=i;
for(j=i+1;j<n;j++)
if(*num[k]>*num[j]) k=j;
if(k!=i)
temp=num[k];num[k]=num[i];num[i]=temp;



void print(int *num[],int n)

int i,**p;
p=num;
for(i=0;i<n;i++,p++)
printf("%d ",**p);
printf("\n");

sort是排序函数,让数列从小到大排序的,这个程序完全能运行且没有任何error or warning。
我的问题来了。。。。:先说sort函数,这个函数传递的指针是num,实参指针传递给形参指针不是 值传递吗?值传递的形参 在该函数里交换 为什么会影响实参? 不是只有像void sort(int **p)的函数, 调用时候 int *a; sort(&a); 才有可能改变a的内容吗?

第二个问题:关于这个自定义的print函数,函数中 p变量 是指针的指针(**p),而num不是个指针吗(*p)吗?两者为什么是相等的?我觉得第四行应该改成 p=# 才对啊?为什么不是这样?

首先,int *num[] 是指针数组 ,存放的是数组元素a[0]-a[5]的地址。
再说sort 参数,传入的是数组num的首地址 ,以及 n 要排序的元素个数。
if(*num[k]>*num[j])里 ,num[i]为对应数组元素a[i]的地址,显然*num是取a[i]的值,也就是说

*num[k]等同于a[k],不同的是,如果你传入的形式是a[i],只是单纯的形参,变量存放在堆栈中,单纯的交换了变量的值,当函数运行结束后,会释放掉堆栈,这些变量值也就被释放掉,所以实际a中内容也没有改变。如果传入的是地址,同样指针也是4字节的形式(32cpu int 地址32位,64位cpu指针就是64位的地址)存放在堆栈,但下面的交换代码里是对这些地址所在位置的值 ,即储存a的内存元素进行交换,这就是你说的a内容里面的值也被交换了,当函数运行结束后,堆栈也被释放,指针也就不存在了。但a的作用域在该函数外,即使该函数结束,也没有权利释放掉a所在内存,所以a的值被交换,就是永远的被交换了。你调用sort(&a)明显也是对地址进行操作的,也是会改变a里面的值。至于形参和实参的问题,形参永远不会影响实参的,即使形参与实参的名字相同,也不会分配到内存同一个地址,函数结束形参就随着栈的销毁而销毁,会对实参造成影响是因为是直接对某个内存地址就行了操作,直接修改了某个内存地址的值。

如果以上看不懂,那么你要好好的看下数据结构 ,做为程序员,正真要弄懂指针,必须也要明白内存中是怎么存放的。虽然操作系统的mmc内存管理使编程员很少直接对内存去操作,而高级语言变量在内存的存放又被编译器做了,但有些东西不知道,你就无法理解,更别谈编程了。
何况现在很少的学校有开设数据结构这门课。。我也很纳闷,数据结构和算法导论都不学的,何谈编程,何谈程序员。。。。。。

对c指针的讲解,建议你看下c和指针 ,还是不错的一本书,前面都是基础,可以忽略,后面涉及指正的部分还是很精华的。

至于第2个问题:这是数组指针里面的东西了,想要弄懂就去看我说的那本书吧。我简单说下:
int a[10];
int *p ;
p=a; 这你应该懂 ,就是普通的指针。*p就是 a 数组的首地址a[0]。*(p+1)就是a[1];

*p[10],可以理解为数组p[10],每个元素均为指针,即这个数组存放的元素也均为指针 ,
这里有10个数组a[10],b[10],c[10].....
p[0]=a,
p[1]=b,
............
p[9]=c,依次元素p[0]-p[9]均为指针 分别指向10个数组,即a,b,c.......
因此 *p 代表p数组的p[0]元素,不懂参考上面的普通指针
*(p+1)代表p[1]元素,*(p+2) 就是p[2].
但这只是寻址p 怎么寻址a呢
p[0]就是a p[1]就是b ......
所以 *p 是a 那么**p就是 *(p[0]) 就代表 a[0] *(*p+1) 就是 *(p[0]+1)代表a[1]
*(p+1)是b 那么 **(p+1)就是*(p[1]) 是b[0] *(*(p+1)+1) 就是*(*p[1]+1))就是b[1]
这里懂了 那么上面你的问题就很简单了

但是不幸的是,这只是一维数组指针。。。。指针还涉及到 函数指针,多维数组的指针,等等,灵活运用指针可以很容易的设计程序结构,使得代码也更紧凑,所以说指针是c的精华,想要弄懂确实不易,但真正明白本质,“指针即地址”这句话,你再回头看这些,其实也不难,而且很简单。
总之就是多看代码,多写代码 ,多看书,祝君好运!!!追问

你写的很详细,谢谢!
先说第二个问题把:我的理解就是 定义数组 数组名 就相当于 一个指针了 例如num,a; 而num里的元素每一个又都是个 指针 并指向 不同的元素,所以 单独说 num 就等于 "指针的指针"(和int **p;的 性质差不多) 这么理解对把?

追答

  你觉的怎么容易理解就怎么理解吧
  int **p; 指向指针的指针,
  int *num[];
  p=num;既然编译器不报错,那么你也可以理解为num是一个二级指针。
  如果是int num[],肯定要加取地址符 p=&num;才会通过。

  其实再普通点
  int *p,num[10];
  p=num;
  一个指针,一个数组名,说num 是一个指针也不对,区别还是挺大的。

参考技术A 数组的名称就是数据的起始地址,也就是一个指针。所以数组的指针就是指针的指针。
所以,第一个问题:传递的是指针的指针,会影响实参。
第二个问题:指针就是一个32位的无符号整型,只要是这种类型的都可以赋值。int **p与num都是指向int型的指针的指针。追问

你说的有道理,第二个问题:传递的是指针的指针 所以 相等 并且输出没有任何问题。
但是第一个问题 能在解释解释吗? 为什么他的 num的元素改变了, 但是原数列a 没变?

追答

num[0] = &a[0];
num[1] = &a[1];

现在交换num[0]和num[1],交换后
num[0] = &a[1];
num[1] = &a[0];
数组a会改变吗?数组a不会有任何变化。

参考技术B 形参会在调用时再申请内存空间,那么实参和形参是不在一个内存地址中,所有改变形参,那么实参是不会改变的。
不在一个内存空间中,怎么互相影响?
用指针也是不会改变的,这个你要这么理解,指针不会改变,但是改变的是指针指向的内存空间中的数据
这个你可以验证一下,在主函数中在调用前和调用后打印指针的内容(不是指针指向的内存空间的内容),不带星号的那种
你看看指针是否改变

在函数调用当中,指针是起一个桥梁的作用追问

你没理解我的意思, 我的想法 就和你一样 形参改变 不会影响实参, 但是你 试一试这个函数,sort里是形参调换, 但是输出的时候 居然对 实参有影响!!

追答

你可以试试这个代码,
void swap(int *a,int *b)

int t;
printf("%d\n%d\n",a,b); //交换前ab
t=*a;*a=*b;*b=t;
printf("%d\n%d\n",a,b); //交换后ab

简单的交换代码
主函数代码
main()

int x=2,y=5;
int *m=&x,*n=&y;
printf("%d\n%d\n",m,n); //调用前mn
swap(m,n); //调用
printf("%d\n%d\n",m,n); //调用后mn


你可以吧这四个printf做一下标记
看看调用前后mnab的变化

参考技术C 第一个问题:void sort(int *num[],int n)要求传的是一个数组指针,就是说他是个数组,里面的值都是些指针(地址)。 int *num[5]=&a[0],&a[1],&a[2],&a[3],&a[4];这句话定义的就是数组指针,里面放的就是些地址值(即指针),你可以简单的想象成(其实就是这么简单)普通的数组数组。sort函数里之所以可以改变值是因为temp=num[k];num[k]=num[i];num[i]=temp;,他们交换了数组中值的顺序,比如&a[4]a[1]&a[2]&a[3]&a[0,这样当你后面print中按顺序输出的时候 就按顺序输出了。
第二个问题:可以这样理解p(存放地址指向数组地址)------------>数组地址(拿到数组元素后,发现数组元素也都是些地址)------->就可以取到数组中元素所在地址的内容,所以要**p。
你可以试试void print(int *num[],int n)

int i,*p;
p=num;
for(i=0;i<n;i++,p++)
printf("%d ",*(*p));
printf("\n");

你可以试试这样也学也是可以的。我身边没有c编译器。追问

第二个问题理解了,但是第一个问题不懂啊, 形参 和 实参 不是相对独立的吗, 形参改变了 怎么实参也跟着变? 我有点弄混了

追答

确实是相对独立的。
但是,如果你给形参传一个地址值(也就是说这个值是个地址),这样实参和形参就都指向那个内容了(注意:这个时候是独立的,实参手里拿着个地址指向内容,形参手里也拿着个地址指向内容),这样任何一方修改了地址指向的内容,另一方指向的内容都会改变,因为他们指向的是同一个内容。
但是如果你给形参传的就是一个值,这个时候依然是独立的,这时实参手里拿着个内容,形参手里拿着个内容,这时候是两份内容。。。。语言表达能力有限,讲个比喻吧。
将内容(真正的值)比喻为是个风筝,传地址值时,将我手里再拿根线分给你,这时候你和我都拿着个线指向风筝了,你把风筝往左拉,那么我手里线所指向的风筝就往中左了。传向int,float这样的值时,我是直接拿了个和我(实参)一样的风筝给你(形参),你怎么玩,都和我手里的风筝没有关系。不知道你理解了没有

C语言指针初学者 请帮我看看下面的提 为啥调用函数返回值是char型的 这样不就只能返回一个字符了吗

题目描述
读入一个实数,输出该实数的小数部分,小数部分若多余的末尾0,请去掉。如输入111111.12345678912345678900
则输出0.123456789123456789。若去掉末尾0之后小数部分为0,则输出“No decimal part”。注意该实数的位数不超过100位。
请定义并使用如下函数。
char *decimal(char *p)

将字符串p表示的实数的自小数点开始的小数部分存入一个字符串,并由函数返回,若p为“123.456”,则返回的字符串为“.456”。若小数部分为0,返回空指针NULL。


输入
输入一个实数。不超过100位。
输出
输出小数部分,输出占一行。
样例输入
111111.12345678900012345678900

样例输出
0.123456789000123456789

提示

来源

返回值不是char类型,而是指向char类型的指针

这样做的目的在于,由于各个数字变量类型存在表示值的上限,所以把小数当成字符串来处理,这样就不会受表示值上限的限制,比如你输入一个100位的小数,不管你用double还是用float类型,都表示不了,只能用字符串的形式来处理

#include "stdio.h"
#include "string.h"
#include "stdlib.h"
char *decimal(char *p)

   int total_length=strlen(p);
   char *point=NULL,*out=NULL;
   int out_length=0;
   //去末尾的0
   while(1)
   
      if(*(p+total_length-1)!='0')
        break;
      *(p+total_length-1)='\\0';
      total_length--;  
   
   //没有小数点,也就没有了小数部分,返回NULL
   if((point=strchr(p,'.'))==NULL)
   
     return NULL;
   
   //有小数点,但是是最后一位,说明小数部分也是0,返回NULL
   else if(*(p+total_length-1)=='.')
   
     return NULL;
   
   //这种情况下,说明有小数部分
   else
   
      out_length=total_length-(point-p);
      out=(char *)malloc(out_length+1);
      memset(out,0,out_length+1);
      strncpy(out,point,out_length);
      memset(p,0,total_length);
      strncpy(p,out,out_length);
      free(out);
      out=NULL;
      return p;
   
   
 
int main()

    char p[200]=0;
    char *q=NULL;
    printf("请输入一个小于100位的小数:\\n");
    gets(p);
    if((q=decimal(p))!=NULL)
    
      printf("0");
      puts(q);
    
    else
    
      printf("No decimal part\\n");
    

结果:

追问

基本上能看懂 请解释下有小数部分情况下中的语句好吗 不大明白
还有那个while(1) 在很多情况下见到 但是不知道到底是什么条件
我改成 while(*p!='\0') p--可以吗 能讲下吗

追答

while(1)
是个死循环
条件一直是成立的
所以需要你自己设立条件来break;
while(1)

//如果字符串最后一个字符不是'0'了,说明字符串末尾的'0',清完了,就可以中止循环了

if(*(p+total_length-1)!='0')
break;
//如果最后一个是'0',那就换成'\0'空字符,

*(p+total_length-1)='\0';
//空字符的话,字符串长度肯定就小1了。

total_length--;

这块的目的就是从字符串的最后一个字符开始反向找,看有没有'0',有的话,就换成空字符'\0'
如果最后一个不是'0'
了,那我们去掉末尾的'0'字符的目的就达到了
所以就中止这个循环了。

有小数时的情况 :
strchr的作用就是找一个字符串,某个特定字符的位置,我们肯定是找小数点'.'的位置
找到位置就从这个位置把小数部分复制出来。


out_length=total_length-(point-p);//小数字符串的长度
out=(char *)malloc(out_length+1);//临时动态申请内存来存小数
memset(out,0,out_length+1);//先把临时空间清0
strncpy(out,point,out_length);//把小数从p中复制到out中,point指向的是小数点的位置
memset(p,0,total_length);//把原字符串p清空
strncpy(p,out,out_length);//把已经存入临时空间的小数部分存回原字符串
free(out);//释放动态申请的内存,防止内存溢出
out=NULL;//指针设为空
return p;//现在p中存的就是小数部分了(小数点开头),返回

参考技术A 返回值不是char型 是char*的 返回字符串

追问
返回的是一个指针的地址是吗 那要怎么样才能输出从返回的指针的地方输出后面的字符串呢 比如就那这道题来说

//////////////
字符串以\0为结束标注,遍历字符串直到遇见\0
参考技术B 你好,返回值是char*,也就是char型的指针,并不是char型的变量。char型的指针是指向一个字符串的某一个字符的地址,他就可以通过对这个指针的操作来访问这个字符串了。追问

返回的是一个指针的地址是吗 那要怎么样才能输出从返回的指针的地方输出后面的字符串呢 比如就那这道题来说

以上是关于C语言的指针传递,指针的指针的问题,谁能帮我分析分析这个问题?的主要内容,如果未能解决你的问题,请参考以下文章

谁能帮我说下C语言中的堆栈

指针减去数组和变量中的指针

你能帮我理解指针和地址吗?

我在自己写一个STL,其中的list使用双向链表的,谁能帮我写一个迭代器。

c语言关于指针引用的问题

空指针异常 - findViewById()