自学c语言

C 语言是一种通用的、面向过程式的计算机程序设计语言。

当前最新的 C 语言标准为 C18

前期准备

C 编译器  写在源文件中的源代码是人类可读的源。它需要"编译",转为机器语言,这样 CPU 可以按给定指令执行程序。(.c-->.exe)

下载Dev-C++ 图标长这样哦别下载错了

编写代码

1 #include <stdio.h>
2 int main(){
3     printf("Hello World!");
4     //system("pause"); //暂停程序 不会一闪而过
5     return 0;
6 }     

保存为 hello.c 文件 

进入代码目录

win+r cmd 打开命令提示符 cd到程序目录执行编译  gcc hello.c   会生成hello.exe 文件

想用vs code开发C语言点击这里

数据类型  

1 基本类型:
  它们是算术类型,包括两种类型:整数类型浮点类型
2 枚举类型:
  它们也是算术类型,被用来定义在程序中只能赋予其一定的离散整数值的变量。
3 void 类型:
  类型说明符 void 表明没有可用的值。
4 派生类型:
  它们包括:指针类型、数组类型、结构类型、共用体类型和函数类型。

存储类

存储类定义 C 程序中变量/函数的可用范围和生命周期。说明符应放置类型之前

  1.auto 

    局部变量的默认存储类, 规定变量只能在函数内部使用

  2.register 

    定义存储在cpu寄存器中而不是 RAM(内存) 中的局部变量

  3.static 

    局部变量时进入和离开作用域不会进行创建和销毁

    全局变量时会使变量的作用域限制在声明它的文件内

  4.extern 

    在一个文件中声明一个全局变量或函数

#include <stdio.h>
// 函数外定义变量 x 和 y
int x;
int y;
int add(){
    // 函数内声明变量 x 和 y 为外部变量
    extern int x;
    extern int y;
    // 给外部变量(全局变量)x 和 y 赋值
    x = 4;
    y = 2;
    return x+y;
}
int main()
{
    int result;
    result = add();
    //文件编码UTF8时中文乱码 应设为GBK
    printf("result 为:%d=%d+%d",result,x,y);
    return 0;
}

 

运算符

  7个 算术运算符 关系运算 符逻辑运算 符位运算符 赋值运算符 杂项运算符

#include <stdio.h>
//不用第三个变量 A^B,异或交换值
// 这种利用位运算的交换方法只适用于整型变量,不能用于浮点型变量
int main()
{
    int A = 60;//二进制 0011 1100
    int B = 13;//二进制 0000 1101
    A = A^B;//A=0011 0001
    B = A^B;//B=0011 1100
    A = A^B;//A=0000 1101
    // A^=B^=A^=B; //简写
    printf("原A=60 B=13,现A=%d,B=%d",A,B);
    return 0;
}

 

enum(枚举)

  一种基本数据类型  enum 枚举名 {枚举元素1,枚举元素2,……}; 

   第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。

  可以把第一个枚举成员的值定义为 1,第二个就为 2,以此类推  

#include <stdio.h>
#include <stdlib.h>
// 枚举类型
int main()
{
    enum Colors { red=1, blue, green };
    enum Colors color;
    printf("%d是红色,%d是蓝色,%d是绿色,请输入数字选择喜欢的颜色",red,blue,green);
    scanf("%u",&color);
    switch(color){
        case red:
         printf("你喜欢红色\n");
         break;
        case blue:
         printf("你喜欢蓝色\n");
         break;
        case green:
         printf("你喜欢绿色\n");
         break;
        default:
         printf("没有你喜欢的颜色\n");
    }
    system("pause");
}

 

C指针

  指针指向内存地址的变量,指针变量存放的是普通变量内存地址

int  var = 20;   /* 实际变量的声明 */
int  *ip;        /* 指针变量的声明 */ 
ip = &var;  /* 在指针变量中存储 var 的地址 */

*和&都是一元运算符

