关于c:小凯15天快速讲完c语言简单学习第六课

45次阅读

共计 6436 个字符,预计需要花费 17 分钟才能阅读完成。

温习

0.1 定义函数

返回值类型   函数名称(形式参数类型 1  形式参数名称 1,形式参数类型 2 形式参数名称 2......)
{
     // 函数内的语句
     return  返回值;
}

// 获取两个整数中的较大值

int  GetMax(int a,int b)
{if(a>b)
      {return a;}
      else
      {return b;}
}

// 调用函数

int main()
{
     int  nMax = 0;
     nMax = GetMax(10+5,20);
     return 0;
}

形参:定义函数的时候,规定要传入的参数的类型
实参:调用函数的时候,传递的具体数据
形参的扭转,不会影响实参的值。

和宏的比拟
函数是先把参数的值给运算进去,而后传递给形参。即使是最简略的函数,调用也会产生额定的耗费。
宏是一个预处理,是编译之前进行的一个替换,不宜编写的简单。
什么时候应用函数,什么时候应用宏呢???
当代码比拟简短,且大量调用的时候,应用宏
代码比较复杂,调用不频繁的时候,应用函数。

#include <stdio.h>
#define MUL(a,b) a*b


int GetMul(int m, int n)
{return m * n;}
int main()
{
    int n1 = 0;
    int n2 = 0;
    n1 = MUL(5+1,10);//5+1*10
    printf("%d", n1);
    n2 = GetMul(5+1,10);
    printf("%d", n2);
    return 0;
}

0.2 全局变量和局部变量

全局变量:定义在函数内部的变量,能够被所有的函数所共享。
局部变量:定义在函数外部的变量,只在定义它的花括号内应用。
static:

a. 动态:润饰局部变量,来到动态局部变量的作用域时,不会被销毁,下次进入函数,仍然可能应用上一次来到时的数据。b. 暗藏:润饰全局变量或者函数,起到一个暗藏的作用,润饰之后,全局变量或者函数就只能在本文件中应用了

extern:(extern int a;)

a. 用于申明一个全局变量。遇到了 extern,就意味着这个变量去别的中央寻找。

const:
a. 定义一个不能批改的常量。能够制订数据类型
const float pi = 3.14;
//#define PI (float)3.14

0.3 工程治理的形式

个别状况下,一个程序是由多个文件组成的。不同的文件中实现不同的函数。而后将他们组合到一起。
对于一个.cpp 文件来说,要想将本人的函数提供给其余文件应用,须要实现一个对应的.h 文件。
**.h 文件寄存函数和变量的申明。
.cpp 文件寄存函数的定义和全局变量的定义 **。
在要应用函数和变量的文件中,蕴含对应的头文件就能够了。
** 肯定要留神的问题:
.h 文件中,只能寄存申明,千万不要搁置定义。**

0.4 指针

根本应用:

a. 定义指针变量
b. 给指针变量赋值
c. 解援用

int* p =nullptr;
int a = 100;
p = &a;
*p = 400;

有一个根本的作用:
能够在函数的外部批改到内部的数据
不能说是形参扭转的实参

1. 构造体

1.1 构造体

#include <stdio.h>
int main()
{
    // 如果我当初想要存储两个学生的信息
    // 姓名  年龄  身高  体重
    char szName1[20] = {"xiaoming"};
    int nAge1 = 18;
    int nHeight1 = 176;
    double fWeight1 = 75.5;

    char szName2[20] = {"xiaohong"};
    int nAge2 = 18;
    int nHeight2 = 170;
    double fWeight2 = 60.5;
    // 如果想要输入 xiaoming 的信息
    printf("姓名:%s 年龄:%d 身高:%d 体重:%lf",
        szName1, nAge1, nHeight1, fWeight1);
    // 如果想要输入 xiaohong 的信息
    printf("姓名:%s 年龄:%d 身高:%d 体重:%lf",
        szName2, nAge2, nHeight2, fWeight2);
    // 下面的语法,是能够解决问题的, 然而比拟麻烦
    // 有一个更简略的办法,就是应用构造体
    struct 
    {char szName[20];
        int nAge ;
        int nHeight ;
        double fWeight;
    } stcStudent1 = {"xiaoming",18,176,75.5};
    // 应用的时候,关键在于 szName,nAge,nHeight
    //fWeight 他们在语法上有了分割,他们的关系
    // 就是由编译器主动保护了,不须要程序员保护了。// 应用这种语法,咱们仅仅须要记住 stcStudent1 名字
    // 其余成员,通过 "." 就可能主动的找到了
    // 加重了咱们治理这些数据的累赘
    // 这种劣势,在数据越多的时候越显著。printf("姓名:%s 年龄:%d 身高:%d 体重:%lf",
        stcStudent1.szName,
        stcStudent1.nAge,
        stcStudent1.nHeight,
        stcStudent1.fWeight);


    return 0;
}

