当前位置:首页 > 技术 > 正文内容

C语言中的函数机制详解

访客 技术 2026年6月6日 1

函数基础与定义

在C语言中,函数是程序的基本组成单元,用于封装可重复使用的代码块。每个函数都有独立的功能,并可通过调用执行。

示例:实现两个整数相加的函数:

#include <stdio.h>

// 函数声明(原型)
int add_numbers(int, int);

int main() {
    int a = 15;
    int b = 25;
    int result;

    result = add_numbers(a, b);
    printf("Sum: %d\n", result);

    return 0;
}

// 函数定义
int add_numbers(int x, int y) {
    return x + y;
}

上述代码展示了函数的声明、调用和定义过程。函数必须先声明或定义后才能被调用,除非定义位于主函数之前。

参数传递方式

C语言支持两种主要的参数传递模式:值传递和地址传递。

值传递(按值调用)

实参的副本被传入函数,形参在栈上分配新内存空间。因此,在函数内部修改形参不会影响原始变量。

#include <stdio.h>

void modify_value(int val) {
    val = 100;  // 修改的是副本
    printf("Inside function: %d\n", val);
}

int main() {
    int num = 10;
    printf("Before call: %d\n", num);
    modify_value(num);
    printf("After call: %d\n", num);  // 值未改变
    return 0;
}

地址传递(指针传参)

通过传递变量的地址,使函数能够直接操作原数据。常用于交换变量、修改多个值等场景。

#include <stdio.h>

void swap_values(int *p1, int *p2) {
    int temp = *p1;
    *p1 = *p2;
    *p2 = temp;
}

int main() {
    int x = 5, y = 8;
    printf("Before swap: x=%d, y=%d\n", x, y);
    swap_values(&x, &y);
    printf("After swap: x=%d, y=%d\n", x, y);
    return 0;
}

数组作为函数参数

当数组作为参数传递时,实际上传递的是首元素的地址,即一个指针。因此函数无法直接获取数组长度,需额外传入大小信息。

使用数组名传参

#include <stdio.h>

int calculate_sum(int arr[], int length) {
    int total = 0;
    for (int i = 0; i < length; ++i) {
        total += arr[i];
    }
    return total;
}

int main() {
    int data[] = {1, 3, 5, 7, 9};
    int size = sizeof(data) / sizeof(data[0]);
    int sum_result = calculate_sum(data, size);
    printf("Total sum: %d\n", sum_result);
    return 0;
}

使用指针方式处理字符串

编写一个去除字符串中所有空格的函数:

#include <stdio.h>

void remove_spaces(char *str) {
    char *src = str;
    char *dst = str;

    while (*src != '\0') {
        if (*src != ' ') {
            *dst++ = *src;
        }
        src++;
    }
    *dst = '\0';  // 添加结束符
}

int main() {
    char text[] = "  hello   world  ";
    printf("Original: [%s]\n", text);
    remove_spaces(text);
    printf("Cleaned: [%s]\n", text);
    return 0;
}

返回指针的函数

这类函数返回一个内存地址,通常指向静态变量、动态分配内存或全局数据,避免返回局部变量地址(会导致野指针)。

正确返回静态缓冲区地址

#include <stdio.h>
#include <string.h>

char* create_greeting(int id) {
    static char buffer[50];  // 静态存储周期,函数结束后仍有效
    sprintf(buffer, "User-%d", id);
    return buffer;
}

int main() {
    char *msg1 = create_greeting(101);
    char *msg2 = create_greeting(102);

    printf("%s\n", msg1);  // 输出 User-102(共用同一缓冲区)
    printf("%s\n", msg2);
    return 0;
}

整型转字符串实现

#include <stdio.h>

char* int_to_string(char *output, int value) {
    int remainder;
    int index = 0;
    int start = 0, end;

    // 处理负数
    if (value < 0) {
        output[index++] = '-';
        value = -value;
        start = 1;
    }

    // 提取每位数字
    do {
        remainder = value % 10;
        output[index++] = remainder + '0';
        value /= 10;
    } while (value > 0);

    output[index] = '\0';

    // 反转数字部分
    end = index - 1;
    while (start < end) {
        char tmp = output[start];
        output[start] = output[end];
        output[end] = tmp;
        start++;
        end--;
    }

    return output;
}

int main() {
    char str_buffer[32];
    int num;
    printf("Enter an integer: ");
    scanf("%d", &num);
    int_to_string(str_buffer, num);
    puts(str_buffer);
    return 0;
}

函数指针的应用

函数指针可以指向某个函数的入口地址,从而实现动态调用、回调机制和多态行为。

基本语法与调用

#include <stdio.h>

int multiply(int a, int b) {
    return a * b;
}

int divide(int a, int b) {
    return b != 0 ? a / b : 0;
}

int main() {
    int u = 20, v = 4;
    int result;

    // 声明函数指针
    int (*func_ptr)(int, int);

    func_ptr = multiply;
    result = func_ptr(u, v);
    printf("Multiply: %d\n", result);

    func_ptr = divide;
    result = (*func_ptr)(u, v);
    printf("Divide: %d\n", result);

    return 0;
}

函数指针广泛应用于事件处理、插件架构和算法选择等高级编程场景。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。