&var 返回的是变量var在内存中的地址

 *ip 返回的是指向的变量var(20)的值

普通指针和数组指针:

#include <stdio.h>
// 指针 &a=获取变量地址 *a=指针变量
int main()
{
    int var = 12;
    int *ip = NULL;

    ip = &var;

    printf("var 变量地址:%p \n",&var);
    printf("ip 变量存储的地址:%p \n",ip);
    printf("ip 变量地址:%p \n",&ip);
    printf("*ip 变量的值:%d \n",*ip);

    system("pause");

    // int var = {
    //     地址:000000000062FE14
    //     值:12
    // }
    // &var = var地址

    // int *ip = {
    //     值:NULL
    //     地址:000000000062FE10
    // }

    // ip = &var
    // *ip = var值 = 12
    // ip = &var = var地址


    //指针与数组
    const int MAX = 3;

    int arr[] = {10,100,200};
    int i,*ptr[MAX];
    for(i=0;i<MAX;i++){
        ptr[i] = &arr[i];
    }
    for(i=0;i<MAX;i++){
        printf("var[%d] 地址:%p || %p,值:%d \n",i,ptr[i],&arr[i],*ptr[i]);
    }

    system("pause");

    return 0;
}

 函数指针:

#include <stdio.h>
// 函数指针
int max(int x,int y){
    return x>y?x:y;
}
int main()
{
    int (*p)(int,int) = &max; //&可以忽略
    int a,b,c,d;
    printf("请输入3个数字:");
    scanf("%d %d %d",&a,&b,&c);
    d = p(p(a,b),c);
    printf("最大数为:%d \n",d);

    system("pause");
}

 

字符串

  C 语言中,字符串实际上是使用空字符 \0 结尾的一维字符数组\0 是用于标记字符串的结束

#include <stdio.h>
#include <string.h>
// 字符串
int main()
{
    char arr[6] = {'h','e','l','l','o','\0'};
    printf("%s \n",arr);

    char str[] = "hello";
    printf("%s \n",str);

    char str1[] = "字符串1",str2[] = "字符串2",str3[5];

    // 操作字符串的函数 需要引入<string.h>标准库
    strcpy(str3,str1);//将str1复制到str3
    printf("str3=%s \n",str3);

    strcat(str3,str2);//将字符串str2链接到str3末尾
    printf("str3=%s \n",str3);

    int len = strlen(str3);//返回str3的长度
    printf("str3的长度为%d \n",len);

    int cmp = strcmp(str3,str1);//如果 str1,str3相同的,返回 0;str1<str3 返回小于 0;str1>str3 返回大于 0。
    printf("cmp=%d \n",cmp);

    char *chr,ch='1';//ch为一个字符
    // str3="字符串1字符串2"
    chr = strchr(str3,ch);//返回一个指针 字符ch 第一次出现的位置
    printf("chr=%s \n",chr);//返回 "1字符串2"

    char s[2] = "";//汉字一般占2个字节(系统或编码方式不一样可能占3个字节)
    char *ss = strstr(str3,s);//返回一个指针,指向字符串 str3字符串 s 的第一次出现的位置
    printf("\'串\'出现在str3的位置为:%s \n",ss);
    
    system("pause");
}

 

 结构体

  用struct定义,自定义数据类型用来存储不同类型的数据成员

struct 结构名 { 
    数据类型1;
    数据类型2;
    ...
} 变量1,变量2 ... ;

结构体变量的初始化,访问结构成员,结构作为函数参数,指向结构的指针 例子:

#include <stdio.h>
#include <string.h>
// 结构体
struct Books//生成结构体Books
{
    char title[50];
    char author[50];
    char subject[50];
    int book_id;
} book;//声明结构变量book

