• C语言入门语法


    一、数据类型

    常量

    1、通过预处理声明常量

    #include <stdio.h>
    
    #define PRICE 100
    
    int main() {
        printf("价格:%d
    ",PRICE);
        return 0;
    }
    

    2、通过 const 关键字声明常量

    #include <stdio.h>
    const int NUM=10;
    int main() {
        printf("数量:%d
    ",NUM);
        return 0;
    }
    

    区别:#define 在运行时会替换为指定的值,const可以看到数据类型,更推荐使用 const

    整型

    在C语言中分为有符号和无符号整型,无符号是整数,有符号包含负数

    类型 说明
    short 2字节
    int 4字节
    long 32位系统4字节,64位系统8字节
    long long 8字节
    int main() {
        int a=10;
        int b=-10;
        long long c=20;
        int d=0b111;//二进制
        int e=0xb;//十六进制
        int f=010;//八进制
        unsigned int g=12;//无符号正数
        printf("a=%d,b=%d,c=%d,d=%d",a,b,c,d);
        return 0;
    }
    
    • 0b 为二进制方式赋值
    • 0x 为十六进制数据
    • 0 为八进制数据

    进制相关知识参考其他教程

    c11 标准 stdint.h 对数据的长度进行了统一

    类型 说明
    int8_t 统一8位1字节
    int16_t 2字节
    int32_t 4字节
    int64_t 8字节
    uint8_t 无字符1字节

    浮点数

    类型 说明
    float 单精度,4字节,32位
    double 双精度,8字节,64位
    long double 长双精度,16字节,128位

    typedef 自定义类型

    typedef uint8_t mychar;
    int main() {
        mychar ch='a';
        printf("%c",ch);
        return 0;
    }
    

    自定义类型相当于对类型起了个别名

    二、goto 跳转

    使用 goto 实现循环

    int i=0;
    aa:
    printf("%d
    ",i);
    i=i+1;
    if(i<100){
        goto aa;
    }
    

    标签名: 标记一个位置,goto 标签名; 跳转到指定标签位置,标签名可以自己定义。

    三、输入与输出

    相关函数文档参考:C语言stdio.h文档

    1、字符输出

    putchar:写字符到标准输出,相当于调用 putc
    遍历字符并打印

    #include <stdio.h>
    
    int main() {
        char c;
        for (c='a';c<='z';c++) {
            putchar(c);
            putchar('
    ');
        }
        return 0;
    }
    

    说明:char 类型占用1字节,每个字符都映射对应一个整数。

    查看字符对应整数

    #include <stdio.h>
    
    int main() {
        char c;
        printf("=====小写====
    ");
        for (c='a';c<='z';c++) {
            printf("%d
    ",c);
        }
        printf("=====大写====
    ");
        for (c='A';c<='Z';c++) {
            printf("%d
    ",c);
        }
        return 0;
    }
    

    大写字符和小写字符刚好差距32, putchar('A'+32); 可转为小写。

    2、字符串输出

    puts:写字符串到标准输出

    输出字符串示例

    #include <stdio.h>
    
    int main() {
        char string [] = "Hello world!";
        puts(string);
        return 0;
    }
    

    3、格式化输出

    printf:打印格式化数据到标准输出,如果包含格式说明符(以%开头的子串)将格式化,用附加参数替换说明符(说明符可以认为是占位符)。

    格式化时用到的说明符

    符号 解释 举例
    d 或者 i 有符号十进制整数 392
    u 无符号十进制整数 7235
    o 无符号八进制 610
    x 无符号十六进制整数 7fa
    X 无符号十六进制整数(大写) 7FA
    f 十进制浮点,小写 392.65
    F 十进制浮点,大写 392.65
    e 科学记数法(尾数/指数),小写 3.9265e+2
    c 字符 a
    s 字符串 hello
    p 指针地址(内存地址) b8000000
    % %%打印出一个% %

    格式化输出示例

    #include <stdio.h>
    
    int main() {
        printf("字符:%c %c
    ",'a',65);
        printf("整数:%d %ld
    ",600,6500000L);//%ld 长整
        printf("浮点数:%f
    ",33.3);
        printf("十六进制:%x
    ",0xffa);
        printf("特殊打印:%%
    ");
        return 0;
    }
    

    结果

    字符:a A
    整数:600 6500000
    浮点数:33.300000
    十六进制:ffa
    特殊打印:%
    

    4、输入字符和字符串

    getchar:从标准输入中获取字符

    获取字符示例

    #include <stdio.h>
    
    int main() {
        uint8_t c;
        puts("输入文本,在一个句子中包含.以结束:");
        do{
            c=getchar();
            putchar(c);
        }while (c!='.');
        return 0;
    }
    

    uint8_t c; uint8_t 大小与char一样

    gets:从标准输入中获取字符串

    获取字符串示例

    #include <stdio.h>
    
    int main() {
        char str[256];
        puts("输入你的名字:");
        gets(str);
        printf("你的名字:%s
    ",str);
        return 0;
    }
    

    5、格式化输入

    scanf:从标准输入中读取格式化数据,格式化说明符可参照 printf

    格式化输入示例

    #include <stdio.h>
    
    int main() {
        char ch;
        int num;
        char str[10];
    
        puts("输入一个字符:");
        scanf("%c",&ch);
        printf("用户的输入字符为:%c
    ",ch);
    
        puts("输入一个整数:");
        scanf("%d",&num);
        printf("用户输入的整数为:%d
    ",num);
    
        puts("输入字符串:");
        scanf("%s",str);
        puts(str);
        return 0;
    }
    

    int、char 等类型需要用&符号取得变量内存地址,变量 str 是一个数组,本身存储的就是内存地址,所以不用加上&符号。(可以理解为值与引用)

    四、数组

    1、一维数组

    创建数组方式1:

    const int NUMS_LEN=10;
    int nums[NUMS_LEN];
    

    数组中元素的内存地址没有做过任何处理,有可能被之前其他程序使用过,所以数组中的默认的值是不可预期的。

    演示数组中元素默认值:

    #include <stdio.h>
    
    int main() {
        const int NUMS_LEN=10;
        int nums[NUMS_LEN];
        for (int i = 0; i < NUMS_LEN; ++i) {
            printf("索引:%d,值:%d
    ",i,nums[i]);
        }
        return 0;
    }
    

    创建数组方式2:

    int ages[]={19,20,30};
    

    2、二维数组

    创建方式1:

    int nums[3][4];
    for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 4; ++j) {
            printf("%d,%d,value=%d
    ",i,j,nums[i][j]);
        }
    }
    

    创建方式2:

    int nums[2][3]={
            {1,2,3},
            {4,5,6}
    };
    

    二维数组直接初始化值需要指定长度!

    如果想对数组中的元素清0,可以在遍历中为元素赋值为0.

    3、字符数组(字符串)

    声明字符串并检查长度:

    char str[10];
    printf("长度:%ld
    ",strlen(str));
    

    strlen 函数用于检测字符数组中字符的长度

    存入字符串并检查长度:

    char str[10]="hello";
    printf("长度:%ld
    ",strlen(str));
    

    作为字符串结尾:

    char str[10]="hello";
    printf("长度:%ld
    ",strlen(str));
    

    字符串中不论内容多少,只要遇到 就认为字符串结束!

    char数组填充函数 memset:

    memset(要填充的数组,要填充的值,要填充多长)

    #include <stdio.h>
    #include <string.h>
    int main() {
        char str[]="你好";
        memset(str,1,6);
        for (int i = 0; i < 6; ++i) {
            printf("%d
    ",str[i]);
        }
        return 0;
    }
    

    关于中文:

    char str[2]="你好";
    printf("长度:%ld
    ",strlen(str));//一个汉字占用3字节
    

    五、字符串操作

    1、字符串连接

    strcat:连接字符串,将源字符串的副本附加到目标字符串。

    #include <stdio.h>
    #include <string.h>
    int main() {
        char * str="hello";//要拼接的字符
        char result[100];//目标字符
        memset(result,0,100);//填充0到目标字符
        strcat(result,str);//拼接str到result
        strcat(result,"world");//拼接 world 到result
        puts(result);
        return 0;
    }
    

    strncat:连接指定数量的字符到目标字符串

    #include <stdio.h>
    #include <string.h>
    int main() {
        char dist[10];
        strncat(dist,"hello",3);
        puts(dist);
        return 0;
    }
    

    结果

    hel
    

    2、格式化字符串

    sprintf:将格式化数据写入字符串(格式方式和其他格式化方法很像)

    #include <stdio.h>
    #include <string.h>
    int main() {
        char str[10];
        memset(str,0,10);
        int len,a=10,b=5;
        len=sprintf(str,"%d+%d=%d",a,b,a+b);
        printf("格式结果:[%s],长度为:%d",str,len);
        return 0;
    }
    

    结果:

    格式结果:[10+5=15],长度为:7
    

    3、字符串与基础数据类型转换

    sscanf:从字符串中读取格式化数据

    使用 sscanf 截取数据

    #include <stdio.h>
    #include <string.h>
    int main() {
        //原字符串
        char sentence[]="Java is 23 years old";
        //保存拆分出来的数据的变量
        int age;
        char name[10];
        sscanf(sentence,"%s %*s %d",name,&age);
        printf("age is %d,name is %s",age,name);
        return 0;
    }
    

    "%s %*s %d" 分别表示了 Java is 23 (空格也包含在内),%*s 表示匹配的字符串但忽略该数据(可以理解占了个位置)。&age 这里需要将基本类型的内存地址引入。

    使用 sscanf 转换数据类型

    #include <stdio.h>
    int main() {
        char * str="100";
        int a;
        sscanf(str,"%d",&a);
        printf("转换后:%d",a);
        return 0;
    }
    

    使用 atof 将字符串转为 double

    #include <stdio.h>
    #include <stdlib.h>
    int main() {
        double result=atof("3.14");
        printf("转换后:%f",result);
        return 0;
    }
    

    4、字符串比较

    == 用来比较两个变量的内存地址,不能比较字符串的值。使用 strcmp 比较两个字符串的值,两个字符串的值相等返回 0。

    #include <stdio.h>
    #include <string.h>
    int main() {
        char * str1="hello";
        char str2[]="hello";
        if(strcmp(str1,str2)==0){
            puts("两个字符串的值相等");
        }else{
            puts("两个字符串的值不相等");
        }
        return 0;
    }
    

    5、字符串的截取

    strchr:从指定字符开始截取

    #include <stdio.h>
    #include <string.h>
    int main() {
        char * str="helloworld";
        char * result=strchr(str,'w');
        puts(result);
        return 0;
    }
    

    strrchr:从最后一个指定字符开始截取

    #include <stdio.h>
    #include <string.h>
    int main() {
        char * str="helloworld";
        char * result=strrchr(str,'l');
        puts(result);
        return 0;
    }
    

    strstr:从指定字符串开始截取

    #include <stdio.h>
    #include <string.h>
    int main() {
        char * str="helloworld";
        char * result=strstr(str,"wo");
        puts(result);
        return 0;
    }
    

    区分大小写

    strncpy:从字符串中复制指定长度字符

    #include <stdio.h>
    #include <string.h>
    int main() {
        char * str="helloworld";
        char dest[10];
        strncpy(dest,str,5);
        puts(dest);
        return 0;
    }
    

    使用指针从指定位置开始截取

    #include <stdio.h>
    #include <string.h>
    int main() {
        char * str="helloworld";
        char * str1=str+5;
        puts(str1);
        return 0;
    }
    

    可以先利用指针操作从某个位置开始截取,然后利用 strncpy 截取指定个。

    六、函数

    声明和调用的方式和java很相似

    1、main 函数的参数

    main 函数的参数,也是程序运行时的参数

    声明时需指定:1.参数的长度,2.参数数组

    #include <stdio.h>
    int main(int argc,char ** argv) {
        printf("参数的个数:%d
    ",argc);
        for (int i = 0; i < argc; ++i) {
            printf("参数 %d 的值:%s
    ",i,argv[i]);
        }
        return 0;
    }
    

    2、可变参数

    #include <stdio.h>
    #include <stdarg.h>
    void testarg(int n,...){
        printf("参数的个数:%d
    ",n);
        va_list args;
        va_start(args,n);
        for (int i = 0; i < n; ++i) {
            int temp=va_arg(args,int);
            printf("参数:%d
    ",temp);
        }
        va_end(args);
    }
    int main(int argc,char ** argv) {
        testarg(2,10,55);
        return 0;
    }
    

    函数设置可变参数时需指定参数个数形参 int n ,和 ...(任意个数的参数)。需引入头文件 stdarg.h,va_list 类型保存有关变量参数的信息,va_start 初始化变量参数列表,va_arg 取出参数的值,va_end 结束使用变量参数列表

    七、预处理

    1、预设常量

    除了可以用 #define 定义一个常量,还可以在编译参数中指定预设常量的值

    编写没有声明常量但使用了常量的代码

    #include <stdio.h>
    
    int main() {
        printf("The num is %d
    ",THE_NUM);
        return 0;
    }
    

    使用命令进行编译和运行

    gcc -o test main.c -DTHE_NUM=4
    test.exe
    

    -o 参数指定编译后可执行文件的文件名,-D 参数后面直接跟常量名和值(没有空格)

    2、编译条件针对不同平台编译

    1.编写代码

    #include <stdio.h>
    
    #define WIN 1
    #define LINUX 2
    #define MAC 3
    
    int main() {
    #if PLATFORM==WIN
        printf("Hello Windows
    ");
    #elif PLATFORM==LINUX
        printf("Hello Linux
    ");
    #elif PLATFORM==MAC
        printf("Hello MAC
    ");
    #else
        printf("Unknow platform
    ");
    #endif
        return 0;
    }
    

    if、#elif、#else、#endif 这些都是编译条件,用于决定代码块是否编译

    2.编译时指定条件

    //编译源码
    gcc -o test main.c -DPLATFORM=1
    //运行
    test.exe
    

    3、头文件重复引入

    1、演示重复引入

    1.创建a.h

    #include "b.h"
    

    2.创建b.h

    #include "a.h"
    

    3.引入a.h

    #include <stdio.h>
    #include "a.h"
    int main() {
    
        return 0;
    }
    

    2、防止头文件重复引入

    修改a.h

    #ifndef A_H
    #define A_H
    #include "b.h"
    #endif
    

    修改b.h

    #ifndef B_H
    #define B_H
    #include "a.h"
    #endif
    

    通过标记一个常量来判断是否重复引入,一旦拥有这个常量将不再引入

    八、指针

    声明一个变量后,系统会为这个变量分配内存空间,每个变量都有一个内存地址,相当于旅馆的房间号。

    我们要获取变量中的值需要通过内存地址来找到对应的值,但是还有另一种情况是这个变量中存放的是另个一变量的内存地址。这就好像你通过房间号找到这个房间,发现在这个房间里面是另一个房间的房间号。

    所以,一个变量的地址就成为该变量的指针

    声明指针并指向一个变量的地址

    #include <stdio.h>
    int main() {
        int *a,*b;
        int i=10;
        a=&i;
        b=&i;
        printf("a的值为:%d
    ",*a);
        printf("b的值为:%d
    ",*b);
        i=99;
        printf("a的值为:%d
    ",*a);
        printf("b的值为:%d
    ",*b);
        return 0;
    }
    

    C语言用*表示指针,为指针赋值时 & 取出变量的地址。(指针a和指针b指向同一个地址,修改i后指针中的也会改变,这个很类似Java和C#中的引用类型)

    指针的大小

    #include <stdio.h>
    int main() {
        int *a;
        printf("size is %ld
    ", sizeof(a));
        return 0;
    }
    

    64位系统中为8字节,32位中为4字节

    1、函数指针

    有参无返回值的函数指针

    #include <stdio.h>
    
    void hello(int a,char* c){
        printf("hello
    ");
    }
    int main() {
        void(*fp)(int,char*)=&hello;
        fp(1,"");
        return 0;
    }
    

    有参有返回值的函数指针

    #include <stdio.h>
    
    int test(int a){
        printf("hello:%d
    ",a);
        return a;
    }
    int main() {
        int(*fp1)(int)=&test;
        int result=fp1(11);
        printf("返回值:%d
    ",result);
        return 0;
    }
    

    给函数指针设定类型别名

    #include <stdio.h>
    
    void hello(){
        printf("Hello
    ");
    }
    typedef void(*SimpleHello)();
    int main() {
        SimpleHello h1=&hello;
        h1();
        SimpleHello h2=&hello;
        h2();
        return 0;
    }
    

    重复定义时可以简化代码

    2、无类型指针

    无类型指针可以代表所有类型的数据

    无类型指针存放字符串

    #include <stdio.h>
    
    int main() {
        void *test="Hello";
        printf("%s
    ",test);
        return 0;
    }
    

    九、结构体与共同体

    1、结构体

    主要用结构体描述属性

    定义使用结构体

    #include <stdio.h>
    struct File{
        char* name;
        int size;
    };
    int main() {
        struct File file;
        file.name="a.txt";
        file.size=10;
        printf("文件 %s 的大小为 %d
    ",file.name,file.size);
        struct File file1={"a.jpg",100};
        printf("文件 %s 的大小为 %d
    ",file1.name,file1.size);
        return 0;
    }
    

    结构体也可以像数组那样初始化(C#中也有结构,两者比较像可以对比记忆)

    定义结构体类型简化代码

    #include <stdio.h>
    typedef struct _File{
        char* name;
        int size;
    }File;
    int main() {
        File file;
        file.name="a.txt";
        file.size=10;
        printf("文件 %s 的大小为 %d
    ",file.name,file.size);
        File file1={"a.jpg",100};
        printf("文件 %s 的大小为 %d
    ",file1.name,file1.size);
        return 0;
    }
    

    2、结构体的内存对齐

    结构体所占大小如何计算的?结构的大小由各变量与字节最大变量对齐后相加得出。

    如图所示,三种情况:

    1.当定义第一个变量大小为1字节,第二个变量比第一个变量大时,第一个变量要对齐大的变量,第一个变量填充为2字节,结构体大小为4字节。

    2.前两变量加起来不足对齐第三个变量的大小,所以填充2字节对齐,结构体大小为8字节。

    3.前4个变量加起来刚好对齐第5个变量的大小,无须对齐。最后一个变量没有对齐则填充2个字节对齐第5个变量,所以结构体大小为12字节。

    #include <stdio.h>
    
    typedef struct _Data{
        uint8_t a;//1
        uint8_t b;//1
        uint8_t c;//1
        uint8_t d;//1
        uint32_t e;//4
        uint8_t f;//1
    }Data;
    int main() {
        printf("size is %d
    ", sizeof(Data));
        return 0;
    }
    

    此时结果为12

    3、结构体指针

    没有使用指针前

    #include <stdio.h>
    typedef struct _Dog{
        char * color;
        int age;
    }Dog;
    int main() {
        Dog dog1={"红色",2};
        Dog dog2=dog1;
        dog1.age=3;
        printf("(dog1 color is %s,age is %d),(dog2 color is %s,age is %d)
    "
            ,dog1.color,dog1.age,dog2.color,dog2.age
        );
        return 0;
    }
    

    没有使用之前将dog1赋值给dog2相当于复制一个副本

    使用结构体指针

    #include <stdio.h>
    
    typedef struct _Dog {
        char *color;
        int age;
    } Dog;
    
    int main() {
        Dog dog1 = {"红色", 2};
        Dog *dog2 = &dog1;
        dog1.age = 3;
        printf("(dog1 color is %s,age is %d),(dog2 color is %s,age is %d)
    ",
                dog1.color, dog1.age, dog2->color, dog2->age);
        return 0;
    }
    

    指针操作用法和之前一样,需要注意的地方是:结构体指针,获取结构体的值时需要使用 -> ,否则将编译错误

    通过函数创建和销毁结构体指针

    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct _Dog {
        char *color;
        int age;
    } Dog;
    Dog* createDog(char* color,int age){
        Dog* dog=malloc(sizeof(Dog));
        dog->color=color;
        dog->age=age;
        return dog;
    }
    void deleteDog(Dog* dog){
        free(dog);
    }
    
    int main() {
        Dog* dog1=createDog("红色",3);
        printf("(dog1 color is %s,age is %d)
    ",
                dog1->color, dog1->age);
        deleteDog(dog1);
        return 0;
    }
    

    malloc函数分配内存,使用完毕后用free函数释放内存(释放后的内存的值不可预期何时修改,free函数调用后别的程序就可以使用这块空间了)

    3、共同体

    共同体的特点:共同体中声明的所有变量共用一块空间

    声明及使用共同体

    #include <stdio.h>
    typedef union _BaseData{
        char ch;
        uint8_t ch_num;
    }BaseData;
    int main() {
        BaseData data;
        data.ch='A';
        printf("ch_num is %d size is %d
    ",data.ch_num, sizeof(BaseData));
        return 0;
    }
    

    变量 ch_num 和 ch 共用同一内存地址,大小为 1,共同体做类型转换比较方便。

    实现颜色的操作

    #include <stdio.h>
    //定义颜色结构体
    typedef struct _ColorARGB{
        uint8_t blue;
        uint8_t green;
        uint8_t red;
        uint8_t alpha;
    }ColorARGB;
    //定义颜色共同体
    typedef union _Color{
        uint32_t color;
        ColorARGB colorARGB;
    }Color;
    int main() {
        Color c;
        c.color=0xFFAADD00;
        printf("red:%X",c.colorARGB.red);
        return 0;
    }
    

    存入的数据是以小端模式,所以定义颜色结构体时的通道的顺序和赋值的颜色顺序相反。结合结构体与共同体实现(巧妙)

    十、文件操作

    fopen函数:以指定文件名和模式获取一个流的FILE指针。

    支持的模式

    模式 说明
    "r" 读:文件必须存在
    "w" 写:创建一个文件,如果已经存在则覆盖
    "a" 追加:向文件末尾追加内容,如果文件不存在则创建该文件
    "r+" 可做读取和修改的操作,文件必须存在

    写文件

    //创建文件指针
    FILE * file=fopen("a.txt","w");
    if (file){
        //写入内容
        fputs("HelloWorld",file);
        //关闭
        fclose(file);
    }else{
        puts("文件操作失败!");
    }
    

    读文件

    #include <stdio.h>
    int main() {
        FILE * file=fopen("a.txt","r");
        if (file){
            char ch;
            while(1){
                ch=fgetc(file);
                if(ch!=EOF){
                    printf("%c
    ",ch);
                }else{
                    break;
                }
            }
            fclose(file);
        }
        return 0;
    }
    
  • 相关阅读:
    P3970 [TJOI2014]上升子序列
    受欢迎的牛(Tarjan缩点模板)
    Y15BeTa的乱搞方法(占坑待填)
    Luogu P4145 上帝造题的七分钟2 / 花神游历各国
    Luogu P1525 【关押罪犯】
    Luogu P1077 摆花 NOIP2012pjT3
    Nowcoder deco的abs
    CSP-S前的芝士清单
    普天同庆
    线段树区改区查标记永久化板子
  • 原文地址:https://www.cnblogs.com/AIThink/p/10085092.html
Copyright © 2020-2023  润新知