【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;
}

相关推荐
郝学胜-神的一滴2 小时前
人工智能与机器学习:从理论到实践的技术全景
人工智能·python·程序人生·算法·机器学习
superman超哥2 小时前
仓颉内存分配优化深度解析
c语言·开发语言·c++·python·仓颉
2401_841495642 小时前
并行程序设计与实现
c++·python·算法·cuda·mpi·并行计算·openmp
invicinble2 小时前
java集合类(二)--map
java·开发语言·python
算法与编程之美2 小时前
不同的优化器对分类精度的影响以及损失函数对分类精度的影响.
人工智能·算法·机器学习·分类·数据挖掘
sali-tec2 小时前
C# 基于halcon的视觉工作流-章71 深度学习-预处理OCR
开发语言·人工智能·深度学习·数码相机·算法·计算机视觉·ocr
宠..2 小时前
QPlainText方法大全
开发语言·qt
咕噜企业分发小米2 小时前
腾讯云知识图谱实体链接的准确率如何评估?
人工智能·算法·机器学习
MicroTech20252 小时前
MLGO微算法科技发布改进量子ODE算法,支持不可对角化矩阵与非齐次系统实现指数级误差优化
科技·算法·矩阵