void printBook(struct Books book);
void setBook(struct Books *b,char title[],char author[],char subject[],int book_id);
int main()
{
    //初始化book变量 
    strcpy(book.title,"百科全书");
    strcpy(book.author,"小明");
    strcpy(book.subject,"啥东西都有");
    book.book_id = 1;

    struct Books book1;//声明结构变量 book1
    
    setBook(&book1,"十万个为啥","小红","啥东西都有解释",2);    

    printBook(book);
    printf("\n");
    printBook(book1);

    system("pause");
}

//通过指向结构变量的指针 "->"访问并初始化结构变量
void setBook(struct Books *b,char title[],char author[],char subject[],int book_id)
{   
    strcpy(b->title,title);
    strcpy(b->author,author);
    strcpy(b->subject,subject);
    b->book_id = book_id;
}

//结构作为参数
void printBook(struct Books book)
{
    printf("标题:%s \n",book.title);//book.title 访问结构成员
    printf("作者:%s \n",book.author);
    printf("主题:%s \n",book.subject);
    printf("ID:%d \n",book.book_id);
}

 

共用体

  可以在相同的内存位置定义个带有多成员的共用体,只能有一个成员带有值

union 共用体名 { 
    数据类型1;
    数据类型2;
    ...
} 变量1,变量2 ... ;

使用例子:

#include <stdio.h>
#include <string.h>

//共用体
int main()
{
    // 声明共用体
    union Data{
        int i;
        float f;
        char c[10];
    };

    // 定义共用体 变量
    union Data data;

    // 初始化共用体 数据成员
    data.i = 123;    
    printf("i:%d \n",data.i);//正常输出 123

    data.f = 20.3;
    printf("f:%f \n",data.f);//正常输出 20.299999 

    strcpy(data.c,"1234567890");
    printf("c[10]:%s \n",data.c);//正常输出1234567890

    // 访问共用体数据成员
    printf("\ni:%d \n",data.i);//i内存位置被c[10]替换输出数据有问题 875770417 
    printf("f:%f \n",data.f);//f内存位置被c[10]替换输出数据有问题 0.000000 
    printf("c[10]:%s \n",data.c);//内存位置被替换为 正常输出1234567890

    system("pause");
}

 

位域

  1000 1000 这是8位(bit) = 1字节(byte/B)  

  有些信息存储时,不需要占一个完整的字节(8位),而只需占几个或1个二进制位(如:0000 0001 只需要最后1位)

  所以c语言提供了一种数据结构,称为"位域"或"位段,可以设置变量用多少位

struct 位域结构名 
{
 //type 只能为int(整型),unsigned int(无符号整型),signed int(有符号整型) 三种类型
 type [member_name] : width ; 
 type [member_name] : width ; 
 ... 
};

这种结构在内存中占8个字节

但是我们a和b可能只存储 0或1 只需要1位,多余的空间就会浪费。 所以就需要用到位域

使用位域后,这个结构体就占用4字节(32位)。每个变量占1位 ,还剩余30位,可以分配给剩余结构体中变量

#include <stdio.h>

// 位域
int main()
{
    // unsigned int类型默认是2字节或4字节
    struct 
    {
        unsigned int a:1;
        unsigned int b:1;
    } status;
    struct
    {
        unsigned int a;
        unsigned int b;
    } status1;

    printf("status size:%dB \n",sizeof(status));//status size:4B
    printf("status1 size:%dB \n",sizeof(status1));//status size:8B

    // status.a = 2;//因为a只分配了1位 2等于二进制0010 所以会报错
    status.a = 1;//1等于二进制0001
    printf("status.a:%d \n",status.a);

    system("pause");
}

 

typedef 和 #define

typedef:为类型取一个新的名字

#define:为各种数据类型定义别名

不同点

  • typedef 仅限于为类型定义符号名称,#define 不仅可以为类型定义别名,也能为数值定义别名,比如您可以定义 1 为 ONE。
  • typedef 是由编译器执行解释的,#define 语句是由预编译器进行处理的。
