值调用与传地址调用的异同
- 格式:doc
- 大小:25.50 KB
- 文档页数:3
解析CC++值传递和址传递的区别C/C++的按值传递和按地址传递有明显不同,下⾯对他们作个区别:按值传递:在调⽤函数中将原函数的值拷贝⼀份过去被调⽤的函数,在被调⽤函数中对该值的修改不会影响原函数的值。
按地址传递:在调⽤函数的时候将原函数的值所在的地址拷贝⼀份过去,被调⽤函数对这个地址所作的修改会影响原来的值。
概述:⾸先我们要知道 “a的地址”和“a地址中的内容”的区别,数据是存放在内存中的,每⼀个变量都有⼀个内存地址,变量的内容存放在对应内存地址的空间中⽐⽅说定义int a = 10;那么a在内存中的地址是0x1100,在这个地址中存储的数据是10假设创建指针p,把a的地址赋值给p,就是把a的⾸地址0x1100赋值给指针p,这个时候p的值就是变量a在内存中的⾸地址int a =10;int*p;p=&a; //把a的⾸地址赋值给P简单点的理解就好⽐你去图书馆借书,每本书都会有他的⼀个编号(地址),记录它所在的位置,⽽这个书,就是这个地址对应的内容,如果你⽤指针,得到的是这个书所对应的编号(地址) ,存储内容就是地址如果你是变量赋值,值传递,那么就相当于复印了⼀遍这个书(地址对应的内容),然后⽤⼀个新的编号(地址),去存储你复印的这本书从命名来理解所以我们就会发现:值传递,或者变量赋值,修改变量的值,修改的是新的新的编号(地址)中的内容(复印的书),不会影响到原来编号(地址)中的数据。
也就是形参是实参内容的拷贝,并不是地址的拷贝,所以改变形参的值并不会影响实参的值使⽤址传递,⽤指针修改变量的值,就是把原编号(地址)中的书给修改了,换了⼀本新的书,就相当于对实参本⾝进⾏的操作。
声明Declaration:描述在其他地⽅创建的对象,并不分配内存。
(可以出现在多个地⽅)定义Definition:产⽣⼀个新的对象,并分配内存。
(只能出现⼀次)值传递新开辟⼀个内存空间,存储原来变量的值,修改变量修改的是新的内存空间中的值。
[总结]值传递、地址传递、引⽤传递的区别值传递、地址传递、引⽤传递的区别:============================值传递好⽐是你把⽂件复制⼀份,通过⽹络传给他,然后他可以在他本机上对⽂件做任何的修改,修改会保存下来,但是你机器上的⽂件不会发⽣任何的变化。
即形参与实参是两个不同的变量,各⾃占⽤不同的存储单元。
地址传递好⽐是你把⽂件在⽹络上的地址告诉他⼈,他⼈通过⽹络访问你机器上的⽂件,他可以对⽂件进⾏修改并保存,此时,⽂件的内容就会发⽣变化。
即形参与实参是相同的变量,占⽤同⼀段内存空间。
引⽤传递是变量的别名,对别名操作就是对变量本⾝操作。
==========================================================地址传递是通过把地址传递给函数,然后函数根据地址要对存储单元操作。
打个⽐⽅说:你告诉某⼈⼀个房间号,让他根据房间号去拿去东西。
交换⼆个数的理解:值传递没有办法交换两个数。
⽤地址传递可以实现。
打个⽐⽅说: 在两个房间门⼝有两个服务⼈员,房间⾥有⼀本书和字典。
现在要求交换这两件东西。
服务员就是地址,如果找到服务员,然后让她把东西拿出来和别⼀个房间交换。
这样达到了交换的⽬的。
地址传递能够实现交换的⽬的。
另⼀种⽅法:在两个服务⼈员中还有⼀个⼈是管理⼈员,如果管理⼈员让这两个服务员换⼀下位置,这样以后按服务员找各⾃房间的东西,也起到了交换的⽬的。
这是通过指针的指针来实现的。
以上⼆种情况是效率最⾼提第⼆种了。
第⼀种是交换房间的东西,⼈没有换。
第⼆种是交换⼈,东西没有换。
从以可以感性的知道,⽤指针操作会更快。
例⼦:void GetMemory(char *p, int num){p = (char *)malloc(sizeof(char) * num);}void Test(void){char *str = NULL;GetMemory(str, 100); // str 仍然为 NULLstrcpy(str, "hello"); // 运⾏错误}这种情况下: p=str; 是值传递。
值传递和引用传递的概念
在编程中,值传递和引用传递是两个重要的概念,它们涉及数据在程序中如何被传递和处理。
值传递指的是在函数调用过程中,将实参的值复制一份给形参,然后在函数内部使用这份副本进行操作。
这意味着函数对形参进行任何修改都不会影响实参。
在值传递中,变量本身的位置没有改变,只是通过复制和传递来完成操作。
值传递通常用于基本数据类型和结构体等值类型。
引用传递则是将实参的地址作为形参进行传递,在函数内部直接对实参进行修改。
这意味着在函数调用过程中,对形参所做的任何修改都会反映在实参中。
在引用传递中,变量本身的位置发生了改变,实参和形参指向同一个内存地址。
引用传递通常用于数组、指针、对象等引用类型。
值传递和引用传递是编程中非常基础的两个概念,它们分别适用于不同类型的变量和特定的编程场景。
了解它们的区别和如何使用它们,有助于编写高效和安全的代码。
值传递、指针传递、引用传递的区别C++中值传递、指针传递、引用传递的总结收藏1. 值传递:形参是实参的拷贝,改变形参的值并不会影响外部实参的值。
从被调用函数的角度来说,值传递是单向的(实参->形参),参数的值只能传入,不能传出。
当函数内部需要修改参数,并且不希望这个改变影响调用者时,采用值传递。
vo id sw ap(in t a,i nt b){ in t tem p; te mp=a; a=b; b=te mp;c out<<a<<‟…<<b<<‟\n‟;}int m ain(){ int x=1;int y=2; sw ap(x,y); co ut<<x<<‟ …<<y<<‟\n‟;retur n 0;}用g db调试后发现,x,y的地址分别是0xf fbef938, 0xffbe f934,值分别是1,2。
而形参a,b的地址分别是0xff bef918,0xf fbef914, 虽然它们存储的值和x,y一样,都是1,2,但是这只是拷贝过来的。
swap只交换了a,b,并不会改变x,y的值。
输出为2,1;1,22. 指针传递:void swap(int*a,in t *b){ in t tem p; te mp=*a; *a=*b;*b=tem p; co ut<<*a<<‟…<<*b<<‟\n‟;}intmain(){ int x=1;int y=2; sw ap(&x,&y);cout<<x<<‟ …<<y<<‟\n‟;}输出结果是2,1;2,1。
调用函数时的三种参数传递方式(传值传引用传地址)在编程语言中,参数是在函数调用过程中传递给函数的值或变量。
参数传递方式可以分为传值、传引用和传地址三种。
1. 传值(Call by Value):参数按照值的方式进行传递,函数接收的是实际参数的一个副本。
在函数内部对参数的修改不会影响到原始的参数。
这是最常见的参数传递方式,在许多编程语言中都是默认的方式。
特点:-参数的值在函数内部是只读的,函数不会对原始的参数进行修改。
-通过副本传递参数,避免了对原始参数进行意外修改的风险。
优点:-参数的值是独立于函数之外的,可以保证函数的安全性和一致性。
-函数内部对参数的修改不会影响到原始的参数,避免了意外的副作用。
缺点:-对于较大的数据类型,由于需要复制参数的值,会消耗更多的内存和时间。
2. 传引用(Call by Reference):参数按引用的方式进行传递,函数接收的是实际参数的引用或指针。
在函数内部对参数的修改会影响到原始的参数。
在引用传递参数的语言中,使用引用的方式可以让函数修改原始参数的值。
特点:-参数在函数内部是可写的,可以对参数进行修改。
-函数通过引用访问参数,可以直接修改原始参数的值。
优点:-函数可以直接修改原始参数的值,方便了对参数的修改操作。
-不需要复制参数的值,减少了内存和时间的消耗。
缺点:-参数的值可以被函数随时修改,可能导致意外的副作用,使程序变得难以理解和调试。
-对于没有被传引用的参数,无法从函数内部访问到其值。
3. 传地址(Call by Address):参数按照地址的方式进行传递,函数接收的是实际参数的地址。
在函数内部对参数进行修改会影响到原始的参数。
传地址方式类似于传引用,不同之处在于传地址是通过参数的指针来修改参数的值。
特点:-参数在函数内部是可写的,可以对参数进行修改。
-函数使用指针访问参数,可以直接修改原始参数的值。
优点:-函数可以直接修改原始参数的值,方便了对参数的修改操作。
传送及比较指令总结传送指令是计算机系统中常用的一类指令,用于将数据从一个位置移动到另一个位置。
这些指令通常用于数据的复制、转移和重排等任务。
在本文中,我将总结一些常见的传送指令,并比较它们的异同。
1.MOV指令:MOV (Move) 指令用于将数据从一个位置复制到另一个位置。
它可以在寄存器之间、寄存器和内存之间、寄存器和端口之间进行数据的传送。
MOV 指令通常具有两个操作数,一个目标操作数和一个源操作数。
例如,`MOV AX, BX` 将寄存器 BX 中的值复制到寄存器 AX 中。
MOV 指令在大多数计算机体系结构中都是支持的,因为它是一种非常基本和常见的操作。
2.LDR和STR指令:LDR (Load) 和 STR (Store) 指令用于将数据从内存中加载到寄存器或将数据从寄存器存储到内存中。
这些指令主要在ARM架构中使用。
LDR指令将内存中的数据加载到寄存器中,例如,`LDR R0, [R1]` 将 R1 寄存器中的地址所对应的内存单元中的数据加载到 R0 寄存器中。
STR 指令则将寄存器中的数据存储到内存中,例如,`STR R0, [R1]` 将 R0 寄存器中的数据存储到 R1 寄存器中的地址所对应的内存单元中。
3.PUSH和POP指令:PUSH指令用于将数据压入栈中,而POP指令用于从栈中弹出数据。
这两个指令通常用于实现函数调用和中断处理等操作。
PUSH指令将数据放入栈顶,然后栈指针下移;而POP指令则将栈顶数据读取到寄存器中,并将栈指针上移。
例如,`PUSHAX`将AX寄存器中的数据压入栈中,`POPAX`将栈顶数据弹出到AX寄存器中。
4.XCHG指令:XCHG (Exchange) 指令用于交换两个操作数的值。
这个指令可以用于两个寄存器之间的值交换,或者寄存器和内存之间的交换。
例如,`XCHG AX, BX` 将寄存器 AX 和 BX 中的数据进行交换。
XCHG 指令常用于实现数据交换、排序和互斥访问等场景。
第一章▪什么是编译器?▪编译程序的结构分为几个阶段,各阶段的任务是什么? ▪遍、编译前端及编译后端的含义?▪编译程序的生成方式有哪些?第二章▪ 1. 写一文法,使其语言是偶正整数的集合.▪要求:(1)允许0打头 (2)不允许0打头解:(1)允许0开头的偶正整数集合的文法E→NT|DT→NT|DN→D|1|3|5|7|9D→0|2|4|6|8(2)不允许0开头的偶正整数集合的文法E→NT|DT→FT|GN→D|1|3|5|7|9D→2|4|6|8F→N|0G→D|02。
证明下述文法G[〈表达式〉]是二义的。
〈表达式>∷=a|(〈表达式〉)|〈表达式〉〈运算符>〈表达式〉〈运算符>∷=+|—|*|/解:可为句子a+a*a构造两个不同的最右推导:最右推导1 〈表达式〉⇒<表达式>〈运算符〉〈表达式〉⇒〈表达式〉〈运算符〉a⇒〈表达式〉* a⇒〈表达式〉〈运算符〉〈表达式〉* a⇒〈表达式〉〈运算符>a * a⇒〈表达式>+ a * a⇒ a + a * a最右推导2 〈表达式〉⇒<表达式>〈运算符〉〈表达式〉⇒〈表达式〉〈运算符〉<表达式〉〈运算符〉〈表达式〉⇒〈表达式><运算符〉〈表达式〉〈运算符〉 a⇒<表达式>〈运算符〉〈表达式〉* a⇒ <表达式〉〈运算符〉a * a⇒<表达式〉+ a * a⇒ a + a * a3. 给出生成下述语言的上下文无关文法:(1){ anbnambm| n,m〉=0}(2){ 1n0m1m0n| n,m〉=0}解:(1){ anbnambm| n,m>=0}S→AAA→aAb|ε(2) { 1n0m1m0n| n ,m>=0}S →1S0|A A →0A1|ε第三章1、构造一个DFA ,它接收∑={a , b }上所有满足下述条件的字符串:字符串中的每个a 都有至少一个b 直接跟在其右边。
c++值传递,指针传递,引⽤传递以及指针与引⽤的区别值传递:形参是实参的拷贝,改变形参的值并不会影响外部实参的值。
从被调⽤函数的⾓度来说,值传递是单向的(实参->形参),参数的值只能传⼊,不能传出。
当函数内部需要修改参数,并且不希望这个改变影响调⽤者时,采⽤值传递。
指针传递:形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本⾝进⾏的操作引⽤传递:形参相当于是实参的“别名”,对形参的操作其实就是对实参的操作,在引⽤传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。
被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。
正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。
举例:#include<iostream>using namespace std;//值传递void change1(int n){cout<<"值传递--函数操作地址"<<&n<<endl; //显⽰的是拷贝的地址⽽不是源地址n++;}//引⽤传递void change2(int & n){cout<<"引⽤传递--函数操作地址"<<&n<<endl;n++;}//指针传递void change3(int *n){cout<<"指针传递--函数操作地址 "<<n<<endl;*n=*n+1;}int main(){int n=10;cout<<"实参的地址"<<&n<<endl;change1(n);cout<<"after change1() n="<<n<<endl;change2(n);cout<<"after change2() n="<<n<<endl;change3(&n);cout<<"after change3() n="<<n<<endl;return true;}运⾏结果:可以看出,实参的地址为0x28ff2c采⽤值传递的时候,函数操作的地址是0x28ff10并不是实参本⾝,所以对它进⾏操作并不能改变实参的值再看引⽤传递,操作地址就是实参地址,只是相当于实参的⼀个别名,对它的操作就是对实参的操作接下来是指针传递,也可发现操作地址是实参地址那么,引⽤传递和指针传递有什么区别吗?引⽤的规则:(1)引⽤被创建的同时必须被初始化(指针则可以在任何时候被初始化)。
带指针的是地址传递,
不带指针的都是值传递,不改变主函数对应参数的值.
传值是把实参的值“拷贝”给形参,运算中是对形参进行操作,实参的值在运算中不会发生变化
传址是把实参的地址传给形参,运算中是对实参进行操作,会改变实参的值
下面是一个例子,看不懂就算了;
C/C++中参数传递有两种方式,传值或传地址(传引用),通常我们要在被调用函数中改变一个变量的值就需要传地址调用方式,例如:
void swap_by_value(int a, int b)
{
int temp;
temp = a;
a = b;
b = temp;
}
void swap_by_ptr(int* pa, int* pb)
{
int temp;
temp = *pa;
*pa = *pb;
*pb = temp;
}
int main(int argc, char* argv[])
{
int a=5, b=6;
swap_by_value(a, b);
printf("a=%d, b=%d\n", a, b);
swap_by_ptr(&a, &b);
printf("a=%d, b=%d\n", a, b);
return 0;
}
很显然,通过传值调用并不能改变main函数中的a与b,要想改变a与b的值就必须通过传地址方式,然后在被调用函数中通过指针访问到主函数中的变量。
可是通过传递变量指针就一定是传地址调用么?看下面一个例子
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void get_str(char* p)
{
p = (char*)malloc(sizeof("abcd"));
printf("%d\n", sizeof("abcd"));
strcpy(p, "abcd");
return;
}
int main(int argc, char* argv[])
{
char* p = NULL;
get_str(p);
printf("p=%p\n", p);
puts(p);
return 0;
}
其中参数p是一个指针,程序想通过p来得到在get_str(char* p)中分配的一块内存。
但是上面的代码并不能完成此功能。
原因是程序需要在get_str(char* p)中修改main函数中的指针变量p,使其指向malloc返回的一块内存,要想在 get_str(char* p)中修改main函数中p的值就只有把p的地址传过去,在get_str中通过地址访问主函数中的变量p。
正确的代码如下:#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void get_str(char** p)
{
*p = (char*)malloc(sizeof("abcd"));
printf("%d\n", sizeof("abcd"));
strcpy(*p, "abcd");
return;
}
int main(int argc, char* argv[])
{
char* p = NULL;
get_str(&p);
printf("p=%p\n", p);
puts(p);
return 0;
}
引用是C++中的语法,结合了传地址和传值的优点,即传递的是一个地址,但在形式上又没有指针那个怪异的符合*,看上去舒服多了。
下面是一个传引用版的swap()函数
void swap_by_ref(int& a, int& b)
{
int temp;
temp = a;
a = b;
b = temp;
}
int main(int argc, char* argv[])
{
int a=5, b=6;
swap_by_ref(a, b);
return 0;
}
总之,如果想在被调用函数中改变哪个变量的值就需要把那个变量的地址传给被调用函数,这样在被调用函数中通过地址就可以访问和修改这个变量。
传值的时候会对参数进行拷贝,然后传给被调用函数,而传地址只是对地址进行一份拷贝,然后赋给形参,这样在C++中如果传递一个比较大的对象时效率就有所区别了。
对于简单类型的变量来说传值和传地址在根本上是没有区别的,都是传递一个数给被调用函数,而怎样去解释和使用这个数是我们事先约定好的。
从函数定义的角度看,函数可分为库函数和用户定义函数两种。
(1)库函数
由C系统提供,用户无须定义,也不必在程序中作类型说明,只需在程序前包含有该函数原型的头文件即可在程序中直接调用。
在前面各章的例题中反复用到printf 、scanf 、getchar 、putchar、gets、puts、strcat等函数均属此类。
(2)用户定义函数
由用户按需要写的函数。
对于用户自定义函数,不仅要在程序中定义函数本身,而且在主调函数模块中还必须对该被调函数进行类型说明,然后才能使用。
主函数一般而言,编写一个能运行在操作系统上的程序,都需要一个主函数。
主函数意味着建立一个独立进程,且该进程成为了程序的入口,对其它各函数(在某些OOP语言里称作方法,比如Java)进行调用,当然其它被调用函数也可以再去调用更多函数.......这样整个程序的运行轨迹就像一种栈,有时我们称之为调用栈。
主函数既是程序的入口,又是程序的出口,通常我们还可以指定一个exit code再退出,以表明程序最后的结果是什么样的。
由于主函数肩负着入口和出口的重任,所以最好不要把太多的细节方面的逻辑直接放在主函数内,这样不利于维护和扩展。
主函数应该尽量简洁,具体的实现细节应该封装到被调用的子函数里面去。