探索C语言的奥秘:从基础到高级

本文详细介绍了C语言的基础知识,包括其起源、程序结构、数据类型、运算符、控制流程、函数与模块化编程、递归、函数指针、数组与指针、结构体、联合体以及文件操作和预处理指令。文章强调了C语言在各个领域的广泛应用和学习C语言的重要性。
摘要由CSDN通过智能技术生成

 

引言

  • C语言的重要性和应用领域简介
  • C语言是一种通用的、高级的编程语言,它可以用于系统编程、应用软件开发、嵌入式系统开发、驱动程序编写、数据库管理等多个领域 。C语言的可移植性好,性能高,能够直接访问硬件地址,而且到达某个地址的时间非常短,这使得C语言天生适合开发操作系统或者嵌入式应用程序 。在最初的时候,C语言主要就是被应用在这两个领域。此外,由于C语言具有可移植性,适应性强,有时也被用作不同编程语言的中间语言,这样不同编程语言之间就可以共享组件/模块
  • 为什么学习C语言
  • 学习C语言有很多好处。首先,C语言是个低级语言,从总体上来说,低级的编程语言可以让你更好的了解计算机。其次,设备驱动程序和操作系统只能用C语言来编写。如果你想要得到一份编写微控制器程序的工作的时候,他们都是用C语言编写的。就因为不想学习一门新的语言,你就准备限制你能得到工作的机会吗?此外,C的程序比其他用别的语言写的程序,实现相同的功能,它用的代码行数更少,而它带来的运行效率却更快。有时候,你的程序所需要的速度,只有C语言能做到。最后,如果你学习过C语言,你就能学习现在任何的高级编程语言 。

第一部分:C语言基础

1.1 C语言的历史

  • C语言的起源
  • C语言的起源可以追溯到1960年代,当时美国AT&T公司的贝尔实验室(Bell Labs)的研究员Ken Thompson为了开发UNIX操作系统,需要一种简单、高效、可移植的编程语言。于是他开始着手开发一种新的编程语言,最终于1972年完成了这种语言的编译器和标准库,并将其命名为C语言 。
  • C语言的发展历程
  • C语言的发展历程可以追溯到1960年代,当时美国AT&T公司的贝尔实验室(Bell Labs)的研究员Ken Thompson为了开发UNIX操作系统,需要一种简单、高效、可移植的编程语言。于是他开始着手开发一种新的编程语言,最终于1972年完成了这种语言的编译器和标准库,并将其命名为C语言 。

    随着C语言在多个领域的推广、应用,一些新的特性不断被各种编译器实现并添加进来。 于是,建立一个新的“无歧义、与具体平台无关的C 语言定义”成为越来越重要的事情。1989年,美国国家标准协会(ANSI)推出了第一个C语言标准(ANSI X3.16),它规定了C语言的标准语法和标准库函数。1990年,国际标准化组织(ISO)接受了ANSI X3.16,并将其命名为ISO/IEC 9899:1990

1.2 程序结构

  • Hello World程序解析
  • C语言:#include <stdio.h>
    int main()
    { 
        printf("Hello, World!"); 
        return 0; 
    }

1.3 数据类型与变量

  • 常用基本数据类型:
    1、常用基本数据类型:
    char //一个字节
    short //两个字节
    int //四个字节
    long //4到8个字节长整型
    long long //更长的整型
    float //4个字节单精度浮点数 
    double //8个字节双精度浮点数

  • 声明和初始化变量

1.4 运算符和表达式

  • 算术运算符
  • +  -    /  %
  • 转义字符
  • 转义字符释义
    \?在书写连续多个问号时使用,防止他们被解析成三个字母
    \'用于表示字符常量'
    \"用于表示一个字符串内部的双引号
    \a警告字符,蜂鸣
    \b退格符
    \f进纸符
    \n换行符
    \r回车
    \t水平制表符
    \v垂直制表符
    \dddd是个八进制数
    \xddd是个十六进制数
  • 逻辑运算符
    逻辑与(&&):如果两个操作数都为真(非零),则结果为真(1),否则为假(0)。
    逻辑或(||):如果两个操作数中至少有一个为真(非零),则结果为真(1),否则为假(0)。
    逻辑非(!):将操作数的值取反,即将0变为1,将1变为0

  • 条件运算符
  • 三元条件运算符(?:):它的语法是条件表达式 ? 表达式1 : 表达式2。
    如果条件表达式的结果为非零值(即为真),则整个表达式的值为表达式1的值;
    否则,整个表达式的值为表达式2的值。
    这种形式的运算符可以用于给变量赋值或进行简单的条件判断

  • 运算符优先级
  • 括:括号() [] 结构体:->指针 常规
    单:单目:++ -- ~! sizeof() (强转)[&:取址解引用->指针]
    术:算术:+ -  / % 
    移:移位:>> <<
    关:关系:> < >= <= == !=
    位:位运算:& | ^
    逻:逻辑:&& || ! 
    三:三目: ? :
    赋:赋值:= += -=
    逗:逗号:,

