首页 > 编程语言 >[C/C++] 函数

[C/C++] 函数

时间:2023-07-09 22:36:24浏览次数:30  
标签:函数 int C++ printf include data 指针

疑问

1、函数结束后,函数栈释放的内容有哪些?

2、通过函数修改形参的值怎么实现?值传递还是引用传递?基本类型、数组、结构体有什么区别?

3、如果想通过函数对实参进行malloc,为什么必须用二级指针?

函数栈空间

在一个函数执行完毕后其所占用的内存空间(除了静态和全局变量)统统会被释放掉

执行程序有一个栈空间,每一次函数调用都会开辟新的栈空间,完成调用后,新开辟的栈空间被释放

因此如果递归函数没有正确返回的话,程序会出现栈溢出导致程序崩溃

// 代码说明函数栈释放
int *func(){
    int x = 10; // 栈空间局部变量,调用结束后被释放
    return &x;
}
// out put: if branch
int main() {
    int *ret = func();
    if (ret == NULL) {
        printf("Address of stack memory associated with local variable is freed after caller end");
    }
}

int *func2(){
    static int x = 10; // 栈空间静态变量,调用结束后没有被释放
    return &x;
}

// out put: else branch
int main() {
    int *ret = func2();
    if (ret == NULL) {
        printf("Address of stack memory associated with local variable is freed after caller end");
    } else {
        printf("x address: %p\n", ret);
        printf("Address of stack memory associated with local variable is not freed after caller end");
    }
}

值传递

C语言所有的函数参数都是“传值引用”,意味着函数将获得该数值的一份拷贝,函数可以放心修改这个拷贝值,而不是修改调用方实际传给它的参数

基本数据类型

基本数据类型的数据通过函数修改,都必须传入指针实现,不能直接传入基本类型参数

⚠️如果函数的参数是指针的话,传递给函数的是这个指针的副本,可以修改调用方传入的数值

void func(int x) {
    printf("调用中:x = %d %p\n", x, &x);
    x = 20;
}

// not work
int main() {
    int x = 10;
    printf("调用前:x = %d %p\n", x, &x);
    func(x);
    printf("调用后:x = %d %p\n", x, &x);
    return 0;
}

void func2(int *x) {
    printf("调用中:x = %d %p\n", *x, &x);
    *x = 20;
}

// work
int main() {
    printf("调用前:x = %d %p\n", x, &x);
    func2(&x);
    printf("调用后:x = %d %p\n", x, &x);
    return 0;
}

数组类型

数组名的值实际上是一个指针,可以执行访问操作,参数同样是一份拷贝,但在这一份拷贝上执行的间接访问操作的的是原来的数组,和传递指针效果一样

如果不希望在函数内部修改数组的值,可以通过const修饰符限定

动态申请内存 & 指针函数

如果想在函数内部动态申请内存,应该怎么实现呢?

有以下两种方式

  • 形参为二级指针,在函数内部分配内存
  • 形参为空,通过指针函数方式分配内存
#include <stdio.h>
#include <stdlib.h>

// CASE 1
void allocateMemory(char** ptr) {
    *ptr = (char*)malloc(10 * sizeof(char));  // 分配内存并将地址存储到ptr所指向的指针中
    strcpy(*ptr, "Hello");
}

int main() {
    char* myString = NULL;
    allocateMemory(&myString);  // 传递myString的地址作为参数
    printf("%s\n", myString);  // 输出动态分配的字符串
    free(myString);  // 释放内存
    return 0;
}

// CASE 2
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void allocateArray(int** ptr, int size) {
    *ptr = (int*)malloc(size * sizeof(int));  // 分配内存
    for (int i = 0; i < size; i++) {
        (*ptr)[i] = 100;
    }
}

//void copyArray(int* src, int* dest, int size) {
//    for (int i = 0; i < size; i++) {
//        dest[i] = src[i];
//    }
//}

int main() {
    int size = 5;
    int* dynamicArray;
    allocateArray(&dynamicArray, size);  // 动态分配数组
    int staticArray[size];
//    copyArray(dynamicArray, staticArray, size);  // 将动态数组复制到静态数组
    memcpy(staticArray, dynamicArray, size * sizeof(int));
    for (int i = 0; i < size; i++) {
        printf("%d ", staticArray[i]);  // 输出静态数组元素
    }
    printf("\n");
    free(dynamicArray);  // 释放动态分配的内存
    return 0;
}
// CASE 1
#include <stdio.h>
#include <stdlib.h>

char* allocateMemory() {
    char* ptr = (char*)malloc(10 * sizeof(char));  // 分配内存
    strcpy(ptr, "Hello");
    return ptr;  // 返回指向动态分配内存的指针
}

int main() {
    char* myString = allocateMemory();  // 调用指针函数,接收返回的指针
    printf("%s\n", myString);  // 输出动态分配的字符串
    free(myString);  // 释放内存
    return 0;
}

// CASE 2 array
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define TYPE int

TYPE *mem_malloc(int len) {
    if (len <= 0) {
        return NULL;
    }
    TYPE *a = NULL;
    a = (TYPE *) malloc(sizeof(TYPE) * len);
    for (int i = 0; i < len; ++i) {
        *(a + i) = 10;
    }
    return a;
}

int main() {
    int size = 10;
    TYPE staticArray[size];
    TYPE *x = mem_malloc(size);
    memcpy(staticArray, x, size * sizeof(TYPE));
    for (int i = 0; i < size; ++i) {
        printf("%d ", *(staticArray + i));
    }
    free(x);
    return 0;
}

为什么不能通过一级指针实现在函数内部分配内存呢?如果使用一级指针

