C

作者: 李霖弢 | 来源:发表于2022-11-02 16:47 被阅读0次

    C 与 C++

    静态类型,强类型(无泛型,但支持如 int 到 float 的隐式转换)

    • C 语言通常用于操作系统、软件、嵌入式开发(如Unix系统、Kindle、OpenGL 等)。
      C语言文件后缀为.c.h(头文件)。

    • C++ 是 C 的超集,与C语言最大的区别是支持面向对象编程。一个有效的C程序通常也是一个有效的C++程序。
      C++语言文件后缀为.cpp


    编译

    C/C++可以使用相同的编译器

    CL

    CL是微软的非开源编译器,通常包含在 Visual Studio 的环境包中

    GCC

    gcc 是 GNU 的开源编译器,其windows版本为minGW

    1. 下载(选择 x84_win32_seh版本,如在线安装失败也可以直接下载压缩包,但需要配置环境变量到mingw\mingw64\bin
    2. 输入gcc -v判断是否安装配置成功
    3. 通过gcc c语言文件名将文件编译为可执行文件
    编译多个文件
    • 通过指令可以直接编译多个文件,如gcc a.c b.c -o c.exe
    • VSCode可以调用CL/GCC,本身没有编译功能
      配置完毕后工作目录下出现.vscode/task.json,修改后可变为多文件编译:
        "tasks": [
            {
                "type": "cppbuild",
                "label": "C/C++: gcc.exe 生成活动文件",
                "command": "D:\\C\\x86_mingw64\\bin\\gcc.exe",
                "args": [
                    "-fdiagnostics-color=always",
                    "-g",
                    // "${file}",
                    "${cwd}//*.c",
                    "-o",
                    "${fileDirname}\\${fileBasenameNoExtension}.exe"
                ],
          }
      ]
    

    基础语法

    #include <stdio.h> //预处理器指令,在实际编译前包含头文件
    
    int main() //主函数
    {
       /* 注释 */
       printf("Hello, W3Cschool! \n");//语句结束符;
       
       return 0;
    }
    

    注意多文件处于一个作用域中,因此全局函数/变量名不能重复,如main只能有一个。


    输入和输出

    %c 字符
    %s 字符串
    %d 10进制整数
    %x 16进制整数
    %f 10进制浮点数

    int number;
    scanf("%d", &number);
    printf("number is %d \n", number);
    

    数据类型

    常见数据类型

    char 整数类型,1字节(8位)
    int 整数类型,4字节
    float 单精度浮点数类型,4字节
    double 双精度浮点数类型,8字节
    void 无,如函数无返回值、函数无参数、指针指向void

    • sizeof()
      可以获得变量的字节数,如sizeof(100)返回4
    • signed 和 unsigned
      float、double总是带符号的,而整形中可手动定义是否带符号。
      char默认为无符号,其他整型默认为有符号。当需要变更时,如unsigned int = 10即可。
    常量

    字符常量是位于单引号''内的单个字符,也可以是如'\n'的特殊字符
    字符串常量是位于双引号""内的一个或多个字符,下面这三种形式所显示的字符串是相同的:

    "hello, dear"
    
    "hello, \
    
    dear"
    
    "hello, " "d" "ear"
    
    • 定义常量
      常量必须同时完成声明、定义、赋值。
      使用#defineconst定义常量,通常定义为大写字母形式
    #define LENGTH 10   
    const int WIDTH = 5;
    int area = LENGTH * WIDTH;
    
    存储类

    用于定义变量或函数的作用范围和生命周期

    • auto
      局部变量默认的存储类
    {
       int mount;
       auto int month;
    }
    
    • register
      向系统建议(实际是否可以由系统决定)存在寄存器(而非内存)的变量,因此变量最大尺寸为1字节。
      该变量没有内存地址,因此不可使用&操作符。
    • static
      修饰的局部变量不会在进入作用或离开时候创建或销毁
      修饰的全局变量/函数不能被其他源文件引用(外部链接属性变成了内部链接属性)
    void fn()
    {
        static int i = 0;
        i++;
        printf("%d""\n", i);
    }
    int main()
    {
        fn();//1
        fn();//2
        fn();//3
    }
    
    • extern
      声明,告诉编译器有这个变量存在,但不会为其分配空间。
      对于无法初始化的变量,会在同一作用域中找到另一个同名变量的存储位置(通常用于引用另一个文件中的变量或函数):
    # a.c
    #include <stdio.h>
    int main()
    {
        extern int a;
        printf("%d \n",a);
    }
    
    # b.c
    #include <stdio.h>
    int a = 200;
    

    在python、js等弱类型语言中,不区分声明和定义,因其总是在赋值时才分配内存空间。但在C语言中,如 extern int a则只是声明,不分配内存空间;int a才是定义,分配了4字节空间(如此前无声明则同时做了声明和定义);int a = 1则是同时进行了声明、定义、赋值。一个变量可以多次声明,但只能定义一次。
    此外,全局变量定义时会自动进行初始化。如int a;会初始化为0,char b;会初始化为'\0'


    指针

    指针是一种特殊的数据类型

    • 指针的值是目标对象的内存首地址(表现为一个整数,可以用%d%x打印出来)
    • 指针的类型是目标对象数据的类型

    对指针的加减操作会指向另一个内存地址,因此每次整数的变化大小和该指针指向的数据类型字节数有关:

    char value_1 = 'a';
    char *test_1 = &value_1;
    printf("test_1 from %d to %d \n", test_1, test_1 - 1); //test_1 from 6421823 to 6421822
    
    double *test_2 = 1000;
    test_2 = test_2 + 1;
    printf("test_2 %d \n", test_2); // 1008
    
    地址 &

    返回变量地址,地址为一个int
    注意,局部变量超出作用域后即销毁,因此无法在作用域外通过其地址获取值,除非定义局部变量为 static 变量

    指针 *

    用于定义一个指向另一个变量地址的指针,或将该指针解引用,还原为其值。
    指针定义时所用的类型为其指向内容的类型。
    未赋值的指针会初始化为一个随机值,通常手动赋值为NULL

    int main()
    {
        int a = 4;
        int *another_a = NULL;
        another_a = &a; /* 'another_a' 现在包含 'a' 的地址 */
        a = 222;
        printf("a 的值是 %d\n", a);//222
        printf("another_a 的值是 %x\n", another_a);//61fdf4
        printf("*another_a 是 %d\n", *another_a);//222
    }
    

    重复使用*则为指向指针的指针,同理,解引用时也需要使用多个*

    int realInt = 9;
    int *pointer_1 = &realInt;
    int **pointer_2 = &pointer_1;
    printf("printf realInt by pointer_2: %d", **pointer_2);//9
    
    传值调用与引用调用

    向函数传递参数时,默认为传值调用,修改函数内的形式参数不会影响实际参数。
    通过为函数形参添加*,声明参数为引用类型,则改为引用调用

    #include <stdio.h>
    
    void swap(int *x, int *y);
    
    int main ()
    {
       /* 局部变量定义 */
       int a = 100;
       int b = 200;
       swap(&a, &b);
    
       printf("交换后,a 的值: %d\n", a );//200
       printf("交换后,b 的值: %d\n", b );//100
     
       return 0;
    }
    
    void swap(int *x, int *y)
    {
       int temp;
       temp = *x;    /* 保存地址 x 的值 */
       *x = *y;      /* 把 y 赋值给 x */
       *y = temp;    /* 把 temp 赋值给 y */
      
       return;
    }
    

    数组

    数组是一个固定大小的相同类型元素的顺序集合。
    C中数组会占用连续的内存(类似C#的数组),因此。而JS的数组和Python的列表则是哈希映射。

    • 数据仅能在定义时赋值,如double balance[]={1000.0, 2.0, 3.4, 7.0, 50.0};
    • 若定义时未进行赋值,则必须声明数组的长度,如double balance[5];。未赋值的成员会根据数据类型自动初始化。
    • 其他场合下只能对数组中具体成员进行赋值,如balance[0]=2;
    double arr2[] = {1.1, 2, 4, 8};
    double *pp = arr2;
    printf("%f \n", *(pp + 2));//4
    

    数组变量是指向数组第一项的指针,因此可以用指针模拟一个数组。通过比较同一个数组中不同成员的指针地址大小,可以判断成员在数组中的先后关系:

    const int MAX = 3;
    
    int main ()
    {
       int  var[] = {10, 100, 200};
       int  i, *ptr;
       ptr = var;
       i = 0;
       while ( ptr <= &var[MAX - 1] )
       {
          printf("Address of var[%d] = %x\n", i, ptr );
          printf("Value of var[%d] = %d\n", i, *ptr );
          ptr++;
          i++;
       }
       return 0;
    }
    
    多维数组
    int a[3][4] = {  
     {0, 1, 2, 3} ,   /*  初始化索引号为 0 的行 */
     {4, 5, 6, 7} ,   /*  初始化索引号为 1 的行 */
     {8, 9, 10, 11}   /*  初始化索引号为 2 的行 */
    };
    
    指针数组

    在数组名前加*则为指针数组:

    int *pointerArray[2];
    int int_1 = 1;
    int int_2 = 2;
    pointerArray[0] = &int_1;
    pointerArray[1] = &int_2;
    printf("value of pointerArray 1: %d \n",*pointerArray[0]);//1
    printf("value of pointerArray 2: %d \n",*pointerArray[1]);//2
    
    函数返回值

    函数不能直接返回一个数组,但可以返回其指针(注意使用static,使局部变量可以在函数外被访问):

    int *array()
    {
        static int arr[3] = {1, 2, 3};
        return arr;
    }
    int main()
    {
        int *p;
        p = array();
        printf("%d : %d \n", p + 0, *(p + 0));
        printf("%d : %d \n", p + 1, *(p + 1));
        printf("%d : %d \n", p + 2, *(p + 2));
    }
    

    字符串

    C语言中,字符串是以'\0'结尾的字符数组(所以字符串实际占用的字节数比strlen获取的字符串长度多1)。
    其中'\0'称为空字符或结束符(NUL,Null character),可以手工添加到字符数组尾,或者编译器会在初始化时自动添加。

    char string[7] = {'R', 'U', 'N', 'O', 'O', 'B'};
    printf("string: %s\n", string);//RUNOOB
    char string2[] = "RUNOOB";
    printf("string2: %s\n", string2);//RUNOOB
    
    字符串数组

    由于字符串本身是个字符数组,因此字符串数组就是字符的二维数组:

    char stringstring[2][3] = {"aaa", "bbb"};
    printf("stringstring %c \n", stringstring[1][1]);//b
    printf("stringstring %s \n", stringstring[1]);//bbb
    

    因为字符串是字符的数组,而数组的变量又是个指针,因此可以把字符串作为指针数组的成员,来表示字符串数组:

    char *names[] = {
        "Zara Ali",
        "Hina Ali"
    };
    printf("%s \n", names[0] ); //Zara Ali
    

    相关文章

      网友评论

          本文标题:C

          本文链接:https://www.haomeiwen.com/subject/mtkftdtx.html