1.5 控制流程

  • 条件语句(if-else)
  • if (条件表达式) {
        // 如果条件为真,则执行这里的代码
    } else {
        // 如果条件为假,则执行这里的代码
    }
    在上述代码中,首先判断条件表达式的值。
    如果条件表达式的结果为非零值(即为真),则执行if代码块中的语句;
    否则,执行else代码块中的语句。

  • 循环语句(for、while、do-while)
  • for循环:for循环用于在给定的范围内重复执行一段代码。它的语法如下
  • for (初始化表达式; 条件表达式; 更新表达式) {
        // 循环体(需要重复执行的代码)
    }
    

    在for循环中,首先进行初始化表达式,然后检查条件表达式的值。如果条件表达式的结果为真(非零),则执行循环体中的代码;否则跳出循环。然后进行更新表达式,更新循环变量的值。然后再次回到循环条件处,继续判断条件表达式的值,以此类推

  • while循环:while循环用于在满足某个条件时重复执行一段代码。它的语法如下:
  • while (条件表达式) {
        // 循环体(需要重复执行的代码)
    }
    

    在while循环中,首先判断条件表达式的值。如果条件表达式的结果为真(非零),则执行循环体中的代码;否则跳出循环。然后再次回到循环条件处,继续判断条件表达式的值,以此类推。

  • do-while循环:do-while循环与while循环类似,但它至少会执行一次循环体中的代码,然后再根据条件表达式的结果决定是否继续执行循环。它的语法如下:
  • do {
        // 循环体(需要重复执行的代码)
    } while (条件表达式);
    

    在do-while循环中,首先执行循环体中的代码,然后再根据条件表达式的结果决定是否继续执行循环。如果条件表达式的结果为真(非零),则再次执行循环体中的代码;否则跳出循环。

  • 开关语句(switch)
  • C语言中的开关语句(switch)用于根据不同的条件执行不同的代码块。它的语法如下:
  • switch (表达式) {
        case 常量1:
            // 如果表达式的值等于常量1,则执行这里的代码
            break;
        case 常量2:
            // 如果表达式的值等于常量2,则执行这里的代码
            break;
        ...
        default:
            // 如果表达式的值不等于任何常量,则执行这里的代码
    }
    

    在上述代码中,首先将表达式的值与每个case后的常量进行比较。如果表达式的值等于某个常量,则执行该case后的代码块;否则继续比较下一个case。如果所有的case都不匹配,则执行default后的代码块。

    需要注意的是,每个case后面的代码块通常以break关键字结束,以确保跳出switch语句。如果没有break,程序会继续执行下一个case的代码块,直到遇到break或switch语句结束。

第二部分:函数与模块化编程

2.1 函数基础

  • 函数的定义和声明
  • 函数的定义:函数定义包括函数的返回类型、函数名和参数列表。函数定义的语法如下:
    return_type function_name(parameter list) {
        // 函数体(需要重复执行的代码)
    }
    
  • return_type:指定函数的返回类型。如果函数不返回任何值,则使用关键字void
  • function_name:指定函数的名称,用于在程序中调用函数。
  • parameter list:指定函数的参数列表,包括参数的类型和名称。参数是可选的,可以没有参数。
  • 下面是一个使用函数定义的例子:
    #include <stdio.h>
    
    int addNumbers(int num1, int num2) {
        int sum = num1 + num2;
        return sum;
    }
    
    int main() {
        int result = addNumbers(3, 5);
        printf("The sum is: %d\n", result);
        return 0;
    }
    

    在这个例子中,使用函数定义来创建一个名为addNumbers的函数,该函数接受两个整数作为参数,并返回它们的和。在main函数中调用了addNumbers函数,并将结果输出到屏幕上。

  • 参数传递
  • 值传递:函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。
  • 址传递:1、传址调用是吧函数外部创建变量的内存地址传递给函数的一种调用函数的方式。
  • 2、这种传递方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。
  • 返回值
  • 返回类型:函数可以使用不同的数据类型作为返回值,包括基本数据类型(如int、float、double等)和自定义的数据类型。返回类型应该与函数声明中的返回类型一致。

  • 返回表达式:函数可以通过计算表达式的值并将其作为返回值返回。例如,可以使用return x + y;来计算两个变量的和并返回。

  • 多个返回值:C语言允许函数返回多个值。这可以通过使用指针或结构体来实现。例如,可以使用指向结构体的指针作为函数的返回值,该结构体包含多个成员变量。

  • 默认返回值:如果函数没有指定返回值,则使用默认的返回类型和值。对于基本数据类型,默认的返回类型为int,默认的返回值为0。