1、调用者caller的待分配对象是NULL

2、被调函数callee的形参的解引用后,其类型不再是指针,无法申请内存。

3、如果直接对形参进行malloc,由于形参是实参的拷贝,也不能实现对caller函数对象的内存申请

回调函数 & 函数指针

以一个客户端、服务端的例子说明回调函数的应用

整体流程说明

  • 服务端提供回调的注册接口和函数指针
  • 客户端注册回调到服务端,客户端修改服务端的data值
  • 服务端demo只支持一个回调函数的注册,后续可以通过表的方式通过消息支持多个回调函数的注册
// client.c
#include <stdio.h>
#include "server.h"

// 回调处理函数
void client_callback_process(int* data) {
    printf("SDK User: Callback function called with data: %d\n", *data);
    // 模拟修改data的操作
    *data = 456;
    printf("SDK User: Modified data: %d\n", *data);
}

int main() {
    registerCallback(client_callback_process);
    server_main_process();
}

// server.h
typedef void (*Callback)(int *);
void registerCallback(Callback callback);
void server_main_process();

// server.c
#include <stdio.h>
#include "server.h"

// 全局变量用于接收回调函数和参数
Callback callbackFunction = NULL;

// 注册回调函数给SDK服务方
void registerCallback(Callback callback) {
    // 模拟注册回调函数的操作
    printf("SDK Provider: Callback function registered.\n");
    // 将回调函数赋值给全局变量
    callbackFunction = callback;
}

// 触发事件,调用客户端注册的回调函数并传递参数
void triggerEvent(int *data) {
    printf("SDK Provider: Event triggered with data: %d\n", *data);

    // 调用客户端注册的回调函数并传递参数
    if (callbackFunction != NULL) {
        callbackFunction(data);
    }
}

// 模拟服务端的处理流程
void server_main_process() {
    int data = 123;
    triggerEvent(&data);
    printf("SDK Provider: Receive data modified by SDK user: %d\n", data);
}

标签:函数,int,C++,printf,include,data,指针
From: https://www.cnblogs.com/ffopen/p/17539558.html

相关文章

  • 《C++》容器--deque
    deque双端数组。可以对头尾进行插入和删除操作#define_CRT_SECURE_NO_WARNINGS1#include<iostream>#include<deque>#include<algorithm>usingnamespacestd;voidprintdeque(constdeque<int>&d){ for(deque<int>::const_iteratorit=d.begi......
  • watch函数
    与Vue2.x中watch配置功能一致两个小“坑”:监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)。监视reactive定义的响应式数据中某个属性时:deep配置有效。//情况一:监视ref定义的响应式数据watch(sum,(newValue,oldValue)=>{......
  • 【《C++ Primer 第四版》读书笔记】4.2.5-指针和const限定符
    1.指向const对象的指针1.1表现形式constdouble*ptr,constvoid*ptr1.2如何理解无法通过ptr这个指针变量去修改所指向内存区域的值,但是ptr这种指针变量可以重复赋值,指向不同的内存地址注意ptr这个指针变量赋值时,既可以赋值为const类型变量(书中所说的const对象)的地址,也......
  • isinstance() 函数
     文章目录一、isinstance()函数1.1用途描述1.2使用方法1.3案例分析 一、isinstance()函数  下面从用途描述、使用方法、案例分析、三个方面进行讲解。1.1用途描述  isinstance()函数来判断一个对象是否是一个已知的类型,类似type()。1.2使用方......
  • day2c++学习
    学习day2C++函数分文件编写(VScode2021配置教程)_spiritLHL的博客-CSDN博客55函数-函数的分文件编写_哔哩哔哩_bilibili!运行还是有中文乱码st1:ctrl+shift+p输出createc++projectst2:在include里建新文件swap.h,里面写头文件和函数声明st3:在src里建新文件swap.cpp......
  • 内置函数reduce
    1'''2filter()函数是Python内置的一个高阶函数,它用于过滤可迭代对象中的元素,只保留满足特定条件的元素。filter()函数接受两个参数:一个函数和一个可迭代对象。3语法:4filter(function,iterable)5其中:61.function是一个函数,它接受一个参数,并返回一个布......
  • R语言 ggplot函数中 annotate选项增加注释
     001、基础绘图ggplot(data=mtcars,aes(x=mpg,y=disp,color=factor(cyl)))+geom_point()##基础绘图 002、annotete在任意位置增加注释ggplot(data=mtcars,aes(x=mpg,y=disp,##在坐标,25,300处增加QQcolor=factor(cyl)))+geom_point......
  • freeRTOS 10.0.1 的xQueueReceive 函数bug
    xQueueReceive读取队列后,如果再次读取消息队列并保存到同一个变量中,那么还可以读到值 读取后,再读取一次,还有值 必须要手动清除该变量,或者用一个新的指针接收,才会读到0 举例:手动清楚该变量,再读取就是0 要么就是用一个新的变量来接收,这样也可以读到0  ......
  • C++电影评分系统[2023-07-09]
    C++电影评分系统[2023-07-09]程序设计综合课程设计任务书任课教师:张启军班级:22数字媒体1、2、重、补修班时间:第20周分组:2人一组(经老师同意后可1人或3人一组)一、题目电影评分系统二、课程设计目的和要求本课程设计通过完成一个规模适当的、完整的程序,综合运用......
  • 莫比乌斯函数与反演
     莫比乌斯函数的原式是u(n)={1,n=1(-1)^r,n=p1*p2*p3*......*pr 其中p为不同的质数                       0,其他}它有两种解法,分别是欧拉筛和杜教筛下面给出欧拉筛的代码:#include<bits/stdc......