C语言基础

作者: 宝塔山上的猫 | 来源:发表于2017-10-23 00:00 被阅读55次

    C语言基础

    这是很简单的C语言,如果想深入了解,可以自己自学,C语言毕竟可以说是高级编程语言的基础呀,书籍一堆一堆的。
    我们要学习C语言,使用C语言开发要怎么搞呢?
    首先就是一个必要的开发工具,这里推荐开源工具Dev-C++,一款用于编译C语言的工具,就像eclipse开发java一样,除了安装Dev-C++之外,不在需要其他工具了。

    C之Hello world

    打开Dev-C++工具,直接选择新建或者快捷键CTRL+N新建一个C文件,然后我们输入以下代码:

    #include<stdio.h>    
    #include<stdlib.h>   
    main(){    
           printf("helloworld!\n");  //System.out.println();   "\n"换行符
           system("javac Hello.java");
           system("java Hello");
           system("notepad");
           system("pause"); //system执行windows的系统命令
           }
    

    然后保存到命名为C 的目录中,将文件保持为hello.c,但这只是一个原始代码,继续在Dev-C++中的菜单栏找到运行,然后选择编译,这时你会发现在hello.c的旁边出现一个hello.exe。
    哈,是不是很神奇,我们自己搞出一个exe可执行文件!
    当然我们现在还不用执行hello.exe,我们要在C目录下添加一个Hello.java(标准的java文件)的文件夹,里面输入Hello,java就行了。
    这时执行hello.exe,就会打开命令提示符,也就是CMD命令框。
    首先会输入“helloworld!”,然后编译,接着执行Hello.class,最后停止。

    这便是运行流程。那么我们来说说文件中代码的含义吧!
    其中

    #include<stdio.h>    // 相当于 java的import .h c的头文件 stdio.h standard io 标准输入输出
    #include<stdlib.h>   // stdlib standard library 标准函数库    java.lang
    

    在写C语言的时候添加上这两句话准没错的。
    然后main(){ }这是一个主程序入口,就类似于java中的public static void main(String[] args)。
    最后就是system(),这相当于system执行windows的系统命令 ,所以在执行system("javac Hello.java");是相当于我们在命令提示符中直接输入javac Hello.java。

    C语言的基本数据类型

    前面我们已经学会了C语言的Hello world,现在我们要了解一下C语言的数据类型了,我们直接通过与java对比,看看他们直接有多大区别

    java基本数据类型  所占字节            C数据类型   所占字节
    boolean             1   
    byte            1
    char            2               char        1个字节
    short           2               short       2
    int             4               int         4
    long            8               long        4
    float           4               float       4
    double          8               double      8
    

    上面是java和C直接都有的数据类型,同时对比双方所占字节数,但是上面并不是C所有的数据类型,C一共有以下几种数据类型:

    char, int, float, double, long, short, signed, unsigned, void
    * signed 有符号数 最高位是符号位 可以表示负数 但是表示的最大值相对要小
    * unsigned 无符号数 最高位是数值位 不可以表示负数  表示的最大值相对要大
    * signed unsigned 只能用来修饰整形变量 char short int long
    * C没有 boolean  byte C用0和非0表示false true
    

    上面我们了解C所占的字节数,但是如何验证呢?可以通过以下代码验证:

    #include<stdio.h>    
    #include<stdlib.h>
    // char, int, float, double, long, short, signed, unsigned, void
    // signed unsigned 有符号 无符号  只能用来修饰整形变量  char int short long 默认有符号
    // sizeof(int)
    
    main(){    
       printf("char占%d个字节\n", sizeof(char));
       printf("int占%d个字节\n", sizeof(int));
       printf("short占%d个字节\n", sizeof(short));
       printf("float占%d个字节\n", sizeof(float));
       printf("long占%d个字节\n", sizeof(long));
       printf("double占%d个字节\n", sizeof(double));
       unsigned char c = 128;
       printf("c = %d\n",c);
    
           system("pause");
           }
    

    其中%d表示的是int类型的占位符,请大家注意,在使用占位符的时候千万不要写错!然后sizeof(int)这个方法表示的是其中的变量所占的字节数。

    占位符的使用

    刚才说到%d这个占位符,那么其他的占位符又是怎样的呢?

    %d  -  int
    %ld – long int
    %lld - long long
    %hd – 短整型
    %c  - char
    %f -  float
    %lf – double
    %u – 无符号数
    %x – 十六进制输出 int 或者long int 或者short int
    %o -  八进制输出
    %s – 字符串
    

    以上就是C语言中的少数占位符,使用代码如下:

    #include<stdio.h>    
    #include<stdlib.h>    
    
    main(){    
          char c='a';
          short s = 123;
          int i = 12345678;
          long l = 1234567890;
          float f = 3.1415;
          double d = 3.1415926;
          printf("c = %c\n", c);
          printf("s = %hd\n", s);
          printf("i = %d\n",i);
          printf("l = %ld\n",l);
          printf("f = %.4f\n",f);  //默认输出的是6位有效数字的小数 想手动指定 加上.X
          printf("d = %.7lf\n",d);
          printf("%#x\n",i);
          printf("% #o\n",i);
          //char cArray[]={'a','b','c','d','\0'};  // C数组[]要写在变量名后面, \0表示的是字符串结束符,这种方式不能输入汉字
          //char cArray[]="abcdefg";    // 这种方式不用手动写入结束符
          char cArray[]="你好";   // 输入汉字
          printf("cArray = %s",cArray);
           system("pause");
           }
    

    C语言之输入函数

    学习计算机语言逃不过一个宿命,那就是讨论内存地址,而C语言中更是讲究。
    在这里我们先通过键盘输入班级人数,然后通过取出count的地址来保存班级人数

    #include<stdio.h>    
    #include<stdlib.h>
    //scanf("占位符",内存地址)
    main(){    
          printf("请输入班级的人数:");
    
          int count;
          scanf("%d", &count); //&取地址符
          printf("班级的人数是%d\n",count);
          char cArray[20];//c的数组不检测下标越界
          printf("请输入班级的名字:");
          scanf("%s",&cArray);
          printf("班级的人数是%d,班级的名字%s\n",count,cArray);
          printf("count的地址%d\n",&count);
          printf("cArray的地址%d\n",&cArray);
           system("pause");
           }
    

    C中的指针

    C语言中的指针可以说是一个重点难点,比较难以理解,因为我们内存地址让人难以理解,所以这里只是抛砖引玉的粗略的讲讲指针的概念。

    关于符号*

      其中int* pointer 表示声明一个int类型的指针变量pointer
      x * y  表示乘法运算      
      *pointer; 取出指针变量pointer 中保存的内存地址对应的内存中的值
    

    接着我们来看看指针的运用:

    #include<stdio.h>    
    #include<stdlib.h>    
    
    main(){  
           int i = 123;
           //一般计算机中用16进制数来表示一个内存地址
           printf("%#x\n",&i);
           //int* int类型的指针变量  pointer指针  指针变量只能用来保存内存地址
           //用取地址符&i 把变量i的地址取出来 用指针变量pointer 保存了起来
           //此时我们可以说 指针pointer指向了 i的地址
           int* pointer = &i;
           //int *pointer ; int * pointer   
           printf("pointer的值 = %#x\n",pointer);
           printf("*pointer的值%d\n",*pointer);
           *pointer = 456;
           printf("i的值是%d\n",i);
           system("pause");
           }
    

    运行这段代码,会得到以下结果:

    0x62fe44
    pointer的值 = 0x62fe44
    *pointer的值123
    i的值是456  
    

    其中i的值为123,但是计算机给i分配一块内存地址既是0x62fe44这个空间,然后这个空间存放的值为123,这便是i=123的含义。
    然后我们在创建了int类型的指针变量point,当然指针变量point也获得了自己的一个内存地址。
    同时通过&i将i的内存地址取出,赋予了point,也就是说指针变量point的内存地址的值存入了0x62fe44这个内存地址,而通过 0x62fe44内存地址是可以找到123这个值的。
    那么如何在指针变量point中取出123呢?使用pointer便可以取出。
    最后
    pointer = 456就是将0x62fe44这个内存地址的值重新赋值为456,而i的内存地址是指向0x62fe44的,所以当0x62fe44的值发生改变时,i的值也随之发生改变。
    这里面有点绕,不好理解,但指针方面的知识确实要好好学习一下。

    指针的错误使用

    在使用指针之前要注意对其进行赋值,没有赋值就进行操作的指针被称为野指针,在Windows中是不允许这样操作的,因此指针使用之前要初始化 赋给它一个自己程序中声明的变量的地址
    其次,指针使用的时候要注意 int类型的指针要指向int类型的内存地址, double类型的指针要指向double类型的地址,如果乱指会出bug
    下面是错误的示例代码:

    #include<stdio.h>    
    #include<stdlib.h>    
    /**
    */
    main(){  
             //注意,这是错误的示例代码
             int i;
             double d = 3.1415;
           int* pointer = &d;
           printf("pointer的值=%#x\n",pointer);
           printf("*pointer = %d\n",*pointer);
           system("pause");
           }
    

    C语言交换两数的值

    一个传统的问题,如何交换两数的值,以下代码演示

    #include<stdio.h>    
    #include<stdlib.h>    
    /**
    *值传递 和引用传递  值传递和引用传递传递的实际上 都是数值  只不过引用传递传递的是地址值
    *如果想通过一个子函数来修改main函数中变量的值 一定要用引用传递
    */
    // 值传递
    swap(int i, int j){
             int temp = i;
           i = j;
           j = temp;
             }
    
    // 引用传递         
    swap2(int* pointer, int* pointer2 ){
             int temp = *pointer;
             *pointer = *pointer2;
             *pointer2 = temp;   
               }
    main(){    
           int i = 123;
           int j = 456;
          // swap(i,j); // 无法交换两数的值
           swap2(&i,&j); // 通过交换内存地址中的值,成功交换两数的值
           printf("i的值%d,j的值%d\n",i,j);
           system("pause");
           }
    

    为什么swap方法无法交换i和j的值呢?举个不恰当的例子,那边是局部变量和成员变量的区别,首先swap外部有i和j,然后传入swap之后,i和j便与外部的i和j断绝了关系,因此在内部交换之后无法将交换信息传递到外部。
    而swap2采用的是引用传递,通过获取内存地址,改变内存地址之中的值,这时才能真正的改变两数的值

    c中数组与指针的关系

    数组实际上就是一块连续的内存空间,而数组变量名的地址实际上是第一个元素的地址,因此我们通过指针获取数组变量名的内存地址,可以说是获得了数组的内存地址。
    同时我们通过对内存地址的加减法就能获得数组中的元素。需要注意的是对内存地址的加减法中,相加得到的结果实质上与数据类型有关,如我们使用指针变量*(pointer+0),如果是int类型的数组,实质上会加上4个字节,得到下一个数组元素,char类型的数组也是一样

    #include<stdio.h>    
    #include<stdlib.h>    
    
    main(){             
      // char array[] = {'a','b','c','d','\0'};
       int array[] = {1,2,3,4};
       printf("array[0]的地址%#x\n",&array[0]);
       printf("array[1]的地址%#x\n",&array[1]);
       printf("array[2]的地址%#x\n",&array[2]);
       printf("array[3]的地址%#x\n",&array[3]);
       printf("array的地址%#x\n",&array);
       //数组变量名的地址实际上是第一个元素的地址
       char* pointer = &array;
        //int* pointer = &array;
        char array2[] =  "hello from c"
       char* pointer2="hello from c";
       //printf("%s\n",pointer2);
       /*
       // char类型中每次运算是相加1个字节
       printf("*(pointer+0)=%c\n",*(pointer+0));
       printf("*(pointer+0)=%c\n",*(pointer+1));
       printf("*(pointer+0)=%c\n",*(pointer+2));
       printf("*(pointer+0)=%c\n",*(pointer+3));
       */
       // 在int类型数组中,指针变量每次运算结果是相加4个字节
       printf("*(pointer+0)=%d\n",*(pointer+0));
       printf("*(pointer+1)=%d\n",*(pointer+1));
       printf("*(pointer+2)=%d\n",*(pointer+2));
       printf("*(pointer+3)=%d\n",*(pointer+3));
    
           system("pause");
           }   
    

    C语言中指针变量的字节数

    指针变量的所占字节数与所指向的数据类型无关,与操作系统有关,32位操作系统地址总线是32位,用4个字节的变量来保存32位操作系统的内存地址 1byte 8位 4*8=32
    因此32位操作系统 指针变量占4个字节,而64位操作系统 指针变量占8个字节 。

    #include<stdio.h>    
    #include<stdlib.h>    
    /**
    32位操作系统地址总线是32位   4个字节的变量来保存32位操作系统的内存地址 1byte 8位  4*8=32
    32位操作系统 指针变量占4个字节
    64位操作系统 指针变量占8个字节
    */
    main(){    
          int* pointer;
          double* pointerD;
          printf("int类型的指针变量占%d个字节\n",sizeof(pointer));
          printf("double类型的指针变量占%d个字节\n",sizeof(pointerD));
           system("pause");
           }    
    

    C语言之多级指针

    多级指针:数星星,有几个星就是几级指针。其中二级指针只能保存一级指针的内存地址,同意三级指针只能保存二级指针的内存地址。
    那么如果想通过三级指针拿到最初的变量的值,就使用 ***point3

    #include<stdio.h>    
    #include<stdlib.h>    
    
    main(){    
          int i = 123;
          //int类型的一级指针
          int* pointer = &i;
          //int类型的二级指针 二级指针只能保存一级指针的地址
          int** pointer2 = &pointer;
          //int类型的三级指针
          int*** pointer3 = &pointer2;
          // 取出初始值
          printf("***pointer3 = %d\n",***pointer3);
           system("pause");
           }
    

    C主函数获取临时变量的地址

    main函数获取子函数中临时变量的地址,此时要注意值传递和引用传递的区别,我们想要获得的是内存地址,因此必须传入内存地址,同时二级指针才能保存一级指针的内存地址,因此子函数中要传入的变量应为二级指针

    #include<stdio.h>    
    #include<stdlib.h>    
    
    function(int** pointer){
       int i = 123;
        *pointer = &i;  
        printf("i的地址%#x\n",&i);    
              }
    
    main(){    
          int* pointer1;
          function(&pointer1);
          printf("pointer1的值%#x\n",pointer1);
           system("pause");
           }
    

    C语言中的内存地址回收

    栈内存由系统统一分配统一回收
    静态内存分配 栈内存大小固定的 内存地址是连续的

    #include<stdio.h>    
    #include<stdlib.h>    
    
    int* getData(){
         int array[] ={1,2,3,4,5};
         printf("%#x\n",&array);
         return &array;
         }
    int* getData2(){
         int array[] ={5,4,3,2,1};
         printf("%#x\n",&array);
         return &array;
         }
    
    main(){    
         int* pointer = getData();
         getData2();
         printf("%d,%d,%d\n",*(pointer+0),*(pointer+1),*(pointer+2));
         printf("%d,%d,%d\n",*(pointer+0),*(pointer+1),*(pointer+2));
         printf("%#x\n",pointer);
           system("pause");
           }
    

    在这一例子中,pointer指向的是getData()中的内存地址,可是为什么得到的结果却是getData2()的值呢?那是因为在执行完getData()之后,获得的栈内存地址被回收,然后直接被getData2()使用,导致其中的值被修改。所以使用指针的时候要注意,栈内存只能使用一次,之后会出现同一块内存地址被其他程序占用的情况。如果想让内存始终保持一个只,那么则要使用堆内存

    c中的动态内存分配

    java new对象就会申请一块堆内存,而C语言则要手动申请,使用malloc申请一个固定的内存大小,如malloc(sizeof(int)*5)申请5个int类型大小的内存,注意申请了堆内存需要手动释放,如果忘记释放容易造成内存泄漏。
    其中对内存的特点:不连续的;大小不固定,取决机器的状态

    #include<stdio.h>    
    #include<stdlib.h>    
    //c   malloc memory allocation 内存分配
    main(){
            //malloc 接收的参数 申请内存大小 返回一个内存地址值 申请到的也是一块连续的内存空间   
          int* pointer = malloc(sizeof(int)*5);
          *(pointer+0) = 1;
          *(pointer+1) = 2;
          *(pointer+2) = 3;
          *(pointer+3) = 4;
          *(pointer+4) = 5;
          //C for 循环 循环的临时变量i 要先声明再使用
          int i;
          for(i = 0;i<5;i++){
                  printf("第%d个元素的值= %d\n",i,*(pointer+i));
                  }
          free(pointer);
          printf("第一个元素的值%d\n",*(pointer+0));
           system("pause");
           }     
    

    学生学号管理系统

    #include<stdio.h>    
    #include<stdlib.h>    
    /**
    保存班级人数
    申请一块堆内存保存学生的学号
    来了几个插班生
    扩展一下堆内存
    保存插班生的学号
    realloc re-
    */
    main(){
           printf("请输入班级的人数:");
           int count;
           scanf("%d",&count);
           //申请一块堆内存
           int* pointer = malloc(sizeof(int)*count);
           int i;
           for(i = 0;i<count;i++){
                 printf("请输入第%d个学生的学号:",i+1);
                 scanf("%d", pointer+i);
                 }  
           for(i = 0;i<count;i++){
                 printf("第%d个学生的学号是:%d\n",i+1,*(pointer+i));  
                 }
           printf("请输入插班生的人数:");
           //声明一个变量increment用来保存 插班生的人数
           int increment;
           //接受用户的输入
           scanf("%d",&increment);
           //重新申请一块足够大的内存
           //如果 malloc申请到的内存后面还有足够的空间 realloc会在malloc申请的内存空间后继续申请足够大的内存空间
           //如果 malloc申请到的内存后面没有足够的空间 realloc会找到一块足够大的堆内存 并且把 malloc申请到的内存中的值复制过来
          pointer = realloc(pointer,sizeof(int)*(count+increment));
          for(i = count;i<count+increment;i++){
                 printf("请输入第%d个学生的学号:",i+1);
                 scanf("%d", pointer+i);
                }
          for(i = count;i<count+increment;i++){
                printf("第%d个学生的学号是:%d\n",i+1,*(pointer+i));  
                }
           system("pause");
           }
    

    C的结构体

    c结构体类似java的class,使用struct来声明c的结构体。
    结构体的大小大于等于结构体中每一变量的占字节数的和,
    结构体的大小是最大的那个变量所占字节数的整数倍。如果想获得结构体中的变量,可通过获取结构体指针,然后通过结构体指针获得结构体变量
    注意C结构体中不能定义函数,如果想要在结构体中调用方法,可以使用函数指针。
    函数指针的定义的方式:返回值(函数指针变量名字)(返回值),如下面示例代码中的void(studypointer)();
    其中 -> 表示间接引用运算符,可以访问结构体里面的变量,如示例代码中的stuPointer->age

    结构体的特点
    
    • 结构体中的属性长度会被自动补齐,这是为了方便指针位移运算
    • 结构体中不能定义函数,可以定义函数指针
    • 程序运行时,函数也是保存在内存中的,也有一个地址
    • 结构体中只能定义变量
    • 函数指针其实也是变量,它是指针变量
    • 函数指针的赋值: 函数指针只能指向跟它返回值和接收的参数相同的函数
     #include<stdio.h>    
     #include<stdlib.h>    
    
     void study(){
                printf("good good study!\n");
                }
     struct Student{
           int age;  //8
           int score;  // 4
           char sex;   //1
           void(*studypointer)();
           } ;
     main(){    
      struct Student stu = {18,100,'f'};
      stu.studypointer = &study;
      stu.studypointer();
      struct Student* stuPointer = &stu;
      printf("*stuPointer.age = %d\n",(*stuPointer).age);
      (*stuPointer).sex ='m';
      printf("stu.sex = %c\n",stu.sex);
      printf("stuPointer->age = %d",stuPointer->age);
      //printf("stu.age = %hd\n",stu.age);
      //printf("stu.score = %d\n",stu.score);
      //printf("stu.sex = %c\n",stu.sex);
     // printf("结构体student占%d个字节\n",sizeof(stu));
            system("pause");
            }
    

    C中的联合体

    联合体占字节数取决于,其中成员占内存空间最大的那一个 。
    联合体只能保存一个变量的值,并且联合体共用同一块内存。

    #include<stdio.h>    
    #include<stdlib.h>    
    /**
    
    */
    union u{
          int num; //4个字节
          double d; //8个字节
          }
    main(){    
       union u u1;
       u1.num = 123;
       u1.d = 123.456;
       printf("union占%d个字节\n",sizeof(u1));
       printf("u1.num = %d\n",u1.num);
           system("pause");
           }
    

    C中的枚举

    C中的枚举使用enum,同时枚举中的元素可赋值

    #include<stdio.h>    
    #include<stdlib.h>    
    /**
    */
    enum weekday{
         MON=9, TUE, WEND,THUR,FRI,SAT,SUN
         };
    main(){    
       enum weekday day = MON;
       printf("day= %d",day);
           system("pause");
           }    
    

    C中的自定义类型

    自定义类型使用typedef可给类型赋予别名,如 typedef int i;此时能将i当作int类型使用。
    用途:如果结构体名字过长,可起到省略的作用,其中在Android的jni中大量使用了typedef设置别名,便于辨认数据类型

    // 简单实例:
    #include<stdio.h>    
    #include<stdlib.h>    
    /**
    */
    typedef int i;
    main(){    
               i j = 123;
               printf("j = %d\n",j);
           system("pause");
           }
    

    如在之前的结构体中有

           struct Student{
                 ···
                 } ;
    

    可更改为:

    typedef struct Student{
    int age; //8个字节
    int score; // 4个字节
    char sex; //1个字节
    // 使用函数指针调用函数
    void(*studypointer)();
    } stud;

    此时在main函数中就可以直接使用stud代替Student,如下:

    main(){    
     stud stu = {18,100,'f'};
     // 函数指针指向函数的内存地址
     stu.studypointer = &study;
     ····
    

    }

    相关文章

      网友评论

        本文标题:C语言基础

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