C语言基础 学习笔记总结Day03

第一章 一维数组

1.1 数组基础

 定义与初始化

// 方式1:声明时初始化所有元素  
int arr1[5] = {0, 1, 2, 3, 4};

// 方式2:部分初始化(剩余元素自动补0)  
int arr2[5] = {10, 20};  // → [10, 20, 0, 0, 0]  

// 方式3:自动推断长度
int arr3[] = {2,4,6};     // 长度=3

C

内存布局

地址     | 值  
0x1000 | arr[0] → 0
0x1004 | arr[1] → 1
0x1008 | arr[2] → 2
0x100C | arr[3] → 3
0x1010 | arr[4] → 4

Less

特性

  • 元素连续存储,地址递增
  • 数组名 arr 是首元素地址的常量指针

计算数组长度

int len = sizeof(arr) / sizeof(arr[0]);  

C

注意

  • 仅在定义数组的作用域内有效
  • 数组作为函数参数传递时会退化为指针

1.2 基本操作

遍历数组

for(int i=0; i<5; i++) {  
    printf("arr[%d] = %d\n", i, arr[i]);  
}

 修改元素

arr[2] = 100;  // 直接通过索引修改  

C

注意:索引从 0 开始,最大有效索引为 长度-1

 查找最大值

int max = arr[0];  
for(int i=1; i<5; i++){  
    if(arr[i] > max) {  
        max = arr[i];  
    }  
}  

C

1.3 常见问题

 越界访问风险

int arr[5] = {0};  
arr[5] = 10;  // ❌ 访问越界 → 段错误或数据污染  

 数组名本质

printf("%p == %p", arr, &arr[0]);  // 输出相同地址  
// arr = &other; ❌ 数组名是常量指针,不可修改

 数组赋值限制

int a[5], b[5] = {1,2,3,4,5};  
a = b;              // ❌ 错误!  
memcpy(a, b, sizeof(b));  // ✅ 正确做法  

C

实战案例

案例1:学生成绩统计系统

 
  

C

案例2:斐波那契数列生成器

#include   

int main() {  
    int fib[20] = {0, 1};  // 初始化前两项  

    // 生成数列  
    for(int i=2; i<20; i++) {  
        fib[i] = fib[i-1] + fib[i-2];  
    }  

    // 打印输出  
    printf("斐波那契数列前20项:\n");  
    for(int i=0; i<20; i++){  
        printf("%d ", fib[i]);  
        if((i+1)%5 == 0) printf("\n");  // 每行5个  
    }  
    return 0;  
}  

C

输出效果

0 1 1 2 3  5 8 13 21 34  
55 89 144 233 377  
610 987 1597 2584 4181  

YAML

 高频错误总结

错误类型 错误示例 解决方案
越界访问 arr[5] = 10; 检查循环条件i < 长度
未初始化访问 int arr[5]; printf("%d", arr[0]); 显式初始化数组
错误计算长度 sizeof(arr) 在函数参数中使用 传递长度参数

 核心要点记忆卡

1. 数组是连续内存块,索引从0开始  
2. sizeof(arr) 仅在定义域有效  
3. 数组名是常量指针(不可修改)  
4. 越界访问会导致未定义行为  
5. 数组间赋值需用memcpy或循环  

Markdown

第二章 二维数组

2.1 定义与初始化

 定义方式

// 方式1:按行优先初始化  
int matrix1[3][4] = {  
    {1, 2, 3, 4},  
    {5, 6, 7, 8},  
    {9, 10, 11, 12}  
};

// 方式2:连续初始化(自动填充)  
int matrix2[][4] = {1,2,3,4,5,6}; // → 2行4列,剩余补0  

// 方式3:动态初始化  
int* matrix3[3];  // 指针数组  
for(int i=0; i<3; i++){  
    matrix3[i] = (int*)malloc(4 * sizeof(int));  
}  

C

2.2 内存存储原理

 行优先存储图示

地址      | 值  
0x1000 | matrix[0][0] → 1
0x1004 | matrix[0][1] → 2
0x1008 | matrix[0][2] → 3
0x100C | matrix[0][3] → 4
0x1010 | matrix[1][0] → 5
...  

Less

 地址计算公式

int* base = &matrix[0][0];
int row = 1, col = 2;  
int* elem = base + (row * 4 + col);  // 4为列数  

C

2.3 二维数组传参

函数参数传递方式

固定列数传递(推荐)

