【C】函数与数组

数组

一维数组

  1. 在创建时赋初值就叫初始化(数组类型是去数组名之后剩的部分)
  2. 目前大多是 C99 及以后的编译器,但是有的编译器是不完全支持 C99 标准
  3. 数组越界:编译器无边界检查,越界访问会导致未定义行为(随机值或程序崩溃)
  4. 数组名大多会退化为首元素地址(非左值),仅在 sizeof 操作和取地址时表示整个数组
  5. 全局数组与静态数组默认置零,栈数组不初始化具有不确定值(C99 新增了变长数组)
c 复制代码
#include <stdio.h>

int main()
{
    // 各种初始化
    int arr1[3] = {1, 2, 3};
    int arr2[5] = {1, 2};
    int arr3[] = {4, 5, 6};
    int arr4[10] = {[0] = 10, [5] = 60}; // C99
    
    // 计算数组元素数
    size_t n = sizeof(arr1) / sizeof(arr1[0]);
	
    // 访问
    printf("%d %d %d\n", arr1[1], *(arr1+1), 1[arr1]);
	
    return 0;
}

二维数组

  1. 注意内存分配检查与分配内存的手动管理(避免内存泄漏)
  2. 二维数组名大多情况退化为首行的地址,特殊情况与一维数组类似
  3. 通过公式 i * n + j 计算可以将二维数组的下标映射到一维数组的下标
  4. 函数参数中二维数组必须指定列数,普通二级指针与二维数组类型不匹配
  5. 二维数组(在内存连续存储)初始化时编译器需通过列数计算每行元素个数
c 复制代码
#include <stdlib.h>
#include <string.h>

int main()
{
    // 各种初始化
    int arr1[3][3] = {{1}, {2, 3}};
    int arr2[][5] = {{1, 2}, {3, 4, 5}};
    int arr3[3][4] = {[1][2] = 6}; // C99
    int arr4[2][2] = {{7, 8}, {9, 10}};
    
    // 非连续内存动态二维数组
    int m = 2, n = 3;
    int** arr5 = (int**)malloc(sizeof(int*) * m);
    if (!arr5) { return 1; }
    for (int i = 0; i < m; i++)
    {
        arr5[i] = (int*)malloc(sizeof(int) * n);
        if (!arr5[i])
        {
	        for (int j = 0; j < i; j++) free(arr5[j]);
	        free(arr5);
	        return 1;
	    }
        memset(arr5[i], 0, sizeof(int) * n);
    }
    
    for (int i = 0; i < m; i++) free(arr5[i]);
    free(arr5);
    arr5 = NULL;
	
    // 连续内存动态二维数组
    int m1 = 2, n1 = 3;
    int (*arr6)[n1] = (int(*)[n1])malloc(sizeof(int[m1][n1])); // C99
    if (!arr6) { return 1; }
	
    free(arr6);
    arr6 = NULL;
	
    return 0;
}

函数

库函数

  1. 相关文档:C libraryC Programming LanguageC Standard Library headers
  2. 英语很重要,文档很多都是英语的(可以根据文档模拟实现一些常用的函数与宏)
  3. 库函数使用核心原则:需包含对应头文件、严格匹配参数类型与用法、做好错误处理

自定义函数

  1. 函数名就是函数的地址(函数类型是去函数名之后剩的部分)
  2. 形参和实参各自是独立的内存空间(形参是实参的⼀份临时拷贝)
  3. 因为数组在传递参数时传递的是指针,所以函数内部无法计算原数组大小
  4. 嵌套调用(函数内部调),链式访问(函数调用中调),递归(自己调自己)
  5. 函数的传址调用和传值调用,说的是参数传递状态(与是否地址和值混传没关系)
  6. 通常不用递归就得想迭代(通常是循环),或者递归转非递归(非递归一般更好)
c 复制代码
// .c ---------------------------------------------------------------------------
int g_global = 10;

int add(int a, int b)
{
    return a + b;
}

// .c ---------------------------------------------------------------------------
#include <stdio.h>

// 声明  
int add(int a, int b);
extern int g_global;

int main()
{
    printf("%d\n", add(1, g_global));
    
    return 0;
}

作用域与生命周期

  1. 作用域就近原则:程序在查找变量时,优先从当前作用域的局部变量开始并逐层向外查找
  2. static 修饰的全局变量和函数,只能在本文件内部使用(修饰局部变量会将其生命周期变长)
  3. 内联可能不被编译器采纳,是否生成实现取决于链接(应避免依赖外部链接的 inline 定义)
c 复制代码
// main.h ---------------------------------------------------------------------------
#pragma once
#include <stdio.h>

// 唯一跨平台安全的 inline(C99)写法
static inline int min(int a, int b)
{
	// 生命周期延长
	static int sum = 0;
	sum++;
	printf("sum: %d\n", sum);
	
	return a < b ? a : b;
}

// .c ---------------------------------------------------------------------------
#include "main.h"

// 全局变量
int global = 1;

int main()
{
	// 局部变量
	int global = 10;

	// 作用域就近原则
	printf("%d\n", global);
	{
		printf("%d\n", global);

		// 重新声明外部变量(不是绕过局部)
		extern int global;
		printf("%d\n", global);
	}

	// 内联函数
	printf("%d\n", min(6, 2));
	printf("%d\n", min(min(1, 2), 5));

	return 0;
}

可变函数参数

  1. 函数声明时最后一个固定参数后加 ... 表示可变参数(va_list 类型保存参数列表)
  2. 调用者必须提供足够信息(如参数个数或终止标记)让函数知道何时停止读取参数
c 复制代码
#include <stdio.h>
#include <stdarg.h>

// 可变参数不做类型检查,传错类型是未定义行为
int sum(int count, ...)
{
	// 定义与初始化
    va_list args;
    va_start(args, count);
    
    int total = 0;
    for (int i = 0; i < count; i++)
    {
	    // 获取可变参数
        total += va_arg(args, int);
    }
    
    // 清理资源
    va_end(args);
    
    return total;
}

int main()
{
    printf("%d\n", sum(3, 10, 20, 30));
    
    return 0;
}

相关推荐
Evand J2 分钟前
【2026课题推荐】DOA定位——MUSIC算法进行多传感器协同目标定位。附MATLAB例程运行结果
开发语言·算法·matlab
jllllyuz25 分钟前
基于MATLAB的二维波场模拟程序(含PML边界条件)
开发语言·matlab
leo__52028 分钟前
基于MATLAB的交互式多模型跟踪算法(IMM)实现
人工智能·算法·matlab
忆锦紫33 分钟前
图像增强算法:Gamma映射算法及MATLAB实现
开发语言·算法·matlab
t1987512844 分钟前
基于自适应Chirplet变换的雷达回波微多普勒特征提取
算法
guygg881 小时前
采用PSO算法优化PID参数,通过调用Simulink和PSO使得ITAE标准最小化
算法
老鼠只爱大米1 小时前
LeetCode算法题详解 239:滑动窗口最大值
算法·leetcode·双端队列·滑动窗口·滑动窗口最大值·单调队列
mit6.8242 小时前
序列化|质数筛|tips|回文dp
算法
亲爱的非洲野猪2 小时前
Java锁机制八股文
java·开发语言
rgeshfgreh2 小时前
C++字符串处理:STL string终极指南
java·jvm·算法