关于变量、1/2级指针等形式的形参传递
2014-04-29 21:49
337 查看
在学习数据结构(二叉树的二叉链表)时遇到的一些问题,关于变量、一级指针、二级指针等形式的形参传递,这里做一简要总结。
其实说了那么多,最主要的只是想强调例11和例17,其中最直接的目的就是为了说明例17。但为了把问题说清楚,这里还是一步一步按照调试的思路写了下来。
后来总结了一下,主要有以下几点:
1. 若在main()中定义int类型变量val,则可以定义形参为一级指针形式的函数(如fun(int *dat)),通过调用函数fun(&val)来改变变量val的值。
2. 若在main()中定义int类型的一级指针变量*val,则可以在main()中初始化该指针,使其指向确定的int类型的变量或内存,例如"int a, *val = &a;"或者"int *val = (int *)malloc(sizeof(int));"都是可以的,在这之后便可以在函数fun(int *dat)中来操作(赋值)该指针变量了,调用形式为fun(val)。
(此时没有必要在fun中调用malloc函数使指针变量val指向新的内存,因为这根本就办不到,此时在fun中的malloc函数只会改变形参dat中的内容,而不会改变val中的内容。)
3. 若在main()中定义int类型的一级指针变量*val,但不想在main()中初始化该指针,而是想在其他的函数中初始化该指针,则应当定义形参为二级指针形式的函数(如fun(int **dat)),通过在fun函数中来初始化该指针变量(在fun中通过malloc函数初始化该指针变量),调用形式为fun(&val),在初始化完之后便可以操作(赋值)该变量了。
>>>1
>>>2
若在上述程序的printf("%d\n",tmp)前加一条语句:printf("%d\n",&tmp),即如下,
(对于1、2这两处,难道不奇怪吗?)
若将int tmp放在main()函数外部定义(全局变量),则情况又不同了。
>>>3
>>>4
(对比1、2、3、4,难道不神奇吗?)
>>>5
若将上述程序改为如下所示,则编译无错有警告,警告内容同上,运行也不会输出任何东西,且会弹出如上的对话框。
>>>6
然而若将程序改为:
显然这两个输出结果是main函数中的前两条语句的执行结果,当执行到第三条语句的时候,会弹出图中的对话框。
>>>7
若定义的是全局变量指针,如下,
然而若将上述程序改为:
>>>8
>>>9
>>>10
若将第9个程序改为如下,则编译无错也无警告。
若将该程序中Test()函数改变成如下形式,
这说明Test(ptr)函数的确可以得到指针变量ptr中的内容(即变量a的地址), 并见其传递给形参n。此时这个形参所指地址即是ptr所指地址,在函数Test()中可以向该形参所指地址赋值,但并不能改变该形参所指地址。而行参本身只是一个中间变量,它可以接收Test(ptr)函数传递过来的地址,在函数Test()中它也可以接受函数malloc()重新分配的内存的地址,显然Test(ptr)传递进来的地址和在Test()中malloc()分配的地址是两个不同的地址。
>>>11
综上所述,若在main()函数只定义指针变量,并不对其初始化(即不使其指向确定的存储空间),而是想在函数Test()中对对该指针变量进行初始化,则应该将Test()函数的形参定义为二级指针,如下所示。若想在脑海中快速想象通过二级指针传递参数的指向问题,估计还得需要时间去悟吧,不过自己也能够在纸上借助画图来分析。
>>>12
上述程序编译无错无警告,运行结果如下。
上述程序是在main中定义变量a,但不对其赋初值,而是在函数TestInt()中对其赋初值。
>>>13
若12中的TestInt()函数的形参不是一级指针,而是二级指针,即将程序改成如下形式。这里的函数TestInt1()实际上类似例11中的Test()函数的转型。
例11中的Test()函数是调用main()中定义的指针,而TestInt1则是调用main()中定义的变量。
上述程序运行结果如下。
说明:虽然这时候函数也能够给变量a赋初值,但实际应用中最好不要这样用,因为TestInt1(int **n)的形参是二级指针,而TestInt1(&a)得到的实参&a是一级指针。
>>>14
>>>15
该程序啥也没干,说明指针变量a此时根本就不能够被函数TestStruct1()调用。
若在main中对变量a初始化(程序如下),使其指向正确的地址,则编译就无警告了,且运行也正确了。
>>>16
在例15的基础上,将程序改为如下形式就对了。
说明:此时TestStruct1()函数中的malloc函数中的第一条语句“p = (SamplePtr)malloc(sizeof(Sample));”始终是被注释着的,因为如果在main中定了Sample类型的变量或者Sample类型的一级指针变量(定义成指针时当然要对其初始化,使其指向确定的地址,当然该地址要正确,像NULL这样的肯定不行),此时变量有(或指向)确定的存储空间,在TestStruct1()函数中重新为其分配新的地址是不行的,语句“p = (SamplePtr)malloc(sizeof(Sample));”改变的只是形参p的内容,这个道理与例10相同
。
>>>17
若现在非要在main()中定义一级指针变量,而在函数中初始化该指针,则应当将函数的形参定义为二级指针。
其实说了那么多,最主要的只是想强调例11和例17,其中最直接的目的就是为了说明例17。但为了把问题说清楚,这里还是一步一步按照调试的思路写了下来。
后来总结了一下,主要有以下几点:
1. 若在main()中定义int类型变量val,则可以定义形参为一级指针形式的函数(如fun(int *dat)),通过调用函数fun(&val)来改变变量val的值。
2. 若在main()中定义int类型的一级指针变量*val,则可以在main()中初始化该指针,使其指向确定的int类型的变量或内存,例如"int a, *val = &a;"或者"int *val = (int *)malloc(sizeof(int));"都是可以的,在这之后便可以在函数fun(int *dat)中来操作(赋值)该指针变量了,调用形式为fun(val)。
(此时没有必要在fun中调用malloc函数使指针变量val指向新的内存,因为这根本就办不到,此时在fun中的malloc函数只会改变形参dat中的内容,而不会改变val中的内容。)
3. 若在main()中定义int类型的一级指针变量*val,但不想在main()中初始化该指针,而是想在其他的函数中初始化该指针,则应当定义形参为二级指针形式的函数(如fun(int **dat)),通过在fun函数中来初始化该指针变量(在fun中通过malloc函数初始化该指针变量),调用形式为fun(&val),在初始化完之后便可以操作(赋值)该变量了。
>>>1
void main() { int tmp; printf("%d\n",tmp); }上述程序编译时会有警告,说是tmp没有被初始化。运行该程序后,不输出任何东西,且会弹出如下对话框。
>>>2
若在上述程序的printf("%d\n",tmp)前加一条语句:printf("%d\n",&tmp),即如下,
void main() { int tmp; printf("%d\n",&tmp); printf("%d\n",tmp); }这时编译便不会报错,也不会有警告,运行时也不会弹出任何对话框,且会有输出,输出如下。
(对于1、2这两处,难道不奇怪吗?)
若将int tmp放在main()函数外部定义(全局变量),则情况又不同了。
>>>3
int tmp; void main() { printf("%d\n",tmp); }对于上述程序,编译时无错无警告,运行也正常,运行结果如下。
>>>4
int tmp; void main() { printf("%d\n",&tmp); printf("%d\n",tmp); }对于上述程序,编译无错无警告,运行同样正常,运行结果如下。
(对比1、2、3、4,难道不神奇吗?)
>>>5
void main() { int *ptr; printf("%d\n",ptr); }上述程序,编译时无错但有警告,警告指针ptr没有被初始化。运行不会输出任何东西,且会弹出如下对话框。
若将上述程序改为如下所示,则编译无错有警告,警告内容同上,运行也不会输出任何东西,且会弹出如上的对话框。
void main() { int *ptr; printf("%d\n",*ptr); }
>>>6
然而若将程序改为:
void main() { int *ptr; printf("%d\n",&ptr); printf("%d\n",ptr); printf("%d\n",*ptr); }则编译无错无警告。运行时会有输出,但也会弹出对话框,如下所示。
显然这两个输出结果是main函数中的前两条语句的执行结果,当执行到第三条语句的时候,会弹出图中的对话框。
>>>7
若定义的是全局变量指针,如下,
int *ptr; void main() { printf("%d\n",*ptr); }则编译无错无警告。但运行时不输出任何东西,且会弹出如下对话框。
然而若将上述程序改为:
int *ptr; void main() { printf("%d\n",&ptr); printf("%d\n",ptr); printf("%d\n",*ptr); }则编译也无错无警告。运行时会有输出,但还会弹出对话框,如下图所示。
>>>8
void Test(int *n) { n = NULL; n = (int *)malloc(sizeof(int)); *n = 100; } void main() { int *ptr; Test(ptr); printf("%d\n",&ptr); printf("%d\n",ptr); printf("%d\n",*ptr); }对于上述程序,编译无错有警告,警告指针变量ptr没有被初始化。运行时不输出任何结果,但会弹出如下图的对话框。
>>>9
void main() { int a; int *ptr = &a; printf("a = %d\n",a); printf("&a = %d\n",&a); printf("&ptr = %d\n",&ptr); printf("ptr = %d\n",ptr); printf("*ptr = %d\n",*ptr); }对于上述程序,编译时无错也无警告,运行有结果输出,且这次不会弹出对话框。
>>>10
若将第9个程序改为如下,则编译无错也无警告。
void Test(int *n) { printf("n = %d\n",n); n = NULL; n = (int *)malloc(sizeof(int)); printf("n = %d\n",n); *n = 100; } void main() { int a; int *ptr = &a; Test(ptr); printf("a = %d\n",a); printf("&a = %d\n",&a); printf("&ptr = %d\n",&ptr); printf("ptr = %d\n",ptr); printf("*ptr = %d\n",*ptr); }该程序运行结果如下。
若将该程序中Test()函数改变成如下形式,
void Test(int *n) { printf("n = %d\n",n); //n = NULL; //n = (int *)malloc(sizeof(int)); //printf("n = %d\n",n); *n = 100; }则上述程序运行结果就不一样了,如下所示。
这说明Test(ptr)函数的确可以得到指针变量ptr中的内容(即变量a的地址), 并见其传递给形参n。此时这个形参所指地址即是ptr所指地址,在函数Test()中可以向该形参所指地址赋值,但并不能改变该形参所指地址。而行参本身只是一个中间变量,它可以接收Test(ptr)函数传递过来的地址,在函数Test()中它也可以接受函数malloc()重新分配的内存的地址,显然Test(ptr)传递进来的地址和在Test()中malloc()分配的地址是两个不同的地址。
>>>11
综上所述,若在main()函数只定义指针变量,并不对其初始化(即不使其指向确定的存储空间),而是想在函数Test()中对对该指针变量进行初始化,则应该将Test()函数的形参定义为二级指针,如下所示。若想在脑海中快速想象通过二级指针传递参数的指向问题,估计还得需要时间去悟吧,不过自己也能够在纸上借助画图来分析。
void Test(int **n) { printf("\n通过调用Test(&ptr)函数传递地址给形参n:\n"); //printf("**n = %d\n",**n); //调用该语句时,运行时会弹出内存不能被读的对话框。 printf("*n = %d\n",*n); printf("n = %d\n",n); printf("&n = %d\n",&n); *n = NULL; *n = (int *)malloc(sizeof(int)); printf("\n在Test()函数中,通过malloc()函数分配地址给形参n:\n"); printf("**n = %d\n",**n); printf("*n = %d\n",*n); printf("n = %d\n",n); printf("&n = %d\n",&n); **n = 100; } void main() { int *ptr; printf("指针变量ptr初始化之前\n"); printf("&ptr = %d\n",&ptr); printf("ptr = %d\n",ptr); Test(&ptr); printf("\n指针变量ptr初始化之后\n"); printf("&ptr = %d\n",&ptr); printf("ptr = %d\n",ptr); printf("*ptr = %d\n",*ptr); }上述程序编译无错无警告,执行结果如下。
>>>12
void TestInt(int *n) { printf("&n = %d\n",&n); printf("*n = %d\n",*n); printf("n = %d\n",n); *n = 100; } void main() { int a; printf("%d\n",&a); printf("%d\n",a); TestInt(&a); printf("%d\n",&a); printf("%d\n",a); }
上述程序编译无错无警告,运行结果如下。
上述程序是在main中定义变量a,但不对其赋初值,而是在函数TestInt()中对其赋初值。
>>>13
若12中的TestInt()函数的形参不是一级指针,而是二级指针,即将程序改成如下形式。这里的函数TestInt1()实际上类似例11中的Test()函数的转型。
例11中的Test()函数是调用main()中定义的指针,而TestInt1则是调用main()中定义的变量。
void TestInt1(int **n) { printf("&n = %d\n",&n); //printf("*n = %d\n",**n); //这条语句编译时不会有错,但运行时会提示有内存不能被读。 printf("*n = %d\n",*n); printf("n = %d\n",n); *n = 100; printf("&n = %d\n",&n); //printf("*n = %d\n",**n); //这条语句编译时不会有错,但运行时会提示有内存不能被读。 printf("*n = %d\n",*n); printf("n = %d\n",n); } void main() { int a; printf("%d\n",&a); printf("%d\n",a); TestInt1(&a); printf("%d\n",&a); printf("%d\n",a); }上述程序编译无错但有3个警告,如下图所示。实际上不能够通过TestInt1(&a)这样来执行的,这里只是为了学习而实验的。
上述程序运行结果如下。
说明:虽然这时候函数也能够给变量a赋初值,但实际应用中最好不要这样用,因为TestInt1(int **n)的形参是二级指针,而TestInt1(&a)得到的实参&a是一级指针。
>>>14
typedef struct Node { int data; struct Node *ptr; }Sample,*SamplePtr; void TestStruct(SamplePtr p) { p->data = 100; p->ptr = (SamplePtr)malloc(sizeof(Sample)); } void main() { Sample a; TestStruct(&a); printf("%d\n",a.data); printf("%d\n",a.ptr); }上述程序编译无错无警告,运行结果如下。
>>>15
void TestStruct1(SamplePtr p) { //p = (SamplePtr)malloc(sizeof(Sample)); //p->data = 100; //p->ptr = (SamplePtr)malloc(sizeof(Sample)); } void main() { Sample *a; TestStruct1(a); //printf("%d\n",a->data); //printf("%d\n",a->ptr); }上述程序编译有一个警告无错误,警告是说指针变量a没有被初始化,其运行结果无输出,且会弹出对话框,如下所示。
该程序啥也没干,说明指针变量a此时根本就不能够被函数TestStruct1()调用。
若在main中对变量a初始化(程序如下),使其指向正确的地址,则编译就无警告了,且运行也正确了。
void TestStruct1(SamplePtr p) { //p = (SamplePtr)malloc(sizeof(Sample)); //p->data = 100; //p->ptr = (SamplePtr)malloc(sizeof(Sample)); } void main() { Sample b, *a = &b; //或者不定义变量b,而是通过函数malloc()为指针a赋初始值,即a = (SamplePtr)malloc(sizeof(Sample)); TestStruct1(a); //printf("%d\n",a->data); //printf("%d\n",a->ptr); }
>>>16
在例15的基础上,将程序改为如下形式就对了。
typedef struct Node { int data; struct Node *ptr; }Sample,*SamplePtr; void TestStruct1(SamplePtr p) { //p = (SamplePtr)malloc(sizeof(Sample)); p->data = 100; p->ptr = (SamplePtr)malloc(sizeof(Sample)); } void main() { Sample b, *a = &b; TestStruct1(a); printf("%d\n",a->data); printf("%d\n",a->ptr); printf("%d\n",b.data); printf("%d\n",b.ptr); }上述程序编译无错无警告,运行结果如下。
说明:此时TestStruct1()函数中的malloc函数中的第一条语句“p = (SamplePtr)malloc(sizeof(Sample));”始终是被注释着的,因为如果在main中定了Sample类型的变量或者Sample类型的一级指针变量(定义成指针时当然要对其初始化,使其指向确定的地址,当然该地址要正确,像NULL这样的肯定不行),此时变量有(或指向)确定的存储空间,在TestStruct1()函数中重新为其分配新的地址是不行的,语句“p = (SamplePtr)malloc(sizeof(Sample));”改变的只是形参p的内容,这个道理与例10相同
。
>>>17
若现在非要在main()中定义一级指针变量,而在函数中初始化该指针,则应当将函数的形参定义为二级指针。
typedef struct Node { int data; struct Node *ptr; }Sample,*SamplePtr; void TestStruct1(SamplePtr *p) { *p = (SamplePtr)malloc(sizeof(Sample)); (*p)->data = 100; (*p)->ptr = (SamplePtr)malloc(sizeof(Sample)); } void main() { Sample *a; TestStruct1(&a); printf("%d\n",a->data); printf("%d\n",a->ptr); }上述程序编译无错无警告,运行结果如下。
相关文章推荐
- C++ 引用变量(十二)-- & b *b和C的传递指针形参一样效果
- C++ 构造函数使用 ":成员变量(形参)" 的形式给类里面成员变量赋值,如果成员变量和形参是指针,那么需要注意的事项
- 关于指针作为函数参数传递的理解,对比普通变量作为函数参数的需注意点,其实就是行参和实参的问题。
- 关于二维数组做函数参数,传递形参的形式的问题
- C++,关于指针形参”值传递VS地址传递“的问题
- 指针用于将函数中形参的值传递给实参
- 再开一篇关于C++程序设计基核心之一:引用,指针,符号优先级,函数参数传递
- [c语言笔记]关于指针操作中使用异或交换两个变量的值
- 关于多线程中传递MFC窗口类指针时ASSERT_VALID出错的另类解决
- 关于二维数组与二维指针传递参数
- 我的C++学习日记——关于const数值变量,指针和引用的理解
- C语言中 数组作为函数形参传递相当于指针,在函数中不能得到数组长度,只能得到指针长度4
- [指针一]结构体数组作为形参传递,下标访问和指针访问使用该结构体
- 将指针指向的内容以字符串的形式传递出来
- C/C++中关于地址、指针和引用变量的学习笔记(十) : 引用型变量
- 关于C/C++中指针做形参的一点小分析(转)
- 一个讨论引发关于js中函数声明,函数表达式,形参与变量声明赋值引发的一些事(http://www.cnblogs.com/zhouyongtao/archive/2012/11/22/2783089)
- 无法把指针变量本身传递给一个函数
- 指针和引用作为形参传递下去的区别
- 关于this指针的传递问题总结