传递指针允许多个函数访问指针引用的对象,而无需将该对象声明为全局可访问。 要修改函数中的数据,需要使用指针传递数据。 当数据是需要修改的指针时, 要将指针传递给指针,传递参数(包括指针)时,传递的是它们的值。 也就是说,传递给函数的是参数值的副本。 本文将讨论 C 语言中传递给函数和从函数传递的指针。 返回指针的内容。
使用指针传递数据
使用指针传递数据的主要原因之一是函数可以修改数据
下面的代码实现了一个通用的交换功能:
#
无效交换(int * a,int * b)
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
int main()
整数 m,n;
米 = 5;
n = 10;
(“m=%d,n=%d”,m,n);
交换(&m,&n);
(“m=%d,n=%d”,m,n);
0;
如果参数不通过指针传递,则不会发生交换。 具体原理可以参考任何C语言教材。
将指针传递给常量
将指针传递给常量是C中常用的技术。它非常有效,因为它避免了在某些情况下复制大量内存。 如果您不想修改数据,则必须传递指向常量的指针。
我们无法修改通过指向常量的指针传入的值:
#
void (const int* num1, int*num2)
*num2 = *num1;
int main()
常量 int a = 100;
整数 b = 5;
(“a=%d,b=%d”,a,b);
(&a,&b);
(“a=%d,b=%d”,a,b);
0;
下面的代码会产生错误(第二个形参和实参的类型不匹配,试图修改第一个形参引用的常量):
#
void (const int* num1, int* num2)
*num1 = 100;
*数字2 = 200;
int main()
常量 int 限制 = 100;
(&限制,&限制);
0;}
C语言中堆和栈的区别
预备知识——程序的内存分配
C编译的程序所占用的内存分为以下几部分:
1、栈区(stack)——由编译器自动分配和释放,存储函数参数值、局部变量值等。它的操作就像数据结构中的栈一样。
2.堆区(heap)——一般由程序员分配和释放。 如果程序员不释放它,当程序结束时它可能会被操作系统回收。 注意,它与数据结构中的堆不同,分配方式类似于链表。
3.全局区(静态区)(),全局变量和静态变量存放在一起。 已初始化的全局变量和静态变量位于一个区域,未初始化的全局变量和未初始化的静态变量位于相邻的区域。 另一个区域。 程序结束后由系统释放。
4、文字常量区——常量字符串放置于此,程序结束后由系统释放。
5. 程序代码区——存放函数体的二进制代码。
我们先来说说C语言程序内存分配中的堆和栈。 通常,内存分配程序存储在ROM或Flash中。 运行时需要将其复制到内存中执行。 内存会分别存储不同的信息,如下图所示:
内存中的堆栈区域处于相对较高的地址。 如果地址增长方向向上,则堆栈地址向下增长。 局部变量空间在栈中分配,堆区域向上增长以分配程序员请求的内存空间。 。 另外还有静态区,分配静态变量和全局变量空间; 分配常量和程序代码空间的只读区域; 以及其他一些分区。
栈之间的区别,我们看一个经典的例子:
#
#
整数a = 0; //全局初始化区域
字符*p1; //全局未初始化区域
int main()
整数b; //堆
char s[] = "abc"; //堆
字符*p2; //堆
字符 *p3 = ""; //常量区中,p3在栈上。
int c = 0; //全局(静态)初始化区域
p1 = (字符*)(10); //堆
p2 = (字符*)(10);
0;
不知道你是否明白一点点。 堆和栈的第一个区别就是申请方式不同:栈(英文名称为stack)是由系统自动分配的。 例如,如果我们定义一个 char a; 系统会自动在栈上为其分配空间。 打开
空间。 堆(英文名称为heap)是程序员根据需要申请的空间,如(10); 由于栈上的空间是自动分配和自动回收的,所以栈上数据的生命周期只在函数运行过程中。 ,运行后会被释放,无法再次访问。 只要程序员不释放空间,堆上的数据总是可以被访问的。 但缺点是如果程序员忘记释放,就会造成内存泄漏。 还有一些其他差异在网上得到了很好的总结,并在此处解释:
1.申请后系统响应
栈:只要栈的剩余空间大于申请的空间,系统就会为程序提供内存,否则会报异常提示栈溢出。
堆:首先你应该知道操作系统有一个记录空闲内存地址的链表。 当系统收到程序的请求时,会遍历链表,寻找第一个空间大于请求空间的堆节点,然后将该节点从链表中删除空闲节点并分配空间程序的节点。 另外,对于大多数系统来说,本次分配的大小会被记录在这块内存空间的首地址处,以便代码中的语句能够正确释放这块内存空间。 另外,由于找到的堆节点的大小不一定正好等于请求的大小,系统会自动将多余的部分放回到空闲链表中,这意味着堆还要做一些后续工作申请后。 这就会带来应用效率的问题。
2、应用效率对比
堆栈:由系统自动分配,速度更快。 但程序员无法控制。
堆:是new分配的内存。 一般情况下速度较慢且容易产生内存碎片,但使用起来最方便。
3. 应用程序大小的限制
栈:下面,栈是一种向低地址延伸的数据结构,是一块连续的内存区域。 这句话的意思是,栈顶的地址和栈的最大容量是系统预先确定的。 下面,栈的大小为2M(有的说是1M,总之是编译时确定的常量)。 如果你申请的空间超过了堆栈的剩余空间,就会有提示。 因此,堆栈的可用空间较小。
堆:堆是一种向高地址扩展的数据结构,是一个不连续的内存区域。 这是因为系统使用链表来存储空闲内存地址,这些地址天然是不连续的,而链表的遍历方向是从低地址到高地址。 堆的大小受到计算机系统中可用的虚拟内存的限制。 可见堆获得的空间更加灵活、更大。
4.堆和栈中存储内容
栈:函数调用时,首先压入栈的是主函数中函数调用后的下一条指令的地址(函数调用语句的下一条可执行语句),然后是函数的各种参数。 在大多数 C 编译器中,参数是从右向左压入堆栈的,后面是函数中的局部变量。 请注意,静态变量不会压入堆栈。 当这个函数调用结束时,局部变量先出栈,然后是参数,最后栈顶指针指向原来存放的地址,也就是main函数中的下一条指令。 程序从此时开始继续运行。
堆:一般在堆的头部用一个字节来存储堆的大小。 堆的具体内容由程序员安排。
引用一位前辈的比喻可以看出堆和栈的区别:
使用堆栈就像我们去餐馆吃饭一样。 我们只是点餐(发出申请)、付款、吃饭(使用)。 当我们吃饱了,我们就离开。 切菜、洗菜、洗碗、洗锅等准备工作我们不用操心,收尾工作的好处是速度快,但自由度不大。 使用 Dui 就像制作自己喜欢的菜肴一样。 比较麻烦,但是更符合自己的口味,自由度更大。
局部变量指针
如果不了解程序堆栈的工作原理,很容易犯返回指向本地数据的指针的错误。 请参见以下示例:
#
#
int*(int 大小,int 值)
int arr[大小];
for(int i = 0; i < 大小; i++) {
arr[i] = 值;
到达;
int main()
整数* = (5, 45);
for(int i = 0; i < 5; i++) {
(“%d”,[i]);
0;
一旦函数返回,返回的数组地址就无效,因为函数的堆栈帧已从堆栈中弹出。
一种方法是将 arr 变量声明为,这会将变量的作用域放在函数内部,但将其分配在堆栈帧之外,以防止其他函数覆盖变量值。
#
#
int*(int 大小,int 值)
int arr[10];
for(int i = 0; i < 大小; i++) {
arr[i] = 值;
到达;
int main()
整数* = (5, 45);
for(int i = 0; i < 5; i++) {
(“%d”,[i]);
0;
返回指针
从函数返回对象通常使用以下两种技术:
在函数内部使用分配内存并返回其地址,调用者负责释放返回的内存
将对象传递给函数并让函数修改它。 这样,分配和释放对象的内存就是调用者的责任。
#
#
int*(int 大小,int 值)
int* arr = (int*)(大小 * (int));
for(int i = 0; i < 大小; i++) {
arr[i] = 值;
到达;
int main()
整数* = (5, 45);
for(int i = 0; i < 5; i++) {
(“%d”,[i]);
自由的();
0;
该函数的以下版本传递一个数组指针、数组的长度以及用于初始化数组元素的值。 返回指针只是为了方便。
#
#
int* (int *arr, int 大小, int 值)
if(arr != NULL) {
for(int i = 0; i < 大小; i++) {
arr[i] = 值;
到达;
int main()
int* = (int*)(5 * (int));
(, 5, 45);
for(int i = 0; i < 5; i++) {
(“%d”,[i]);
自由的();
0;
将指针传递给指针
将指针传递给函数时,您传递了一个值。 如果要修改原始指针而不是指针的副本,则需要传递指针的指针。
#
#
void (int **arr, int 大小, int 值)
*arr = (int*)(大小 * (int));
if(arr != NULL) {
for(int i = 0; i < 大小; i++) {
*(*arr + i) = 值;
int main()
int* = NULL;
(&, 5, 45);
for(int i = 0; i < 5; i++) {
(“%d”,[i]);
自由的();
0;
二叉树递归实现和双指针
二叉树的很多操作往往是通过递归调用来实现的,这就决定了整个过程不能只通过main函数来实现,需要调用main之外定义的函数。 因此,对main调用之外定义的函数的参数传递有严格的要求。 在网上搜了很多构建二叉树的程序,但是当我直接复制到自己的电脑上运行时,发现很多错误,无法编译。 以下代码编译通过,不涉及二叉树的所有操作。 重点通过C语言创建二叉树的过程来说明递归实现和双指针的相关问题。
1.二叉树的定义
二叉树的定义结构通常采用以下形式:
节点
字符ch;
节点*,*;
}节点,*BTree;
Node一般可以用来定义二叉树节点,*BTree可以用来定义指向二叉树(根节点)的指针。
2.动态内存分配
使用动态内存分配需要使用函数。 值得注意的是,该函数成功开辟新内存时,默认返回一个void*指针,因此需要强制转换为Node*形式,其调用形式如(Node*)((Node))
3. 递归调用
由于需要递归调用,二叉树上的一些操作需要作为函数独立执行。 不过这些函数都是在main中调用的,所以对传递参数和返回值的处理非常重要。 另外要注意的是,要操作二叉树,首先要知道二叉树的入口,即指向二叉树的指针,即指向二叉树根节点的指针。 因此,传递的参数是一个指向根节点的指针。 并且由于操作涉及分配内存,因此必须传递辅助指针。 在下面的程序中,函数可能有也可能没有返回值(因为传递了地址)。 在main函数中测试,返回值是二叉树根节点的值。
void (节点** pTree)
字符ch;
scanf("%c",&ch);
if(chr == '#') {
(*pTree) = NULL;
} 别的 {
if(!((*pTree) = (节点*)((节点)))) {
出口();
(*pTree)->ch = chr;
(&((*pTree)->));
(&((*pTree)->));
【C语言中指针的传递】相关文章:
1.C语言指针教学
2.C语言指针的使用
3.C语言中指针使用详解
4.C语言指针函数和函数指针详解
5.C语言中指针类型分析
6.C语言中指针的解释
7.C语言中指针数组的概念
8.C语言数组和指针详解