• 【C语言】初阶C语言零碎知识点(查漏补缺)



    目录

    1、sizeof和strlen

    2、函数形参(很重要)

    2.1传值调用

    2.2传址调用

    3、函数的嵌套调用和链式访问

    4、隐式类型转换

    4.1整型提升

     4.2算术转换

    5、野指针

    6、const

    7、写一个函数返回参数二进制中 1 的个数。


    各位朋友大家好呀!在学c语言的时候,总会遇到些零碎的知识点或者名词,每个知识点单独水一篇脸上挂不住,所以缝合成一篇发布了,赶紧来查漏补缺吧。

    c3d9868730067527dec42f67d83af642.gif1、sizeof和strlen

    sizeof是关键字,以数组为例,'\0'也会被计算,所以不要管数组里边放了什么数据,直接数元素个数即可,再乘数据类型大小,单位是字节。

    strlen是库函数,遇到'\0'就停止,不计算'\0'。

    1. #include <stdio.h>
    2. #include <string.h>
    3. int main()
    4. {
    5. char arr[] = "abcdef";
    6. printf("%zu %zu", sizeof(arr), strlen(arr));//输出7和6。
    7. return 0;
    8. }

    6d722a66eee5ccad55e7b517a714757d.gif2、函数形参(很重要)

    形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。

    77ca0ad84d3766ab20fa4bc2557a87bd.gif2.1传值调用

    传值调用时,形参是实参的一份临时拷贝,形参的改变不会改变实参。

    65bd0976fcd0b476a4a1b7979f6116dc.gif2.2传址调用

    可以通过传入实参的地址,在函数内部操纵实参。

    传址调用的总结:先明确想要修改的值是什么类型,设定的形参类型需要比修改类型多一颗*哦!

    举例:

    1、冒泡排序函数需要通过修改数组内的元素,从而达到排序的目的。那么我们需要传入一级指针。

    2、无头单链表的头插,需要改变单链表的头指针,所以要传入二级指针。

    29aa5123ad46328e7a61a5a143708045.gif3、函数的嵌套调用和链式访问

    b66c197ac4eb2c592b59723747dcc670.gif3.1函数的嵌套调用

    函数里边调用函数。

    55ce246120cad9db5a0ae1ce1ecf0db9.gif3.2函数的链式访问

    把一个函数的返回值作为另外一个函数的参数。

    5acdb68989b1b529b1418e241a8bbf0e.gif4、隐式类型转换

    隐式转换指的是不需要用户干预,编译器私下进行的类型转换行为。很多时候用户可能都不知道发生了哪些转换。

    2be8ebd81cbe16601701c68781eaab3e.gif4.1整型提升

    表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。

    所以char和short类型在运算时,会先转换为int或unsigned int参与运算,运算完成后将多余的二进制位截断,重新储存至原类型内。

    整型提升的方式

    1、有符号数据类型高位补符号位

    2、无符号数据类型高位补0

    整型提升的例子

    先来看一段代码

    当程序运行到char c=a+b时,将会发生整型提升。

    125在内存中的补码是01111101

    5在内存中的补码是00000101

    整型提升后125和5的补码分别是:

    00000000000000000000000001111101

    00000000000000000000000000000101

    相加所得c的补码:

    00000000000000000000000010000010
    把c截断的补码:

    10000010

    以%d的形式打印c,再次发生整型提升得到c的补码:

    11111111111111111111111110000010

    c的原码:

    10000000000000000000000001111110(-126)

    b66c197ac4eb2c592b59723747dcc670.gif 4.2算术转换

    两个不同类型的操作数在运算时,排名较低的类型将被转换为排名较高的数据类型。以下是数据类型的排名。

    long double
    double
    float
    unsigned long int
    long int
    unsigned int
    int

    注意:若将排名较高的数据类型转换为排名较低的数据类型,将会丢失精度。

    1. float f = 3.14;
    2. int num = f;//隐式转换,会有精度丢失

    f2aa035dda1d6baf08bbde31c00dfa59.gif5、野指针

    野指针:指针指向的位置是不可知的。

    9e271ef533c4e40790e86179d1d00c72.gif5.1野指针的成因

    1、指针未初始化

    1. #include <stdio.h>
    2. int main()
    3. {
    4. int *p;//局部变量指针未初始化,默认为随机值
    5.    *p = 20;
    6. return 0;
    7. }

    2、指针越界

    1. #include <stdio.h>
    2. int main()
    3. {
    4. int arr[10] = { 0 };
    5. int* p = arr;
    6. for (int i = 0;i < 12;i++)
    7. {
    8. *(p++) = i;//程序报错
    9. }
    10. return 0;
    11. }

    3、指针指向的空间销毁/释放

    1. #include <stdio.h>
    2. int* test()
    3. {
    4. int a = 10;
    5. return &a;
    6. }
    7. int main()
    8. {
    9. int*p = test();//由于a在出了函数范围即被销毁,可以认为此时p是野指针
    10. if (p != NULL)
    11. {
    12. printf("%d\n", *p);//
    13. }
    14. return 0;
    15. }

    动态内存管理中,需要将堆区的指针及时置空(后续文章进行讲解)

    9b88c98924d96f6e5f1ac5b67512a845.gif5.2如何规避野指针

    1. 指针初始化
    2. 小心指针越界
    3. 指针指向空间释放即使置 NULL
    4. 避免返回局部变量的地址
    5. 指针使用之前检查有效性

    5e90ab0d2d9606fc9a5d37568a20a0be.gif6、const

    1. const int* p=arr[10];//保护arr不被修改(可用下一级指针进行更改)
    2. int* const p=arr[10];//保护指针p不被修改(可用下一级指针进行更改)
    3. const int* const p=arr[10];//保护指针p和数组都不可被修改(可用下一级指针进行更改)

    2cb5fd41d45e6fcc27718aa623c50eb5.gif7、写一个函数返回参数二进制中 1 的个数。

    09e8372bd1d2f8f4f7223dd99b9740b7.gif7.1除2法

    1. int count_num_of_1(unsigned int n)
    2. {
    3. int count = 0;
    4. while (n)
    5. {
    6. if ((n % 2) == 1)
    7. {
    8. count++;
    9. }
    10. n /= 2;
    11. }
    12. return count;
    13. }

    注意形参必须是unsigned整型,可以兼顾正负数的二进制位1的统计。

    3e5a3a720647b974ad90902f7843456d.gif7.2使用n &(1<<i)

    1. int count_num_of_1(int n)
    2. {
    3. int i = 0;
    4. int count = 0;
    5. for (i = 0; i < 32; i++)
    6. {
    7. if ((n &(1<<i)) != 0)
    8. {
    9. count++;
    10. }
    11. }
    12. return count;
    13. }

    让1的二进制位分别左移0-31位后与n按位与,即可得到n的二进制位的1的个数。左移较右移的优点:不用考虑当前编译器是算术右移还是逻辑右移。

    a9ae404f79a220cd5ca1731011dc51ef.gif7.3使用n = n & (n - 1)

    1. int count_num_of_1(int n)
    2. {
    3. int count = 0;
    4. while (n)
    5. {
    6. n = n & (n - 1);
    7. count++;
    8. }
    9. return count;
    10. }

    让n与n-1按位与,计算出n的二进制位的个数。类似抽丝剥茧,把n的二进制位上的1,从低位到高位,层层统计出来。


    关注!点赞!评论!收藏!关注!点赞!评论!收藏!关注!点赞!评论!收藏!关注!点赞!评论!收藏!关注!点赞!评论!收藏!

  • 相关阅读:
    Spring简单例子引入Spring要点
    odoo16原码安装后,psycopg2模块出错,应用除了网站其它都安装不了
    使用python压缩文件夹
    清朝盛衰的六个时间点!
    前端开发面试题之http和https详解
    索引的创建和设计原则
    中级C++:map和set的迷你实现
    Nature Microbiology | SeqCode:基于序列数据描述的原核生物命名规则
    人工智能、深度学习、机器学习常见面试题281~300
    Springboot利用Security做OAuth2授权验证
  • 原文地址:https://blog.csdn.net/gfdxx/article/details/124883097