1.2 构造体的个别定义形式

#include <stdio.h>
int main()
{
    struct
    {char szName[20];
        int nAge;
        int nHeight;
        double fWeight;
    } stcStudent1 = {"xiaoming",18,176,75.5};

    struct
    {char szName[20];
        int nAge;
        int nHeight;
        double fWeight;
    } stcStudent2 = {"xiaohong",18,170,65.5};

    // 个别状况构造体的类型名,都是大写
    struct STUDENT
    {char szName[20];
        int nAge;
        int nHeight;
        double fWeight;
    };

    struct STUDENT stcStudent3 = {"xiaobai",18,172,63.3};
    struct STUDENT stcStudent4 = {"xiaolan",18,169,58.3};
    // 应用构造的成员的时候,成员是什么类型,就依照什么类型去应用
    printf("姓名:%s 年龄:%d 身高:%d 体重:%lf",
        stcStudent3.szName,
        stcStudent3.nAge,
        stcStudent3.nHeight,
        stcStudent3.fWeight);
    return 0;
}

1.3 对于构造体的内存布局

在内存中,构造体的成员,是依照程序,顺次排列的。
构造体的大小是所有成员的大小之和。因为对齐的起因,大小会略微大一些。这里的 4 个 CC 就是对齐的后果。

1.4 构造体的嵌套

一个构造体的成员是另外一个构造体类型

#include <stdio.h>

struct NOTEBOOK
{
    int nId;      // 序列号
    double fPrice;// 价格
    int nType;    // 品牌
    //.....
};
struct STUDENT
{char szName[20];
    int nAge;
    int nHeight;
    double fWeight;
    NOTEBOOK stcNoteBook;
};
int main()
{
    STUDENT stcStudent1 = {"xiaoming",18,176,75.5,{100,5000.5,2}
    };
    // 拜访构造体成员的时候,能够一级一级的往下拜访
    printf("%d %lf", 
        stcStudent1.stcNoteBook.nId,
        stcStudent1.stcNoteBook.fPrice);
    stcStudent1.nAge = 20;
    stcStudent1.stcNoteBook.nId = 200;
    stcStudent1.szName[0] = 'y';
    return 0;
}

1.5 构造体数组

当咱们须要定义很多构造体变量的时候,咱们也能够应用构造体数组

#include <stdio.h>

struct NOTEBOOK
{
    int nId;      // 序列号
    double fPrice;// 价格
    int nType;    // 品牌
    //.....
};
struct STUDENT
{char szName[20];
    int nAge;
    int nHeight;
    double fWeight;
    NOTEBOOK stcNoteBook;
};
int main()
{
    // 如果此时有 5 个学生,挨个定义会比拟麻烦,能够应用构造体数组
    // 初始化的时候,没有填写的字段,主动设置为 0
    struct STUDENT stcTest[5] = {{"xiaoming",18,176,75.5,{100,5000.5,2}},
        {"xiaohong",18,175,76.5,{200,8000.5,3}},
        {"xiaobai"}
    };
    // 如果要拜访
    printf("%s", stcTest[1].szName);
    printf("%d\n", stcTest[1].nAge);

    for (int i = 0; i < 5; i++)
    {printf("%s %d\n", stcTest[i].szName, stcTest[i].nAge);
    }

    return 0;
}

2. 联合体

联合体的语法和构造体语法简直是一摸一样的。关键字不一样。
他们有什么区别???
构造体和联合体最大的区别在于:
联合体的所有成员(字段),都是共享内存空间的。联合体的大小,就是最大的成员的大小。
构造体的每一个成员(字段)都是独自占用内存空间的。

#include <stdio.h>
struct TEST1
{
    char cCh;
    int nNum;
};

union TEST2
{
    char cCh;
    int nNum;
};


int main()
{struct TEST1 stcTest1 = {0};
    printf("%c %d\n", stcTest1.cCh, stcTest1.nNum);
    stcTest1.cCh = 'A';
    printf("%c %d\n", stcTest1.cCh, stcTest1.nNum);
    stcTest1.nNum = 100;
    printf("%c %d\n", stcTest1.cCh, stcTest1.nNum);
    union TEST2 stcTest2 = {0};
    printf("%c %d\n", stcTest2.cCh, stcTest2.nNum);
    stcTest2.cCh = 'A';
    printf("%c %d\n", stcTest2.cCh, stcTest2.nNum);
    stcTest2.nNum = 100;
    printf("%c %d\n", stcTest2.cCh, stcTest2.nNum);


    return 0;
}

3. 类型定义

typedef 是可能给数据类型起一个新的名字(别名)

**typedef 和构造体合在一起的用法:

#include <stdio.h>