void printMatrix(int mat[][4], int rows) {  
    for(int i=0; i

C

指针形式传递

void processMatrix(int* mat, int rows, int cols) {  
    for(int i=0; i

C

 常见错误

错误用法 问题分析 解决方案
void func(int** mat) 类型不匹配(静态数组) 使用固定列数或一维指针
不指定列数:int mat[][] 编译器无法计算地址 必须明确指定列数
动态数组传参未传递行列数 无法确定维度 显式传递行数和列数

 高频错误总结

段错误

int mat[3][4];  
mat[3][0] = 5;  // ❌ 行越界  

错误传递动态数组

int** mat = malloc(3*sizeof(int*));  
// 错误调用  
printMatrix(mat, 3);  // ❌ mat类型不匹配  

C

忽略列数约束

void func(int mat[][]);  // ❌ 未指定列数  

C

核心要点记忆卡

1. 二维数组内存按行连续存储
2. 函数传参必须指定列数(动态数组除外)
3. 地址计算:base + (i*col + j)*sizeof(type)
4. 动态二维数组需传递行列数
5. GDB使用 x/[数量][格式] 命令查看内存  

Markdown

第三章 字符数组与字符串

3.1 字符数组特性

 字符串存储规则

char str1[] = {'H','e','l','l','o','\0'}; // 显式包含\0  
char str2[] = "World";                    // 隐式包含\0  

内存布局

地址   | 值
0x1000| 'H'
0x1001| 'e'
...
0x1005| '\0'

输入输出风险

函数 风险点 安全替代方案
scanf 缓冲区溢出 scanf("%10s", str)
gets 无长度限制 fgets(str, size, stdin)

3.2 字符串处理函数

 扩展函数表

函数 功能描述 安全替代方案 示例
strlen 计算字符串长度(不含\0) strnlen (C11) strlen("Hello") → 5
strcpy 字符串拷贝 strncpy strcpy(dest, src)
strcat 字符串拼接 strncat strcat(str, "!")
strcmp 字符串比较 strncmp strcmp("a", "b") → 负数
strchr 查找字符首次出现位置 strchr("abc", 'b') → "bc"
strstr 查找子串首次出现位置 strstr("hello", "ll") → "llo"
strtok 分割字符串 strtok_r (线程安全) strtok(str, ",")
strdup 字符串动态复制(需free) char* copy = strdup(str)

安全函数用法示例

// 安全拷贝(自动添加\0)  
strncpy(dest, src, sizeof(dest)-1);  
dest[sizeof(dest)-1] = '\0';  

// 安全输入  
fgets(str, sizeof(str), stdin);  
str[strcspn(str, "\n")] = '\0';  // 去除换行符  

3.3 常见漏洞

 缓冲区溢出攻击

char buffer[10];  
scanf("%s", buffer);  // 输入超10字符则溢出  

攻击原理:

  • 溢出数据覆盖相邻内存
  • 可能劫持函数返回地址

防御措施

边界检查:

strncpy(dest, src, dest_size-1);  

使用安全函数:                                                                                                                                             

#define _CRT_SECURE_NO_WARNINGS  // Windows需定义  
gets_s(buffer, sizeof(buffer)); // C11安全函数  

练习:学生管理系统设计

#include 

#define MAX_stu 20

// 单个学生
typedef struct 
{
    char name[128]; // 姓名
    char sex[5];    // 性别
    short id;       // 学号
    int age;        // 年龄
    int sco;        // 成绩
}stu_data;


// 一个班
typedef struct 
{
    stu_data data[MAX_stu];  // 学生   20 代表 20 个座位
    int len;            // 记录学生人数
}stu_class;

// 1、插入到那个班
// 2、插入什么数据
// 3、插入到哪里

/************************* 增 *************************/
/*
对 class 结构体中进行尾部数据的插入
函数 void pos_ins_list( stu_class * class , int pos);
参数 
    @ class     要操作的班级
返回值
    无返回值
*/
void tail_ins_list( stu_class * class );

/************************* 删 *************************/
void tail_del_list( stu_class * class );
/************************* 改 *************************/
void chan_list( stu_class * class );

/************************* 查 *************************/
void find_list( stu_class * class );

/************************* 排序 *************************/
void sort_list( stu_class * class );

/************************* 打印 *************************/
void printf_Sql_List( stu_class * class );



// 如果你现在没听懂 一定是老师的问题 不是你的问题
// 我不喜欢 pua 自己的人

int main(int argc, char const *argv[])
{
    /* 初始化结构体 */
    // 创建一个 班级结构体
    stu_class stu = {
        // 学生数据初始化
        .data = { 
            {"石昊" , "男" , 1 , 18 , 60},
            {"萧炎" , "男" , 2 , 18 , 61},
            {"牧尘" , "男" , 3 , 18 , 62},
            {"林动" , "男" , 4 , 18 , 63},
            {"韩立" , "男" , 5 , 18 , 64},
        },
        // 学生个数变量初始化
        .len = 5
    };
    

    printf_Sql_List(&stu);


    // 插入数据
    tail_ins_list(&stu);
    tail_ins_list(&stu);


    printf_Sql_List(&stu);

    return 0;
}


/************************* 增 *************************/
void tail_ins_list( stu_class * class )
{
    // 判定是否为空指针
    if (NULL == class)
    {
        puts("你没有传入班级");
        return ;
    }
    
    // 判断满
    if( MAX_stu < class->len)
    {
        puts("满了 塞钱都没用(ps:除非给系统的开发者(小声bb))");
        return ;
    }

    // 输入数据
    stu_data data;
    printf("请输入学生数据:");
    
    printf("姓名");
    scanf("%s" , data.name );
    printf("性别");
    scanf("%s" , data.sex );
    printf("学号");
    scanf("%hd" , &data.id );
    printf("年龄");
    scanf("%d" , &data.age );
    printf("成绩");
    scanf("%d" , &data.sco );

    // 插入数据
    class->data[class->len] = data; 
    // 迭代长度
    class->len++;
    
}



/************************* 打印 *************************/
void printf_Sql_List( stu_class * class )
{
    // 循环
    for (size_t i = 0; i < class->len ; i++)
    {
        // 打印数据
        printf("name = %s sex = %s id = %d sco = %d age = %d \n" , \
            class->data[i].name , class->data[i].sex , class->data[i].id \
            , class->data[i].age , class->data[i].sco \
        );
    }
    
}

                     

你可能感兴趣的:(c语言,学习,笔记)