zoukankan      html  css  js  c++  java
  • 浅谈C的应用与常见error

      开通博客很久了,一直想写一些自己的东西,由于所学知识有限,一直不能落笔,废话少说,进入正题。

      我下面所写的都是用“.c”后缀的。“.c”后缀是c源文件的后缀,“.cpp”后缀是c++源文件的后缀。c++继承了c语言的一些特性,所以有些bug在“.cpp”里是可以通过的。

      1、scanf()你真的了解了么?;

        scanf()是有返回值的,返回值类型是int,这个你知道么?它返回了成功读入输入信息的个数。

        那么我们可以利用它来做什么呢?

        非零即真,这句话你一定听说过,而且不止一次。那么我们看一下下面的代码。

          int a;
           while (scanf("%d", &a) == 0)
           {
                printf("请输入合法字符:");
           }

        这样写代码是不是好一点,再输入的同时判断了输入的是否正确。但这会使你进入一个死循环。为什么会这样呢?

          我们在输入时,我们的键值需要一块缓冲区在存放键值,如果输入的键值不被scanf()接受,那么它会一直在缓冲区中,那么再仔细看一看上面的代码,你是不是明白了呢。

        解决这个问题并不难,只要将输入的键值接受,他就不会在缓冲区中影响你的程序了。看看下面的代码,有没有豁然开朗,哇,scanf()还可以这么使用!

         int a;
           while (scanf("%d", &a) == 0)
           {
                getchar();
                printf("请输入合法字符:");
           }

        2、你还在纠结while(),for();

        初学者可能会纠结什么场合用while(),什么场合用for()。

        在我看来,这根本不存不值得我去纠结,我们应该想的是使用哪个可以使程序更简单,别人更容易看懂。

        while(),for()的本质都是循环,只是形式不一样而已。

        int i = 0;            int i;

        while (i<10)           for (i=0; i<10; i++)

        {                {

          i++;

        }                }

        上面的代码当然是for()更简明一写,但1、中的例子却是使用while()。for()比while()在语句要多一些,这是你需要的么?

        根据每个人的代码风格不同,不能统一要求使用for()或者while()。建议初学者可以两个都试一下,感觉他们之间的不同,写出漂亮的代码。

      3、switch()你会使用么?

        switch(n)

        {

        case 1:

          break;

        case 2:

          break;

        default:

        }

        你一直这么使用么?

        我们都知道case 后面不加break,会一直执行下面的代码,直到代码全不执行完或者遇到break。

        利用这个特性,我们是不是可以做一些其他的事,比如加法,一年有十二个月,我想算出4月6号是今年的第几天,你想到怎么使用了么?我们看一下下面的代码。

        switch (month-1)
           {
        case 11: sumMonth += 30;
        case 10: sumMonth += 31;
        case 9: sumMonth += 30;
        case 8: sumMonth += 31;
        case 7: sumMonth += 31;
        case 6: sumMonth += 30;
        case 5: sumMonth += 31;
        case 4: sumMonth += 30;
        case 3: sumMonth += 31;
        case 2:
               if ((i%4==0 && i%100!=0) || i%400==0)
                {
                       sumMonth += 29;
               }
                else
                {
                   sumMonth += 28;
                }

        case 1: sumMonth += 31;
        }

        你看懂了么?只是提供一个思路,将我们思维打开,你会看见另一片天空。

        你在case中定义过变量么?

        case中为什么不能定义变量呢?

           int a;
           switch (a)
          {
          case 1:
              int b;
              break;
          case 2:
              b = 0;
              break;
          }
        你这么任性,编译器怎么办!

        那我们为什么不规定case 1定义的变量只能在case 1中使用呢?在6、变量作用域中你将会找到答案。

        当然,我们不应该只局限于switch(),if() else也一样可以完成多重选择的任务,不过switch()内置3-999个标签,使用switch()的程序运行速度可能稍快一些,代码也更简洁。

      

      4、if()的使用

        我们都知道if()用于判断。那么当你判断两个字符相等时,你会怎么写?

        int a;
           if (a == 5)

        如果是我,我会写成

        int a;
           if (5 == a)

        功能是一样的,何必这么麻烦?我们在做一个项目时,不可能十行代码调试半个小时,当我们思维在键盘上飞舞时,我才体会到一个程序员的快乐。在我们正在体验这种快乐时,手误在所难免,如果写成if(a=5),这个错误够你找两天的。但是if(5=a),编译器就可以帮你找到。

        当你想判断两个字符串是否相等时,你会不会这样写?

        char c_c1[10] = "acdefg";

        char c_c2[10] = "qwerty";

        if (c_c1 == c_c2)或者if (c_c1[10] == c_c2[10])

        总之都是不对的。字符串,数组,指针后面还会有所涉及,这里不做详谈。

        比较两个字符串我们用到了C函数库中提供的strcmp()函数,它包含在<string.h>头文件中。strcmp()返回了一个int类型的值,-1,0,1。0代表两个字符串相等,-1,1是由于字符之间的比较(ASCII)决定的。下面我看一个例子。

        char c_ch[10] = "qwertyui"  //最多可以存放9个字符,最后一位是”“

        char *p_ch = "adfgf"

        strcmp(c_ch, p_ch);  //strcmp()中的两个参数是两个字符串的地址

        strcmp("qwertyui", "adfgf");  //这样也是可以的,字符串存放的也是首字母的地址,后面会提到。

        

      5、你知道头文件的作用么?

      我们写代码都习惯把#include <stdio.h>最先写上,但你知道它的作用么?

      #include :文件包含

      当预处理器发现#include 指令后,就会寻找 <>中的文件名并把这个文件包含到你写的程序代码,替换源文件(就是你写的文件)中的#include。虽然你只写了一行代码,但是编译器却给你添加了很多代码。

      <stido.h>包含了输入输出函数,所以在程序的调试阶段,他是不可少的。

      #include "gaozy" 这类头文件你见过么?它与#include <stdio.h>又有哪些区别呢?<>包含的头文件,编译器会在系统目录中搜索。“”包含的头文件,编译器会先在当前工作目录中搜索,如果没有,再去系统目录中搜索。也可以简单的理解成C函数库里的函数在<>包含的头文件里,而我们自己写的函数则放在“”文件中。

      6、变量作用域

      int i;

      while (1)

      {

        int i = 0;

        printf("%d", i);

      }

      这样会报错么?当然不会,因为两个int i;并不在同一个作用域。

      那么什么是作用域,怎么分辨作用域呢?

      我们通常把一个{}内的代码看成在一个作用域。这也是为什么case里不可以定义变量的原因。

      只有{}才可以分辨作用域么?

      当然不是

      while (1)

        int i;

      printf("hellow world!");

      int i;自己单独在一个作用域中,printf()只会执行一次,当然这是一个死循环,不会执行到printf()。现在你有点略懂了么?跟while()相同的还有for(),if()

      还有个问题你想过么?第一次循环定义一个 i,第二次循环又定义一个 i,这不重复定义了么?

      当然不是,作用域内的变量是有生命周期的。也就是说,当第一次循环结束,你所定义的 i 的内存空间已经被释放。

      

      7、伤脑筋的bug

      你写过这样的程序么?

      int i;

      while (1)

      {

        int i = 0;

        printf("%d", i);

        int j;

      }

      编译器是这样报错的::c浅谈c.c(49) : error C2143: syntax error : missing ';' before 'type'

      我英文不好,不理解这句话是什么意思,但是这段代码里根本不存在缺少;的情况。

      我将代码粘到vs2010中,它是可以运行的。我一度怀疑是编译器出错了。但事实上编译器是很少出错的,纠结了很久,我突然想起来,c语言中规定,变量是必须在作用域的开始定义。vs210中,我用文件是后缀是.cpp的,就是是说c++允许在其他函数之后定义变量,而c对此则表示不支持。

      你写的代码有没有之前运行正常,你只是修改了一点,语法上别没有出错。但编译器却报错了呢?

      LINK : fatal error LNK1168: cannot open Debug/SMIS.exe for writing

      之前遇见这个问题也是纠结很久,为什么一会儿可以编译,一会儿又让我等待呢?

      这也不是编译器出错了。而是你执行了程序,没有关闭,对代码进行了修改,又要执行程序。

      编译器如果会说话:“小子,你拿我开心呢吧,还能不能一起玩耍了。”

      当你malloc时,要记得free啊,还有malloc之后跟上一个判断是有必要的。

      p_head = (P_STUDENT)malloc(sizeof(STUDENT));
         if (NULL == p_head)
         {
             printf("动态内存分配失败,程序结束!");
             exit(-1);
         }

      如果没有if()判断的话,在内存分配失败时,运行程序会出错,而且很难找到错误原因。

      8、字符串,数组,指针

      谈到字符串、数组、指针,那么我们就不得不谈内存了。我们可以把内存理解成一排很长很长的房子,它是一个线性结构。房子里住着不同的人家,每个房子有不同编号。

    人家对应的是计算机里定义的变量,房子编号对应的是变量所在地址。所以,我们每定义一个变量,都会占用一套房子,当房子被全部占用时,你的电脑就崩溃了。当然,我们电脑的内存还是很大的。下面来看看字符串、数组、指针之间的关系。

      字符串以“/0”结束,所以字符串长度要在实际长度上+1

      数组名就是数组首元素的地址

      char ch[10] = "asdfg";  //ch[10]是定长数组

      char ch[] = "asdfg";  //ch[]没有规定长度,但是在定义时必须初始化

      char *p_ch = "asdfg";  //指针指向字符串的首地址

      这三种定义字符串的方法差不多。ch和p_ch都是地址。

      数组定义之后不可改变,指针还可以指向其他地址,但要注意内存泄露。

      

      9、指针与地址

      有些书上说指针就是地址,其实这种说法并不准确。

      那么指针是什么,地址又是什么?

      首先地址就是地址,物理内存的编号。

      指针是一个变量,这个变量存放的内容是内存的物理地址。就像int i; i 是一个变量,存放一个 i 值。指针变量也存放一个指针值,这个值是一块物理内存的地址。

      其实指针是地址还是变量并不影响我们对指针的使用。

      10、你用过宏么?

      #define MAX 60

      就是MAX = 60;的意思,之后的代码中MAX都等于60。宏使我们的程序维护更简单,也更容易理解。

      11、malloc与free

      有人说new跟delete更强大,但我并这么认为,new和delete是c++中的运算符,提供了对对象的操作。而malloc和free是c语言中用来进行动态内存分配的,不具备可比性。
    C语言是面向过程的程序设计,根本没有对象的概念。

  • 相关阅读:
    Promise对象
    iterator和for of 循环
    vue项目基本流程
    BASH_SOURCE 用法
    Java GC CMS 日志分析
    zookeeper 删除snapshot和transaction log的源码解读
    openresty nginx 安装过程记录
    opentesty--luasocket 安装
    为什么要使用SLF4J而不是Log4J
    要过一遍的博客列表
  • 原文地址:https://www.cnblogs.com/ITgaozy/p/4395987.html
Copyright © 2011-2022 走看看