• 初识C语言



    该篇博客主要是了解C语言语法构造,达到对C语言有一个大概得认识,不进行深度讲解,后序会对每一知识点进行深度解剖。

    什么是C语言?

    • 人与计算机交流的语言。
    • 常用于写底层软件开发
      在这里插入图片描述
    • C语言的国际标准(ANSI),现如今常用C99
      在这里插入图片描述
    • C语言是一门面向过程的语言。
    • 编译器的作用就是将各种语言转化为计算机能识别的二进制进行编译解释。

    在这里插入图片描述

    第一个C语言程序

    //标准形式 
    #include 
    int main()
    {
        return 0;
    }
    
    //打印hello world
    #include 
    int main()
    {
        printf("hello world");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    观察上述代码,我们可以得出以下C语言特性:

    • C语言代码始终从main函数第一行开始执行,main函数是程序的入口,故C语言代码中不能没有main函数。
    • 编写C语言代码时,尝尝会包含stdio.h头文件,便于编写输入输出函数。
    • xxx.h —— C语言中的头文件。
    • xxx.c —— C语言中的源文件。
    • 源文件的名字要有意义。

    数据类型

    在这里插入图片描述
    思考以下两个问题:

    1. 为什么会出现这么多的类型?
    2. 每种类型的大小是多少?

    在解决上述问题前,先介绍一个单目操作符——sizeof(),注意!!,sizeof是一个操作符,不是函数!!
    作用:用于计算大小(单位为字节)
    涉及到单位,我们就不得不讲讲计算机中的存储单位了,我们都知道计算机存储时是以二进制存储的,下面是关于计算机单位的类别:

    • 比特(bit):计算机中最小的单位
    • 字节(byte)
    • KB
    • MB
    • GB
    • TB
    • PB

    之间的关系:

    • 1byte = 8bit
    • 1KB = 1024byte
    • 1MB = 1024KB
    • 1GB = 1024MB
    • 1TB = 1024GB
    • 1PB = 1024TB

    了解清楚以上知识后,我们就可以解决问题2了,以下我们用代码说话:

    //计算每一种类型的大小(字节)
    #include
    int main()
    {
    	printf("%d\n", sizeof(char));
    	printf("%d\n", sizeof(short));
    	printf("%d\n", sizeof(int));
    	printf("%d\n", sizeof(long));
    	printf("%d\n", sizeof(long long));
    	printf("%d\n", sizeof(float));
    	printf("%d\n", sizeof(double));
    	printf("%d\n", sizeof(long double));//更高精度的浮点型
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述
    我们发现一个问题,long称为长整型,为什么结果和int一样都为4字节呢?
    因为在C语言标准下:sizeof(long) >= sizeof(int),因此long与int可以相等。

    对于问题1,为什么出现这么多的类型,是因为在现实生活中,有着许多不同的物品,存在这么多的类型,其实是为了更加丰富的表达生活中的各种值,而最重要的原因是为了合理利用空间,以达到节省空间的效果。

    还需要注意的一个点是关于汉字是否能用char类型进行存储,我们用代码验证:
    在这里插入图片描述
    可以看到是不行的,因为一个汉字是占用2个字符的,因此要用字符串进行存储:
    在这里插入图片描述

    变量、常量

    生活中的有些值是不变的,如:圆周率,性别,身份证号码,血型等等;有些值是可变的,如:年龄,体重,薪资
    下面将介绍C语言中的常量与变量。
    
    • 1
    • 2

    变量

    1、定义方法:类型 变量名 = 数值

    //定义变量的方法
    #include
    int main()
    {
    	char ch = 'w';
    	int num = 20;
    	float m = 45.5f;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • float weight = 45.5,其实这种变量定义的方法是错误的,因为对于编译器来说,45.5(或者说小数)默认为double类型,因此使用float类型需要:floatweight = 45.5f
    • 当定义变量时,养成给初始化的习惯,在创建变量时就给它一个值。

    2、变量的命名

    • 只能由字母(包括大写和小写)、数字和下划线( _)组成。
    • 不能以数字开头。
    • 长度不能超过63个字符。
    • 变量名中区分大小写的。
    • 变量名不能使用关键字。
    • 要有意义

    3、变量的分类:

    • 局部变量:在{ }部定义的变量就是局部变量
    • 全局变量:在{ }部定义的变量就是全局变量
    //局部变量
    #include
    int main()
    {
    	int global = 2023;
    	printf("%d\n", global);
    	return 0;
    }
    
    //全局变量
    #include
    int global = 2023;
    int main()
    {
    	printf("%d\n", global);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    如果当全局变量与局部变量同名时,该执行哪一个呢?通过代码验证:

    //当全局变量与局部变量同名时
    #include
    int global = 2023;
    int main()
    {
    	int global = 2022;
    	printf("%d\n", global);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述
    可以得到,全局变量和局部变量的名字可以相同,但是在使用时局部优先。

    4、变量的使用

    //变量的使用
    //简单的两数求和
    #include
    int main()
    {
    	int num1 = 0;
    	int num2 = 0;
    	int sum = 0;
    	printf("输入两个操作数:>");
    	scanf("%d %d", &num1,&num2);
    	sum = num1 + num2;
    	printf("sum = %d\n", sum);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    观察上述代码,这是一个求两数之和的代码,需要我们输入两个输,在输出两数之和,这里我们介绍一下输入函数——printf,输出函数——scanf。
    printf:
    C语言自带的一个库函数,其使用前需要包含头文件——stdio.h,用于输出内容到控制台。
    scanf:
    C语言自带的一个库函数,其使用前需要包含头文件——stdio.h,用于输入数据

    5、变量的作用域和生命周期
    作用域:通俗点讲就是该量在此处能不能用
    生命周期:通俗点讲就是该量在此处还能不能用

    局部变量的作用域:是局部变量所在的局部范围,即{}内

    //局部变量作用域
    #include
    int main()
    {
        int a = 10;
        printf("%d\n", a);
    
        {
            int b = 10;
            printf("%d\n", b);
        }
        printf("%d\n", b);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述
    此原因是因为局部变量的作用域为局部范围内,即{}内,出了{}后不能使用。

    全局变量的作用域:全局变量的作用域是整个工程

    #include
    
    int a = 10;
    void test()
    {
        printf("%d\n", a);
    }
    int main()
    {
        printf("%d\n", a);
            test();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    可以运行,因为全局变量的作用域为整个工程。
    我们思考,如果有两个源文件呢?全局变量是否还能使用?用代码说话:

    add.c
    int a = 10;
    
    test.c
    #include
    int main()
    {
        printf("%d\n", a);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    这种有多个.c源文件的情况下,按上述代码执行是会报错的,因此在add.c定义的int a = 10全局变量,在test.c中我不认识,这时候就要用到C语言中的一个关键词—— extern,该关键词的作用是声明外部符号(也就是让本源文件认识来自外部文件的符号),则正确形式如下:

    add.c
    int a = 10;
    
    test.c
    #include
    extern int a;
    int main()
    {
        printf("%d\n", a);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    因此可以得出,全局变量的作用域是很广泛的,作用于整个项目,但随之带来的就是不够安全的缺点,造成人人都能使用的情况。

    生命周期:
    在这里插入图片描述
    程序的生命周期与全局变量的生命周期是一样的,因为程序结束最终是执行完main函数中的return 0

    常量

    1、常量的分类

    • 字面常量
    • const 修饰的常变量
    • #difine 定义的标识符常量
    • 枚举常量

    下面将一一介绍:
    1、字面常量:就是字面值,如:

    int main()
    {
        100;//整形字面值
            'A';字符字面值
            12.5;浮点型字面值
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    2、const 修饰的常变量:当给变量+const时,这个变量就不能被改变!!(const具有常属性),但num本质上还是变量,但从语法层面上为常量不能修改。
    
    • 1
    int main()
    {
        const int num = 10;
        num = 20; //err
        printf("%d\n", num);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    看如下情况:数组的大小为常量,当用const修饰n时,n变为常变量,那可以作为常量作为数组的大小吗?答案是不能的,因为n本质上还是变量!

    int main()
    {
        const int n = 10;
        int arr[n] = {0};//err
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    但在有些编译器中,支持C99标准,引入了变长数组的语法,看如下:

    #include
    int main()
    {
        int n = 10;
        int arr[n];//变长数组
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    上述代码是正确的,可以将变量作为数组的大小,但需要注意的是变长数组不能初始化

    3、#define 定义的标识符常量
    
    • 1
    #include
    //可定义在全局
    #define MAX 1000
    int main()
    {
        //也可定义在局部
        #define MAX 1000
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    4、枚举常量
    
    • 1

    枚举关键字——enum,枚举的意思是把可能的取值一一列举出来
    如性别是可以一一列举出来的、三原色也可以

    #include
    //声明的一个表示性别的枚举类型
    enum Sex
    {
            //下面三个符号是Sex这个枚举类型的未来的可能取值
            //枚举常量
        MALE,
        FEMALE,
        SECRET
    };
    int main()
    {
        enum Sex s = FEMALE;
        printf("%d\n", MALE);//0
        printf("%d\n",FEMALE);//1
        printf("%d\n",SECRET);//2
        FEMALE = 5; //err 常量不能修改
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    枚举常量也是有值的,如果创建枚举常量时都没有给定初始值,则按先后顺序,从0依次往后,如果给定初始值,则取初始值,如给定个别枚举常量初始值,见如下:

    #include
    //声明的一个表示性别的枚举类型
    enum Sex
    {
        MALE = 5,
        FEMALE,
        SECRET
    };
    int main()
    {
        enum Sex s = FEMALE;
        printf("%d\n", MALE);//5
        printf("%d\n",FEMALE);//6
        printf("%d\n",SECRET);//7
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    可以得出,在其之后没有赋初始值的枚举常量是依次递增的。

    字符串+转义字符+注释

    字符串

    由多个字符组成的一串字符称为字符串,C语言用""引起来的一串字符表示字符串。注意:字符串的结束标志是一个’\0’,不做展示,如:(字符串用字符数组来表示)

    形式1char arr1[] = "abc";  => 'a','b','c','\0' (因此这个字符数组为4,包含'\0')
    形式2char arr2[] = {'a', 'b', 'c'}; => 'a','b','c' (因此这个字符数组为3,不包含'\0'
    • 1
    • 2
    //突出字符串结束标志:'\0'的重要性
    #include
    int main()
    {
        char arr1[] = "bit";
        char arr2[] = { 'b', 'i', 't' };
        char arr3[] = { 'b', 'i', 't', '\0' };
        printf("%s\n", arr1);
        printf("%s\n", arr2);
        printf("%s\n", arr3);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述
    字符串末尾隐藏着一个’\0’,作为结束标志。通过上述代码可以看出,arr1与arr3正常打印,arr2打印出随机值,这是因为%s为打印字符串,会寻找’\0’,当寻找到’\0’后,打印出’\0’之前的值,而arr2中寻找不到’\0’,故会一直往下打印,打印出随机值。
    在这里插入图片描述
    在这里插入图片描述
    这里我们介绍一个库函数——strlen(),用于计算字符串长度,其以’\0’为结束标志,计算’\0’之前的字符个数。

    //strlen() —— 计算字符串长度
    #include
    int main()
    {
        char arr1[] = "bit";
        char arr2[] = { 'b', 'i', 't' };
        printf("%d\n", strlen(arr1));
        printf("%d\n", strlen(arr2));
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    转义字符

    转义字符 			释义
    \? 				在书写连续多个问号时使用,防止他们被解析成三字母词
    \' 				用于表示字符常量'
    \“				用于表示一个字符串内部的双引号
    \\ 				用于表示一个反斜杠,防止它被解释为一个转义序列符。
    \a 				警告字符,蜂鸣
    \b 				退格符
    \f 				进纸符
    \n 				换行
    \r				回车
    \t 				水平制表符
    \v 				垂直制表符
    \ddd 			ddd表示1~3个八进制的数字。 如: \130 == 'X'
    \xdd 			dd表示2个十六进制数字。 如: \x300
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    这里重点关注:

    \ddd 			ddd表示1~3个八进制的数字。 如: \130 == 'X'
    \xdd 			dd表示2个十六进制数字。 如: \x30
    
    • 1
    • 2

    注释

    // —— 这种注释为C++语言的注释风格,不支持嵌套注释
    /* */ ——这种注释为C语言的注释风格,但不支持嵌套注释
    
    • 1
    • 2

    选择语句

    这里不作过多讲解,后面博客会单独讲解!

    1. if else 语句
    2. Switch 语句
    #incude<stdio.h>
    int main()
    {
    	int coding = 0;
    	printf("你会敲代码吗?(选择1 or 0):>");
    	scanf("%d", &coding);
    	if(1 == coding)
    	{
    		printf("坚持,你会收获一个好offer\n");
    	}
    	else
    	{
    		printf("放弃,回家吃土\n");
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    #incude<stdio.h>
    int main()
    {
    	int coding = 0;
    	printf("你会敲代码吗?(选择1 or 0):>");
    	scanf("%d", &coding);
    	sitch(coding)
    	{
    		case 1:
    			printf("坚持,你会收获一个好offer\n");
    			break;
    		case 0:
    			printf("放弃,回家吃土\n");
    			break;
    		default:
    			printf("无该选项\n");
    			break;
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    循环语句

    这里不作过多讲解,后面博客会单独讲解!

    1while语句
    2for语句
    3do...while语句
    
    • 1
    • 2
    • 3

    函数

    这里不作过多讲解,后面博客会单独讲解!

    #include
    int main()
    {
    	int num1 = 0;
    	int num2 = 0;
    	int sum = 0;
    	printf("输入两个操作数:>");
    	scanf("%d %d", &num1,&num2);
    	sum = num1 + num2;
    	printf("sum = %d\n", sum);
    	return 0;
    }
    
    //可以将上述代码改造为:
    #include
    int Add(int x, int y)
    {
    	int z = x+ y;
    	return z;
    }
    
    int main()
    {
    	int num1 = 0;
    	int num2 = 0;
    	int sum = 0;
    	printf("输入两个操作数:>");
    	scanf("%d %d", &num1,&num2);
    	sum = Add(num1,num2);
    	printf("sum = %d\n", sum);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    函数的特点就是简化代码,代码复用
    在这里插入图片描述
    在这里插入图片描述

    数组

    一组相同类型元素的集合

    数组的定义

    int arr1[10] = {1,2,3,4,5,6,7,8,9,10}; //定义了一个整形数组,这种为完全初始化
    int arr2[10] = {1,2,3,4,5}; //不完全初始化,剩余5个元素会补充为0
    int arr3[] = {1,2,3,4,5,6,7,8,9,10}; //可以不给定数组大小,会根据初始化个数决定数组大小
    int arr4[10] = {0}; //和arr2一样,都为不完全初始化,只是第一个元素赋值为0
    
    • 1
    • 2
    • 3
    • 4

    数组的下标

    数组的每个元素都有一个下标,下标是从0开始的
    数组可以通过下标来访问
    在这里插入图片描述
    为一块连续的空间

    数组的使用

    #include
    int main()
    {
    	int i = 0;
    	int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    	for(i=0; i<10; i++)
    	{
    		printf("%d ", arr[i]);
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    注意:
    初始化时数组的[]表示数组的元素个数,
    当使用数组时的[]为访问数组中的元素,是一个操作符
    
    • 1
    • 2
    • 3

    操作符

    算数操作符:+ - * / %
    移位操作符:>> <<
    位操作符:& ^ |
    赋值操作符:= += -= *= /= &= ^=  |=   >>=  <<=
    
    单目操作符:
    !      逻辑反操作
    -      负值
    +      正值
    &      取地址
    sizeof    操作数的类型长度(以字节为单位)
    ~      对一个数的二进制按位取反
    --      前置、后置--
    ++      前置、后置++
    *      间接访问操作符(解引用操作符)
    (类型)    强制类型转换
    
    关系操作符:
    >
    >=
    <
    <=
    !=    用于测试“不相等”
    ==    用于测试“相等”
    
    逻辑操作符:
    &&   逻辑与
    ||   逻辑或
    
    条件操作符:exp1 ? exp2 : exp3
    
    逗号操作符:exp1,exp2,exp3,....expN
    
    下标引用、函数调用、结构成员访问操作符:
    [] () . ->
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    以下为使用操作符需注意事项:

    1. %只能作用于整数
    #include
    int main()
    {
    	int a = 10;
    	int b = 3;
    	printf("%d\n", a%b); // == 3
    
    	float c = 3.33f;
    	printf("%d\n", a%c); // err 报错
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1. 当除号两端都为整数的话,执行的是整数除法如:
    #include
    int main()
    {
       int a = 10;
       int b = 3;
       printf("%d\n", a/b); // == 3
       return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    如果想得到小数,除号两端必须有一端为小数:

    #include
    int main()
    {
    	int a = 10;
    	float b = 3.3f;
    	printf("%.2f\n", a/b); // == 3.33
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. 单目表示只有一个操作数,如:
    int main()
    {
        !a,-a,+a,*a  ——>只有一个操作数
        a + b  ——>+两边有两个操作数,为双目
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 注意:sizeof为单目操作符,并不是关键字!!!,是一种操作符

    2. sizeof(数组名) ——>表示计算数组总大小(字节),如int arr[10];结果就为40,因为int字节为4,数组有10个元素,因此40,经常计算元素总个数的方法:
      sizeof(arr) / sizeof(arr[0]);

    3. &&——>逻辑与(并且);||——>逻辑或(或者)

    4. 逗号表达式的特点是:表达式从左向右依次计算,但是整个表达式的结果是最后一个表达式的结果。

    常见关键字

    在这里插入图片描述
    1、auto很少使用,表示一种变量的存储类型,表示自动,如局部变量是自动创建自动销毁的,正确写法为:auto int a = 10; 只是可以省略
    2、break — 循环、Switch语句
    3、continue — 循环
    4、extern用于外部符号声明
    5、register表示变量的存储类型,表示寄存器,修饰变量会被放到寄存器中
    6、signed — 有符号的
    7、unsigned — 无符号的
    8、static — 静态的
    9、struct 结构体
    10、typedef — 类型重命名
    11、union — 联合体

    在这里插入图片描述在这里插入图片描述
    auto —— 为一种存储状态,为自动的,看如下代码:

    //关于auto
    int main()
    {
    	auto int a = 10;
    	printf("%d\n", a);
    
    	//等价于:
    	int a = 10;
    	printf("%d\n", a);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    局部变量默认省略auto,其作用是进入自动创建,结束自动销毁。

    register —— 寄存器(可以放在变量前,提高速度)
    在这里插入图片描述

    #include
    int main()
    {
        register int a = 100;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    typedef — 类型定义(简单来说就是将类型重命名)

    #include
    typedef int uint;
    int main()
    {
        //等价!
        int a = 100; => uint a = 100
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    static —— 静态的
    作用:
    1.修饰局部变量
    2.修饰全局变量
    3.修饰函数

    1.修饰局部变量(改变了局部变量的生命周期,出了作用域依然存在,到程序结束,生命周期才结束)
    
    • 1
    void test()
    {
        int a = 1;
        a++;
        printf("%d ", a);
    }
    
    int main()
    {
        int i = 0;
        while(i < 10)
        {
            test();
            i++;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    test函数中的局部变量a,每次调用都会创建与销毁
    在这里插入图片描述
    结果为10个2,为什么?因为int a = 1;为函数test里的局部变量,每次调用会创建,出函数会销毁,因此每次都是一样的值,再看如下代码:

    void test()
    {
            static int a = 1;
            a++;
            printf("%d ", a);
    }
    int main()
    {
        int i = 0;
        while(i < 10)
        {
            test();
            i++;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述
    结果为2~11,利用static修饰局部变量时,该局部变量出了函数不销毁,保留上一次值,存储位置发生变化,使得局部变量出了作用域不销毁,影响了生命周期。因为一个普通的局部变量是放在栈区的,当被static修饰时,放在静态区。但需要注意,被static修饰的局部变量是可以修改的!

    2.修饰全局变量(外部链接失效,只能在本源文件使用,不能在其他源文件内使用)
    
    • 1
    add.c文件
    int g_val = 2023;
    
    test.c文件
    extern int g_val;
    int main()
    {
        printf("%d\n", g_val);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    代码正确,可以使用int g_val = 2023; 因为全局变量具有外部链接属性,只需要extern声明,就可以跨文件使用。

    add.c文件
    static int g_val = 2023;
    
    test.c文件
    extern int g_val;
    int main()
    {
        printf("%d\n", g_val);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    当使用static修饰add.c文件中的全局变量时,外部链接属性就失效了,变成了内部链接属性,故在test.c文件中使用不了,影响了变量的作用域

    3.修饰函数(外部链接失效,只能在本源文件使用,不能在其他源文件内使用)
    
    • 1
    Add.c文件
    int Add(int x, int y)
    {
        return x + y;
    }
    
    test.c文件
    extern int Add(int, int); //函数声明,形参中可只告诉类型
    int main()
    {
        int a = 10;
        int b = 20;
        int sum = Add(a, b);
        printf("%d\n", sum);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    代码正确,可以使用函数 ,因为函数具有外部链接属性,只需要适当的声明,就可以跨文件使用。

    Add.c文件
    static int Add(int x, int y)
    {
        return x + y;
    }
    
    test.c文件
    extern int Add(int, int); //函数声明,形参中可只告诉类型
    int main()
    {
        int a = 10;
        int b = 20;
        int sum = Add(a, b);
        printf("%d\n", sum);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    当使用static修饰add.c文件中的函数时,外部链接属性就失效了,变成了内部链接属性,故在test.c文件中使用不了,影响了函数的作用域。

    另外再补充内存中的存储空间区域:
    在这里插入图片描述
    栈区 —— 局部范围,使用后就销毁
    静态区 —— 程序结束生命周期才结束

    define定义常量和宏

    1.定义常量:
    
    • 1
    #define M 100
    #define STR "hello world"
    
    int main()
    {
        printf("%d\n", M);
        printf("%s\n", STP);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    2.定义宏:(跟函数差不多,可以传参)
    
    • 1
    #define MAX(x, y) (x>y?x:y)
    int main()
    {
        int a = 10;
        int b = 20;
        int m = MAX(a, b);
        printf("%s\n", STP);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    3.宏不考虑类型,其实是一种替换,具体看如下:
    
    • 1
    #define M 100
    #define STR "hello world"
    int main()
    {
        //printf("%d\n", M);
        //printf("%s\n", STP);
    
        printf("%d\n",100);
        printf("%s\n","hello world");
    }
    
    #define MAX(x, y) ((x)>(y)?(x):(y))
    int main()
    {
        int a = 10;
        int b = 20;
        //int m = MAX(a, b);
        int m = ((a)>(b)?(a):(b));
        printf("%s\n", STP);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    指针

    内存

    谈到指针,就要先了解内存的概念。内存就如酒店房号,可以通过房号精准的找到。
    在这里插入图片描述
    并且,1个内存单元为1字节
    在这里插入图片描述
    在这里插入图片描述

    指针的使用

    1.打印变量地址
    
    • 1

    变量是存储在内存中的(在内存中分配空间的),每个内存单元都有地址,所以变量也是有地址的,我们可以取出变量的地址看看:

    //指针的使用
    int main()
    {
    	int a = 10;
    	printf("%p\n", &a); //%p 用于打印地址
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述
    我们知道一个内存单元为1字节,当存储int类型的变量时,需要4个字节。当我们取该变量地址时,往往是拿取4个字节中,最小的地址。

    2.指针的大小(64位——8字节,32位——4字节)
    
    • 1
    //指针的大小
    int main()
    {
    	int a = 10;
    	float b = 2.2f;
    	double c = 3.3;
    	char ch = 'W';
    
    	int* p1 = &a;
    	float* p2 = &b;
    	double* p3 = &c;
    	char* p4 = &ch;
    
    	printf("%d\n", sizeof(int*));
    	printf("%d\n", sizeof(float*));
    	printf("%d\n", sizeof(double*));
    	printf("%d\n", sizeof(char*));
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在这里插入图片描述
    我们发现不管什么类型的指针,计算大小均为4字节,故指针的大小跟类型无关,再看如下:
    在这里插入图片描述
    为什么又变为8字节呢?这是因为在计算机位数为64位时,指针的大小为8字节,而在32位时,指针大小为4字节。对应的在编译器上就是64位——x64,32位——x86

    3.指针的定义与使用
    
    • 1
    //指针的使用
    int main()
    {
    	int num = 10;
    	int* p = &num;
    	*p = 20;
    	printf("%d\n", *p);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述
    故可以通过指针获取变量的地址,然后再进行修改。
    上述代码中,p为指针变量,用于存储地址,*p 等价于 num。

    结构体

    关键字:struct
    当要描述一个复杂对象时,就不能简单的使用单个的内置类型来描述,故提供了结构体来描述复杂对象。如:

    //声明结构体类型
    struct Student
    {
        char name[20];//名字
        int age;//年龄
        char id[15];//学号
        float score;//成绩
    };
    
    int main()
    {
        //结构体初始化:{},按照顺序以逗号隔开
        struct Student s1 = {"张三", 20, "2022010823", 88.5f};
    
        //打印结构体数据(.(访问操作符))
        printf("%s %d %s %f\n", s1.name, s1.age, s1.id, s1.score);
    
        //通过指针形式,方法一
        struct Student * ps = &s1;
        printf("%s %d %s %f\n"(*ps).name, (*ps).age, (*ps).id, (*ps).score);
    
        //通过指针形式,方法二(->箭头访问操作符,只用于指针->成员)
        //可以简洁,结构体指针->成员,不需要先解引用(*)
        struct Student * ps = &s1;
        printf("%s %d %s %f\n", ps->name, ps->age, ps->.id, ps->.score);
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    在这里插入图片描述

  • 相关阅读:
    Selenium经典面试题-多窗口切换解决方案
    学习常用算法——python
    enter ubuntu boot option in virt-manager
    多进程间通信学习之无名管道
    MacBook Pro M1虚拟机安装西门子TIA博图v16无法正常运行部分组件
    OPNET Modeler 软件的简单介绍与使用
    low power-upf-vcsnlp(四)
    药物研发检测记录模板--氰化物检查
    “音响”事件对国产豪华品牌车汽车厂商的警示
    【网络安全 --- 靶场搭建】sqlserver SQL注入靶场搭建(提供资源)
  • 原文地址:https://blog.csdn.net/m0_74088266/article/details/133779214