关于变量、1/2级指针等形式的形参传递

2024-06-06 06:08

本文主要是介绍关于变量、1/2级指针等形式的形参传递,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在学习数据结构(二叉树的二叉链表)时遇到的一些问题,关于变量、一级指针、二级指针等形式的形参传递,这里做一简要总结。

其实说了那么多,最主要的只是想强调例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);
}
上述程序编译无错无警告,运行结果如下。





这篇关于关于变量、1/2级指针等形式的形参传递的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1035291

相关文章

变量与命名

引言         在前两个课时中,我们已经了解了 Python 程序的基本结构,学习了如何正确地使用缩进来组织代码,并且知道了注释的重要性。现在我们将进一步深入到 Python 编程的核心——变量与命名。变量是我们存储数据的主要方式,而合理的命名则有助于提高代码的可读性和可维护性。 变量的概念与使用         在 Python 中,变量是一种用来存储数据值的标识符。创建变量很简单,

如何在页面调用utility bar并传递参数至lwc组件

1.在app的utility item中添加lwc组件: 2.调用utility bar api的方式有两种: 方法一,通过lwc调用: import {LightningElement,api ,wire } from 'lwc';import { publish, MessageContext } from 'lightning/messageService';import Ca

【C++学习笔记 20】C++中的智能指针

智能指针的功能 在上一篇笔记提到了在栈和堆上创建变量的区别,使用new关键字创建变量时,需要搭配delete关键字销毁变量。而智能指针的作用就是调用new分配内存时,不必自己去调用delete,甚至不用调用new。 智能指针实际上就是对原始指针的包装。 unique_ptr 最简单的智能指针,是一种作用域指针,意思是当指针超出该作用域时,会自动调用delete。它名为unique的原因是这个

C语言指针入门 《C语言非常道》

C语言指针入门 《C语言非常道》 作为一个程序员,我接触 C 语言有十年了。有的朋友让我推荐 C 语言的参考书,我不敢乱推荐,尤其是国内作者写的书,往往七拼八凑,漏洞百出。 但是,李忠老师的《C语言非常道》值得一读。对了,李老师有个官网,网址是: 李忠老师官网 最棒的是,有配套的教学视频,可以试看。 试看点这里 接下来言归正传,讲解指针。以下内容很多都参考了李忠老师的《C语言非

JAVA基础:值传递和址传递

1 值传递和址传递 值传递 方法调用时,传递的实参是一个基本类型的数据 形参改变,实参不变 public static void doSum(int num1,int num2){}main(){doSum(10,20);int i = 10 ;int j = 20 ;doSum(i,j) ;}   public static void t1(int num){num = 20

C和指针:字符串

字符串、字符和字节 字符串基础 字符串就是一串零个或多个字符,并且以一个位模式为全0的NUL字节结尾。 字符串长度就是字符串中字符数。 size_t strlen( char const *string ); string为指针常量(const修饰string),指向的string是常量不能修改。size_t是无符号数,定义在stddef.h。 #include <stddef.h>

【C++】作用域指针、智能指针、共享指针、弱指针

十、智能指针、共享指针 从上篇文章 【C++】如何用C++创建对象,理解作用域、堆栈、内存分配-CSDN博客 中我们知道,你的对象是创建在栈上还是在堆上,最大的区别就是对象的作用域不一样。所以在C++中,一旦程序进入另外一个作用域,那其他作用域的对象就自动销毁了。这种机制有好有坏。我们可以利用这个机制,比如可以自动化我们的代码,像智能指针、作用域锁(scoped_lock)等都是利用了这种机制。

JS_变量

二、JS的变量 JS中的变量具有如下特征 1 弱类型变量,可以统一声明成var 2 var声明的变量可以再次声明 3 变量可以使用不同的数据类型多次赋值 4 JS的语句可以以; 结尾,也可以不用;结尾 5 变量标识符严格区分大小写 6 标识符的命名规则参照JAVA 7 如果使用了 一个没有声明的变量,那么运行时会报uncaught ReferenceError: *** is not de

MFC中App,Doc,MainFrame,View各指针的互相获取

纸上得来终觉浅,为了熟悉获取方法,我建了个SDI。 首先说明这四个类的执行顺序是App->Doc->Main->View 另外添加CDialog类获得各个指针的方法。 多文档的获取有点小区别,有时间也总结一下。 //  App void CSDIApp::OnApp() {      //  App      //  Doc     CDocument *pD

C和指针:结构体(struct)和联合(union)

结构体和联合 结构体 结构体包含一些数据成员,每个成员可能具有不同的类型。 数组的元素长度相同,可以通过下标访问(转换为指针)。但是结构体的成员可能长度不同,所以不能用下标来访问它们。成员有自己的名字,可以通过名字访问成员。 结构声明 在声明结构时,必须列出它包含的所有成员。 struct tag {member-list} variable-list ; 定义一个结构体变量x(包含