zoukankan      html  css  js  c++  java
  • 翁恺C语言基础学习(基本语法结构概念)

    类型转换

    对于printf函数,任何比int类型小的数都会转换成int,而scanf()则不会,需要手动设置读取的数据类型 输入整数   scanf("%d",&a) 输入short类型scanf("%hd",&a) 

    逻辑运算符以及优先级

     

     "!age<20" 解读 就是  age 小于20 再取反  

    int age =19;
    if(!age<20){
        printf("age=19 >20 "); //正确输出
    }else{
        printf("age=19 <20");
    }

     

    逗号运算符 ,主要用于for循环中的条件多次运算

        int b = (3 + 4, 5 + 6);
        printf("b = %d
    ", b);  //输出 b = 11
        int i, j;
        for (i = 0, j = 10; i < 1; i++) {
            printf("i = %d
    ", i);        //输出 i = 0
            printf("j = %d
    ", j);        //输出 i = 10
        
        }

    函数: 单一出口原则

    函数先后顺序:C编译器自上而下的解析代码!因此在调用函数前,需要在方法前定义或者声明函数    

    本地变量:函数每次运行都会产生独立的变量空间,在这个空间中是函数这次运行所独有的称作“本地变量”,函数参数也属于本地变量

    C语言函数内部不能再次定义函数

    函数原型的定义方式

    void test1(void); //明确标识此函数不接收任何参数 
    void test2();    //参数未知,能接收任意函数,可能导致未知错误
    void sum(int ,ini); //规范标识,明确标识该函数接收2个int类型的参数,参数名称可加可不加
    sum(a,b);        //逗号在函数调用的时候作为参数分割符 
    sum((a,b));        //加上了括号,那么逗号则是运算符,代表着只传递了一个参数给sum函数
    //正例  函数只会再末尾处返回正确的值 
    int max(int a,int b)
    {
        int maxValue ;
        if(a>b)
        {
            maxValue = a;
        }else{
            maxValue = b;
        }
        return maxValue;
    }
    //反例  if和else 都可能会是函返回结果 
    int max(int a,int b)
    {
        if(a>b)
        {
            return a;
        }else{
            return b;
        } 
    }

    数组

    C99之前,数组元素数量必须是确定的字面量,C99之后允许动态分配数组大小

    一旦创建,不能改变其大小;数组在其内部依次排列,

    数组作为参数时,函数内部不能用sizeof计算数组的长度

    取址运算符“&”

    获取变量的地址,它的操作数必须是变量,

    地址值的长度取决于编译器,32位编译器为4个字节,64位则为8个字节

    //数组与取址运算符
    void
    testArray(){ int a[10]; printf("%p ",&a); //输出 000000000062FDF0 printf("%p ",a); //输出 000000000062FDF0 printf("%p ",&a[0]); //输出 000000000062FDF0 printf("%p ",&a[1]); //输出 000000000062FDF4 }

    指针变量——标识一个变量的内存地址

    void p1()
    {
        int a=7;            //定义普通变量 a =7 
        int *p = &a;        //定义指针变量 *p = 变量a的地址 
        *p=10;                //将指针变量地址上的值  修改为10; 
        printf("a=%d
    ",a);    //输出 = 10 
     } 

    指针应用一:交换两个变量的值

    指针应用二:当函数运算时会发生错误,使用函数返回值判断函数是否正确运行,而运行结果采用指针返回

    //指针应用、获取函数执行状态, 
    int divide(int a,int b,int *result) {
        int ret = 1;            //定义函数状态值 
        if(b == 0 ) ret = 0;       //除数为0时,标识函数运行出错,无法继续运行 
        else{
            *result = a/b;        //满足条件,执行运算,将结果存入指针中 
        }
        return ret;                //返回函数的运行状态  
    }

     

    指针 与 const 

    const修饰数组时,标识该数组内部的值为const 不能被修改

    void testPointConst(){
        //判断哪个被const的标识是 const在 *前面 还是在 *后面   ,
        //只要*在const前面,那么就是该指针不可修改,否则 指针指向的值 不可通过该指针去修改 
        int i = 10;
        int b = 20; 
        printf("i =%X 
    ",&i);        
        printf("b =%X 
    ",&b);         
        const int* p = &i;        //标识这个i不能通过指针去修改,但指针本身可以进行运算 ,i也可以进行运算 
        int const * p0 = &i;     //含义同上,写法区别 
        int const *p1 = &i;     //含义同上,写法区别
        int const* p2 = &i;      //含义同上,写法区别
    //------------分割线----------------------------
        int *const p3 = &i;
        int * const p4 = &i; 
        p0--; 
        printf("*p0 =%d 
    ",*p0);     //输出 20 
        
        //*p = i++;  //此处错误 assignment of read-only location '*p'
        //p3++;  //此处错误  increment of read-only variable 'p3'
        //p4++;   //此处错误  increment of read-only variable 'p4'
        i++; 
        printf("*p =%d 
    ",*p);     //输出 11 
        
        *p3 = 11; 
        printf("p3 =%X 
    ",p3); 
        printf("p3-- =%X 
    ",p3);
        printf("*p3 =%d 
    ",*p3);
        
    } 

    指针的加法运算

    //指针数组应用 
    void testPointArray(){
        int a[10];
        a[0] = 1;
        a[1] = 2;
        int *p = &a[0] ;
        printf("a[0] %X
    ",&a[0]);    // 0x62fdf0
        printf("a[1] %X
    ",&a[1]);  // 0x62fdf4
        printf("p = %X
    ",p);          //0x62fdf0
        p = p + 4;                    //指针做加法运算时,等于  (指针 加上 这个数 乘以指针类型的长度 ) 
        printf("p+4 = %X
    ",p);     //0x62fe00
        *p = 10;
        printf("a[0] = %X
    ",a[0]);    // 1
        printf("a[1] = %d
    ",a[1]);    //2
        printf("a[4] = %d
    ",a[4]);    //10
    } 

    强制类型转换

    //强制类型转换 
    void testPoint() {
        int a = 65536;                 //FFFF  int类型为 4字节  二进制为   1000,0000,0000,000,,1111,1111,1111,1111 
        int* p = &a;
        short* p1 = (short*)p;        //强转为short后  只读取 16位数据   根据补码规则,所有*p1的值=0 
        printf("%d
    ", a);            //输出 65536; 
        printf("%d
    ", *p1);             //  输出0; 
    }

    动态分配内存

    //动态内存分配
    void testMalloc() {
        //malloc 函数在 <stdlib.h>中定义 ,返回值为void*类型   
        //c99之前 动态内存分配 
        int number;
        int* a;
        int i;
        printf("输入数组大小
    ");
        scanf("%d", &number);        //读取一个整数 
        a = (int*)malloc(number * sizeof(int));          //向os请求分配 int类型 * number块连续的内存空间 ,该函数返回值为void* 因此强转成需要的int类型 
        a[1] = 2;    //指针<->数组  存在转换性 
        a++;
        int c = *a;
        printf("c = %d", c);  //输出2 
        free(a);             //释放之前 申请的内存空间 
    }

    字符串

    以0(整数)结尾的一串字符,0是标识字符串的结束,但它不是字符串的一部分,计算字符串长度的时候不包含这个0,字符串以数组的方式存在,以数组或指针的方式访问(常用指针),string.h中定义了很多处理字符串的函数

    字面量 “hello”会被编译器解析成一个数组放在某处,并且在数组末尾处添加‘’标识字符串结束,因此它的长度等于6; 可以用来初始化字符数组

    两个相连的字符串编译器会将他们联系在一起

    void testString() {
        char b = 'a';
         char*  s = "hello,world";     //指针s 初始化为指向一个字面量,历史原因编译器接收不带sonst的写法 但实际解析时 的s 是  const char* s 
        //s[0] = 'b';                    //因此不可以修改该字符数组 
    
        printf("%p
    ", b);
        printf("%p
    ", s);
        //字符串使用原则:
        //char s[] = "hello,world";                若使用数组,那么这个字符串作为本地变量空间自动被回收
        // char*  s = "hello,world";            若使用指针,不知道字符串在哪里,通常用于处理参数,动态分配空间时使用
        // 字符串可以用 char*的方式表达,但char* 不是绝对的字符串,它的本意是指向字符的数组,
        //只有当它所指的字符结尾处带有结尾的'0'时,才能被确认为是字符串
    
        //字符串输入输出 格式化   scanf("%s",str);  printf("%s" ,str);
        //scanf()函数非安全,因为它不确定读入的字符串长度有多少;
        //安全使用: 在%和s之间 插入一个 确定读取的长度 如 scanf("%7s",str),标识这一次只读取7个字符,多余的字符将会被下次scanf时获取到
    
    
        char buffer[100] = ""; // 这是一个空的字符串,buffer[0] = ‘’;
        char buffer1[] = "";    //相当于用""初始化了该字符串,它的长度只有1
    
        //字符串数组
        char a1[][10] = { "hello" };        //二维数组    ,标识数组中每一个元素的长度都是为 char[10]
        char* a[10];                        //字符串数组,标识这个数组中有10个指针,指向不确定的位置
        a[1] = "hello world";
        a[2] = "hello c";
    
        printf("%s
    ", a[1]);
        printf("%s
    ", a[2]);
    
        //getchar(int a) 获取一个字符   ,返回获取的个数
        //putchar(int a)    输出一个字符,
    }

    字符串函数

    //字符串处理函数
    void testStrFun() {
        //string.h  C语言标准库携带的头文件
        char str[] = "hello1";
        char str1[] = "hello";
        printf("str长度 = %lu 
    ", strlen(str));                //输出5
        printf("str长度 = %lu 
    ", myLength(str));                //输出5
        printf("str长度 sizeof= %lu 
    ", sizeof(str));        //输出6 因为字符串末尾还有一个''
    
        printf("str的地址是=%p
    ", str);
        printf("str1的地址是=%p
    ", str1);
        printf("mycmp是否相等%d
    ", mycmp(str, str1));            //返回0 标识2个字符串相等
        printf("是否相等%d
    ", strcmp(str, str1));                //返回0 标识2个字符串相等
    
        //拷贝函数 strcpy(char *restrict dst,const char *restrict src) //将src的字符串拷贝到dst中 返回dst   restrict标识src与dst不重叠(C99)
        char* dst = (char*)malloc(strlen(str) + 1);
        char* res = strcpy(dst, str);
        printf("res的地址是=%p
    ", res);
        printf("dst的地址是=%p
    ", dst);
        printf("%s 
    ", res);
        printf("dst = %s
    ", dst);
        char* myDst = (char*)malloc(strlen(str) + 1);
        printf("myStrCpy = %s
    ", myStrCpy(myDst, str));
    
        //字符串拼接   cahr * strcat(char* restrict s1,const char *restrict s2) 
        //将字符串s2拷贝到s1的后面,拼接成一个长的字符串,返回s1  条件s1必须具有足够的空间
        char* s2 = "world";
        char s1[20] = "hello,";
        strcat(s1, s2);
        printf("strcat = %s
    ", s1);    //输出 hello,world
    
        // strcpy,strcat,strcmp,都是非安全函数,c语言标准库中也提供了其安全版本
        //char * strncpy(char *restrict dst,const char *restrict src,size_t n)    // n 标识最多拷贝n个字符
        //char * strncat(char *restrict s1,const char *restrict s2,size_t n)    // n 标识最多拼接n个字符
        //char * strncmp(const char *s1,const char *restrict src,size_t n)        // n 标识 比较指定个数的字符,例,n=3,只比较前三个
    
        //字符串查找函数  
        //char * strchr(const char *s,int c);    //从左边开始查找指定字符     返回NULL 标识未找到
        //char * strrchr(const char *s,int c);    //从右边开始
         char s3[] = "helloworld";
        char *ps3 = strchr(s3, 'l');
        //小技巧 查找第二个
        //ps3 = strchr(ps3 + 1, 'l'); 
        //复制指定位置后的字符串
        char* t = (char*)malloc(strlen(ps3) + 1);
        strcpy(t, ps3);
        printf("t = %s
    ", t);    //输出lloworld
        free(t);
        printf("*ps3 = %c
    ", *ps3);
        //复制指定字符前的字符串
        char s4[] = "hello";
        char* p1 = strchr(s4, 'l');
        char temp = *p1;
        *p1 = '';
        char *t1 = (char*)malloc(strlen(s4) + 1);
        strcpy(t1, s4);
        printf("t1=%s
    ",t1);
        *p1 = temp;
        printf("s4=%s
    ", s4);
        free(t1);
    
        //strstr(const char* s1,const char* s2)   //查找指定的字符串,返回找到的字符串指针
        //strcasestr(const char* s1,const char* s2)   //忽略大小写 查找指定的字符串,返回找到的字符串指针
        char* find = strstr(s4, "ll");
        printf("find = %s
    ", find);
    
    
    }

    枚举:是一种用户自定义数据类型, enum 类型名称 {属性名称1,属性名称2};  它的类型为int,依次从零开始

    //枚举
    enum Color{    red,yellow,green,blue,colorLength};
    void testenum() {
        enum Color deviceColor = 2;
        switch (deviceColor)
        {
        case red:printf("红色
    ");break;
        case yellow:printf("黄色
    ");break;
        case green:printf("绿色
    ");break;
        case blue:printf("蓝色
    ");break;
        }
    }

    结构的使用

    //结构   ,若声明在函数内部,则只能在当前函数使用
    //声明于函数外部,标识可以被多个函数调用
    //结构形式1
    struct  date
    {
        int year;
        int month;
        int day;
    };
    //结构形式二:隐藏结构名称,结构末尾处声明p1,p2两个结构变量
    struct {
        int x;
        int y;
    } p1,p2;
    
    //结构形式三:声明结构,结构末尾处声明p1,p2两个结构变量
    struct point {
        int x;
        int y;
    } p3, p4;
    
    void test_struct() {
    
        struct date today;
        today.year = 2021;
        today.month = 7;
        today.day = 2;
        printf("today is %i-%i-%i 
    ", today.year, today.month, today.day); //输出2021-7-2
        struct date thisday = { .month=7,.day=2 };
        printf("thisday is %i-%i-%i
     ", thisday.year, thisday.month, thisday.day);    //输出0-7-2
        //当只初始化结构中部分属性的值时,其他属性被赋值为0
        struct person {
            int age;
            char* name;
        };
        struct person zhangsan = { .age = 16 };
        struct person lisi = zhangsan;
        lisi.name = "李四";
        //结构变量的名字不是结构变量的地址,获取结构变量的地址,必须使用‘&’运算符
        printf("person zhangsan is %s,年龄是%i
     ", zhangsan.name, zhangsan.age);    //输出  person is (null),年龄是16
        printf("person lisi is %s,年龄是%i
     ", lisi.name, lisi.age);    //输出  person is 李四,年龄是16
    
        //结构作为函数变量时,函数内部获取的时该结构的值,也就是外部结构的克隆体,函数内部操作不会改变外部结构变量
        struct person *p = &zhangsan;
        printf("p->.age = %d
    ", p->age);        //输出p->.age = 16;    ‘->’ 箭头符合标识该指针指向结构的属性值
    }
    //嵌套结构,地址空间分布
    void test_nest_struct() {
        struct  rectangle
        {
            struct point start;
            struct point end;
        }rt1,rt2;
        printf("rt1的地址%p
    ", &rt1);                        //012FF934
        printf("rt1中start 地址%p
    ", &rt1.start);            //012FF934
        printf("rt1中end 地址%p
    ", &rt1.end);                //012FF93C
        printf("rt1中start 中x的地址%p
    ", &rt1.start.x);        //012FF934
        printf("rt1中start 中y的地址%p
    ", &rt1.start.y);        //012FF938
    }
    
    //自定义别名
    typedef long int64_t;        //int64_t 可以在其他地方代替 int使用
    typedef struct { int age; char *name; } Person;        //标识 一个匿名结构,它的引用名称为Person
    void test_typedef() {
        Person s = { 25,"张三" };
        printf("person is name = %s,age=%d
    ", s.name, s.age);
    }
    结构的相关使用

    联合体

    //联合体(共用体) union
    //结构体和共用体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员
    typedef union MyUnion{int age;char ch[sizeof(int)];} People;
    void testunion() {
        People people;
        people.age = 98;
        int i;
        for (i = 0; i < sizeof(int); i++) {
            printf("%02hhX", people.ch[i]);
        }
        printf("%d", people.age);
    }

    全局变量:任何函数内部都可以使用,在main函数之前被初始化,若没有初始化则编译器会将其赋值0(指针为null),初始化必须是一个确定值(常量or字面量),若函数内部存在同名变量,则该全局变量在函数内部被隐藏

    静态本地变量:实际是全局变量,但是它的的作用域只在被定义的函数内部,“全局生命周期,本地作用域”

    //尽量避免使用全局变量和本地静态变量
    int *f1() {
        int i = 12;
        printf("i=%p
    ", &i);    //输出    
        return &i;
    }
    int g() {
        int k = 22;
    
        printf("k=%p
    ", &k);    //输出    k=005FF658
        printf("k=%d
    ", k);    
    }
    void test_variable() 
    {
        int *p = f1();
        printf("*p=%d
    ", *p);    //输出f1函数返回值 12   地址值 i=005FF658
        g();                    //输出 k = 22,          地址值 i=005FF658
        printf("*p=%d
    ", *p);    //输出 *p=-858993460  
        //说明*p指向的f1函数的本地变量i的内存空间在函数执行完毕后,又被分配到其他函数中使用!
    }

    宏:

    /*
    编译预处理指令,C语言中以“#”开头的的都是编译预处理指令
    #define 用来定义一个宏 格式 : #define  <宏名称>  <值>  <//注释内容>
    示例:#define PI 3.1415926
    在C语言编译器编译之前,编译预处理程序会将程序中使用的宏,替换成原有的值,纯文本替换
    tips: gcc--save-temps 可以查看相关文件
    
    如果一个宏中有其他宏的名字,也会被替换
    若宏超过一行,则需要在换行处加‘’  
    宏后面出现的注释不会被当作宏的内容
    
    没有值的宏,用于条件编译,后面有其他的编译预处理指令来检查这个宏是否被定义过了
    #define _DEBUG   
    C语言预定义宏        '__LINE__','__FILE__','__DATE__','__TIME__','__STDC__'
    
    带参数的宏,可以理解为一个表达式,最终时需要表达式的值,因此用一对括号将其包裹 ,参数也可能时表达式——也需要括号
    正例    #define RADTODEG(X) ((X)*57.29578)
    
    反例1:#define RADTODEG1(X) (X *57.29578)    
    反例2:#define RADTODEG2(X) (X)*57.29578   
    
    当把 x=(5+2)以表达式的方式传入反例1时  会得到: 5 + 2 *57.29578   
    当执行  180/RADTODEG2(1)  以表达式的方式传入反例2时  会得到: 180/(1)  *57.29578 =10313.240400
    
    参数宏与函数的区别:宏没有类型检查,效率高,关键词纯文本替换,因此空间占用高,函数有类型检查,以及调度等额外开销
    部分宏会被inline函数取代
    
    */
    
    void test_pretreatment() {
    
        //file 文件全路径,line 所在的行号
        printf("%s:%d
    ", __FILE__, __LINE__);//d:visualstudioworkwork_cstudy_cstudy_cstudy_c.c:389
        printf("%s:%s
    ", __DATE__, __TIME__);//Jul  2 2021:18:03:06  date 当前日期,time 当前时间
    }

    C语言项目组织结构

      #include <xxx.h>  :标识让编译器在系统指定目录下查找头文件
      #include “xxx.h”  :标识让编译器在当前目录下查找头文件,若未找到再去系统指定目录下查找

    环境变量和编译器命令行参数也可以指定头文件查找的目录

    编译器知道自己头文件的具体目录

    #include  :  编译器预处理指令,它会将找到头文件内容,以纯文本的方式插入到当前的文件中,

     .h头文件,就是将函数原型放在这个文件中,在函数具体实现的“.c文件”include这个头文件,让编译器在编译的时候能正确识别函数的返回值,参数等限定内容,头文件在引用文件和实现文件之间形成一个沟通的桥梁,保证函数的一致性

    static 修饰函数,变量时标识这个函数,变量不对外部公开,仅在当前的".c"文件中使用

     

    标准头文件结构

    #pragma once    //该指令可以保证该头文件只会被 引入一次,但部分编译器不支持
    
    #ifdef ——A_HEAD——    //判断是否已经引入了这个头文件,并且定义为 ——A_HEAD——
    #define ——A_HEAD——     //若未引用,则引用这个头文件
    //变量声明 标识该变量能被外部所访问
    extern double PI;
    //函数原型声明
    int max(int a, int b);
    
    
    #endif // DEBUG            //标识 A_HEAD这个定义结束

     

  • 相关阅读:
    javaweb基础----省市县三级联动(javascript版)
    javaweb开发出错排查思路
    javaweb基础----使用原生fileupload上传文件时找不到上传的文件位置
    javaweb基础----Tomcat启动失败(Tomcat9)
    javaweb基础----Tomcat端口被占用
    命令行显示
    java开发环境
    java se ee me 区别
    解决win10 2503 2502 权限等问题
    浏览器只有ie可以登录
  • 原文地址:https://www.cnblogs.com/shenwenbo/p/14927938.html
Copyright © 2011-2022 走看看