#include <stdio.h>
// typedef 关键字为类型取一个新的名字
int main()
{
    typedef unsigned int BYTE;//定义unsigned int别名为 BYTE
    BYTE A;
    unsigned int a;
    
    #define zx int //定义int别名为zx
    #define PI 3.14 //定义常量
    zx b;
A
= 2; a = A; b = a;
printf(
"%d == %d \n",A,a); printf("%d,PI=%f\n",b,PI); system("pause"); }

 

输入输出

C 语言把所有的设备都当作文件。所以设备(比如显示器)被处理的方式与文件相同。

#include <stdio.h>

// 输入输出
int main()
{
    int a;
    char s[50];

    // printf("Enter a value:");
    // scanf("%s",&s);//输入 从标准输入流 stdin 读取输入
    // printf("value = %s \n",s);//输出 写入到标准输出流 stdout 

    printf("Enter a char:");
    a = getchar();//屏幕读取下一个可用的字符,并把它返回为一个整数
    printf("you entered char:\n");
    putchar(a);//把字符输出到屏幕上,并返回相同的字符。
    printf("\n");

    // char str[100];
    // printf("Enter a string: \n");
    // gets(str);//从 stdin 读取一行到 s 所指向的缓冲区,直到一个终止符或 EOF
    // printf( "\nYou entered: ");
    // puts(str);//字符串 s 和一个尾随的换行符写入到 stdout

    system("pause");
}

 

文件读写

不管是文本文件还是二进制文件,都是代表了一系列的字节。

打开文件  FILE *fopen( const char * filename, const char * mode ); 

filename 是字符串,文件名, mode 是下列值中的一个

r 打开一个已有的文本文件,允许读取文件。
w 打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会从文件的开头写入内容。如果文件存在,则该会被截断为零长度,重新写入。
a 打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容。
r+ 打开一个文本文件,允许读写文件。
w+ 打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件。
a+ 打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。
处理二进制文件:
"rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"
#include <stdio.h>

//文件的读写
int main()
{
    FILE *fp = NULL;

    // 写入文件
    fp = fopen("./13.txt","w");// "w" 打开一个文本文件,允许写入文件。如果文件不存在会创建一个新文件
    fputc(2,fp);    //把一个字符写入到流中
    fprintf(fp,"这是 创建的文件\n");    //把一个字符串写入到文件中
    fputs("这是 fputs 输入的内容\n",fp);    //把字符串 s 写入到 fp 所指向的输出流中 成功返回一个非负值,发生错误,返回 EOF
    fclose(fp);//关闭文件

    char buff[255];

    // 读取文件
    fp = fopen("./13.txt","r");//打开一个已有的文本文件,允许读取文件
    char c = fgetc((FILE *)fp);//从 fp 所指向的输入文件中读取一个字符
    printf("0:%d \n",c);

    fscanf(fp,"%s",buff);//读取字符串,但是在遇到第一个空格和换行符时,它会停止读取
    printf("1:%s \n",buff);

    fgets(buff,255,(FILE *)fp);//从 fp 所指向的输入流中读取 n - 1 个字符
    printf("2:%s \n",buff);

    fgets(buff,255,(FILE *)fp);
    printf("3:%s \n",buff);
    
    // 0:2 
    // 1:这是 
    // 2: 创建的文件

    // 3:这是 fputs 输入的内容

    fclose(fp);

    return 0;
}

 

预处理器

相当于文本替换工具,会在编译器实际编译之前执行

14.c

#include <stdio.h>

// 预处理器
#include "14.h" //包含一个源代码文件
int main()
{
    //定义宏
    #define PI 3.14 //把所有 PI 替换成3.14
    #define uInt unsigned int //把所有 uInt 替换成 unsigned int
    uInt a = 222;
    #undef PI //取消已定义的宏
    #define PI 3.1415926 //重新定义PI
    printf("%f,%d \n",PI,a);

    myFun();

    #ifndef MESSAGE //如果宏没有定义,则返回真
        #define MESSAGE "success"
    #endif
    printf("MESSAGE = %s \n",MESSAGE);
    

    return 0;
}
void myFun()
{
    printf("This is 14.h a=%d,arr=%s \n",a,arr);
}