2.2 递归函数

  • 递归的概念
  • 递归是一种算法,它是指在函数的定义中使用函数自身的方法。在C语言中,递归是指一个函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型的复杂问题层层转化为一个与原问题相似但规模较小的问题来求解。

    递归只要少量代码就可描述出解题过程中所需要的多次重复计算,大大地减少了程序的代码量。但是,在使用递归时,程序员需要注意定义一个从函数退出的条件,否则会进入死循环。

    下面是一个使用递归计算阶乘的例子:
    #include <stdio.h>
    
    int factorial(int n) {
        if (n == 0) {
            return 1;
        } else {
            return n * factorial(n - 1);
        }
    }
    
    int main() {
        int num;
        printf("Enter a positive integer: ");
        scanf("%d", &num);
        printf("%d! = %d
    ", num, factorial(num));
        return 0;
    }
    

  • 递归与迭代的比较
  • 实现方式不同:递归是函数自身调用自身的方式,而迭代是通过循环结构实现。

  • 时间复杂度不同:递归的时间复杂度通常比迭代高,因为递归会重复计算相同的子问题。但是有些情况下,递归可以通过记忆化技术来避免重复计算,从而降低时间复杂度。迭代的时间复杂度通常比递归低,因为它只需要遍历一次数据即可解决问题。

  • 空间复杂度不同:递归的空间复杂度通常比迭代高,因为每次递归都会创建一个新的函数调用栈帧,存储函数的局部变量等信息。而迭代只需要一个循环变量和一个计数器等少量临时变量,因此空间复杂度较低。

  • 可读性和易用性不同:递归的代码通常比较简洁易懂,但是由于需要嵌套调用函数,代码量较大时容易出错。迭代的代码量较少,易于理解和调试,但可能会存在嵌套循环等问题,导致代码难以理解和维护。

2.3 函数指针

  • 函数指针的定义和用法
  • 函数指针是一种指向函数的指针变量。它可以用于调用函数、传递参数和返回值等操作。
    下面是一个使用函数指针的例子:
    #include <stdio.h>
    
    int add(int a, int b) {
        return a + b;
    }
    
    int main() {
        int result;
        int (*func_ptr)(int, int) = &add; // 定义一个指向 add 函数的指针变量 func_ptr
        result = func_ptr(2, 3); // 通过指针变量调用函数 add,并传入参数 2 和 3
        printf("The result is: %d
    ", result); // 输出结果:The result is: 5
        return 0;
    }
    

  • 回调函数
  • 回调函数是一种常用的编程技术,它允许将一个函数作为参数传递给另一个函数。在C语言中,可以使用函数指针来实现回调函数。
    下面是一个使用回调函数的例子:
    #include <stdio.h>
    
    // 定义一个函数指针类型
    typedef void (*callback)(int);
    
    // 定义一个需要回调的函数
    void print_number(int num) {
        printf("%d\n", num);
    }
    
    // 定义一个接受回调函数作为参数的函数
    void process_number(int num, callback cb) {
        cb(num); // 调用回调函数
    }
    
    int main() {
        // 定义一个回调函数
        callback my_callback = print_number;
    
        // 调用 process_number 函数,并将回调函数作为参数传递进去
        process_number(42, my_callback);
    
        return 0;
    }
    

    在上面的例子中,首先定义了一个函数指针类型 callback,它指向一个返回类型为 void 且接受一个 int 类型参数的函数。然后定义了一个需要回调的函数 print_number,它接受一个 int 类型的参数并将其打印出来。接着定义了一个接受回调函数作为参数的函数 process_number,它接受一个 int 类型的参数和一个回调函数作为参数,并在函数体中调用回调函数。最后在 main 函数中定义了一个回调函数 my_callback,并将其作为参数传递给了 process_number 函数。运行程序后,输出结果为 42

第三部分:高级C编程

3.1 数组与指针

  • 数组的概念与用法
  • 指针的基本操作
  • 动态内存分配

3.2 结构体与联合体

  • 结构体的定义和使用
  • 联合体的概念

3.3 文件操作

  • 文件的打开与关闭
  • 文件读写操作
  • 文本文件与二进制文件

3.4 预处理指令

  • 宏定义
  • 条件编译

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值