typedef struct _TEST1
{
    char cCh;
    int nNum;
}TEST1;
struct _TEST2
{
    char cCh;
    int nNum;
}TEST2;
int main()
{
    // 应用 typedef 就能够在定义变量的时候,少写一个 struct
    TEST1 stc = {'A',100};

    struct _TEST2 stc1 = {'A',100};
    return 0;
}

4. 堆空间

4.1 根本用法

申请空间:malloc
开释空间:free

#include <stdio.h>
#include <stdlib.h>
int main()
{
    // 局部变量所在的空间,称之为【栈空间】// 栈空间 是主动申请,主动开释的,大小都是编译器确定的
    // 这个变量,占用空间,固定的就是 4 个字节
    int a = 0;
    // 这个数组,占用空间,固定的就是 40 个字节
    int arr[10] = {0};
    int n = 0;

    printf("通知我,你须要多少个 int:");
    scanf_s("%d", &n);
    //malloc 申请堆空间,// 它会将申请到的堆空间的地址,作为返回值
    // 返回进去,那么此时,存储地址,应该是指针
    // 个别申请堆空间,就应该配合指针一起应用
    int* p1 = (int*)malloc(n*sizeof(int));

    // 这里申请 4 个字节
    int* p2 = (int*)malloc(1 * sizeof(int));

    // 当咱们不想应用堆空间了,须要本人被动去开释
    free(p1);
    p1 = nullptr;
    free(p2);
    p2 = nullptr;
    return 0;
}

4.2 常见用法

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int n = 0;
    int* p1 = nullptr;
    printf("通知我,你须要存储多少集体的年龄:");
    //scanf_s("%d", &n);
    ////int arr[n] = {0}; 数组长度必须是常量
    //int* p1 = (int*)malloc(n * sizeof(int));

    //for (int i = 0; i < n; i++)
    //{//    printf("请输出一个年龄:");
    //    scanf_s("%d",&p1[i]);
    //}
    int arr[10] = {4,2,1,4,6,8,0,7,8};
    p1 = arr;
    for (int i = 0; i < 10; i++)
    {printf("%d", p1[i]);
    }
    return 0;
}

4.3 数组和堆的区别

1. 数组的长度是常量,堆在申请的时候,既能够是常量,也能够是变量。
2. 堆须要咱们本人开释,数组不须要咱们本人开释,主动开释的

#include <stdio.h>
#include <stdlib.h>
int main()
{
    //1. 数组的长度是常量,堆在申请的时候,既能够是常量,也能够是变量。int n = 0;
    int* p1 = nullptr;
    //printf("通知我,你须要存储多少集体的年龄:");
    //scanf_s("%d", &n);
    //int arr[n] = {0}; // 数组长度必须是常量
     p1 = (int*)malloc(n * sizeof(int));
    //2. 堆须要咱们本人开释,数组不须要
    //while (true)
    //{//    malloc(2);
    //}
    while (true)
    {int nArr[10];
    }
    return 0;

4.4 两个内存操作函数

memcpy 内存拷贝
memset 给一块内存,每个本人都设置为雷同的数据。

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
int main()
{
    int* p1 = nullptr;
    p1 = (int*)malloc(10 * sizeof(int));
    memset(
        p1,             // 起始地址
        0,              // 要设置的初始值 
        10 * sizeof(int)// 一共设置多少个字节
    );
    p1[0] = 100;
    p1[1] = 200;
    int* p2 = nullptr;
    p2 = (int*)malloc(10 * sizeof(int));

    memcpy(
        p2,
        p1,
        10 * sizeof(int)
    );
  free(p1);
  p1 = nullptr;
  free(p2);
  p2 = nullptr;
    return 0;
}

4.5 一些概念

内存透露:如果内存申请了,然而遗记开释了,此时就称之为内存透露,内存会被始终占用到开释或者程序完结。
悬空指针:一个指针被开释了,然而没有被置为 null,指针就还会指向这个被开释的区域,如果无心应用到了,就会解体。
free(p1)
//….
*p1 = 100;
野指针:没有被初始化的指针
int * p2;
*p2 = 100;
运行的程序,内存区域的划分:
1. 堆区 动静生存期 申请就存在 开释就销毁

a. malloc 申请的内存,都在堆区

2. 栈区 主动生存期 进入作用域 主动申请 来到作用域 主动销毁

a. 局部变量,还有参数,都是在栈区

3. 全局数据区 动态生存期 程序开始运行就申请 程序完结 才销毁

a. 全局变量,动态局部变量  都在全局数据区

4. 常量区 动态生存期 程序开始运行就申请 程序完结 才销毁

a. 字符串都在常量区

5. 代码区 动态生存期 程序开始运行就申请 程序完结 才销毁

a. 程序的指令,都在代码区

补充材料:
C 语言内存分区 -(堆,栈,全局 / 动态存储区,自在存储区,代码区)与可执行程序的三段 -(Text 段,Date 段,Bss 段)

正文完
 0