14.h

int a = 4;
char arr[] = "hello word";
void myFun();

 

 头文件

头文件是扩展名为 .h 的文件,包含了函数声明,宏定义,变量。被多个源文件中引用共享。有两种类型的头文件:程序员编写的,编译器自带。

#include <file.h>
#include "file.h"

 强制类型转换

强制类型转换是把变量从一种类型转换为另一种数据类型

#include <stdio.h>

// 强制类型转换
int main()
{
    int a = 6;
    char b = 'b';
    float c = a + b;//a 强制转换为 6.000000 b 强制转换为 98('b'的ASCII=98)
    printf("a=%d+b=%c,c=%f \n",a,b,c);
    system("pause");
}

 内存管理

C分配和管理函数。这些函数在 <stdlib.h>头文件

void *calloc(int num, int size);//分配了 num*size 个字节长度的内存,每个字节值都初始化为0

void free(void *address);//释放 address 所指动态分配的内存空间

void *malloc(int num);//堆区分配指定大小的内存空间,不会初始化值

void *realloc(void *address, int newsize);//内存扩展到 newsize

使用malloc分配内存

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int main()
{
    char name[100];
    char *desc;

    strcpy(name,"weilian tom");

    desc = (char *)malloc(200 * sizeof(char));//动态分配内存 它的值不会初始化
    printf("%p \n",desc);

    if(desc == NULL){
        fprintf(stderr,"Error - unable to allocate required memory\n");
    }else{
        strcpy(desc,"weilian tom is a famous professional clown actor \n");
    }
    // free(desc);//释放内存
    printf("name:%s\ndesc:%s \n",name,desc);

    system("pause");

}

 

命令行参数

执行程序时,在命令行传值给程序

传入的参数是由main函数处理的,argc是参数个数 argv是参数列表

#include <stdio.h>

// 命令行参数
int main(int argc, char *argv[])
{
    printf("有%d个参数 \n",argc);
    int i;
    for(i=0;i<argc;i++)
    {
        printf("第%d个参数是:%s \n",i+1,argv[i]);
    }
}

执行程序后 第一个参数总是程序名

 时间格式化和定时器函数

#include <stdio.h>
#include <time.h>

// 标准库 time.h
void delayed(void (*fun)());
void parseTime();
int main()
{
    //time_t这是一个适合存储日历时间类型
    time_t curtime;//声明time_t格式变量
    printf("%d",curtime);

    time(&curtime);//计算当前日历时间,并把它编码成 time_t 格式

    printf("%s",ctime(&curtime));//ctime 返回表示当地时间的字符串
    
    delayed(&parseTime);

    return 0;
}

// 获取当前时间并格式化
void parseTime(){
    time_t ct;
    time(&ct);
    // 格式化时间
    struct tm *nowTime = localtime(&ct);// 返回本地时区的值来填充 tm 结构体
    
    //年tm_year 月tm_mon 日tm_mday 时tm_hour 分tm_min 秒tm_sec
    printf("%d-%d-%d %d:%d:%d \n",nowTime->tm_year+1900,nowTime->tm_mon+1,nowTime->tm_mday,nowTime->tm_hour,nowTime->tm_min,nowTime->tm_sec);
}
// 延时函数
void delayed(void (*fun)()){
    while (1)
    {
        static int t1,t2;        
        if(t2-t1 > 1000){
            fun();
            t1 = t2;
        }
        t2 = clock();//返回程序执行起到现在使用的时间
    }  
}

 

 

 

posted @ 2022-05-14 16:57  ꧁一个函数一撮头发꧂  阅读(83)  评论(0编辑  收藏  举报