精华内容
下载资源
问答
  •  1.C语言中常见的注释掉代码的方法,大家熟悉的可能就是“//”注释掉行,“/* */”注释掉段,其实还有一种方法,采用预编译的方法。  举个例子:  #include  int main()  {    printf("hello word!\n...

      今天突然想起来两个小问题,所以拿来与大家一起分享。

      1.C语言中常见的注释掉代码的方法,大家熟悉的可能就是“//”注释掉行,“/*  */”注释掉段,其实还有一种方法,采用预编译的方法。

      举个例子:

            #include <stdio.h>

           int main()
           {
     

               printf("hello word!\n");
              #if 0
             printf("hello word!\n");
             printf("hello word!\n");
             printf("hello word!\n");
              #endif
             return 0;
           }

       结果如下:

         

     

      2.常见的制造死循环的方法有:

         while(1);

         for(;;);

         有一点注意,为啥不能用goto语句?因为goto,则可以从语句的内部随意地跳出,具有程序的破坏性。

     

    希望大家可以补充
    展开全文
  • C语言循环小总结

    2017-11-06 17:15:10
    但是在运用for语句的时候,有时会出现一些难以预料的情况,比如死循环,又比如输出的结果并不是预期的。这时候,我们就需要看清楚在{}中的一些语句是否出现了循环的副作用。举个很简单的例子吧。  当你把continue...
    每个do while语句都可以用一个for语句来替代。并且可以让初始条件,判断语句以及循环语句放在同一个语句中,十分的方便,也可以有效的减少代码量。但是在运用for语句的时候,有时会出现一些难以预料的情况,比如死循环,又比如输出的结果并不是预期的。这时候,我们就需要看清楚在{}中的一些语句是否出现了循环的副作用。举个很简单的例子吧。

        当你把continue和break用的不是很恰当的时候,就会出现很可怕的错误,熟悉循环语句的程序猿应该很清楚break语句会提前终止循环,跳出这个循环体,而continue语句只会终止本次循环,而不会跳出循环体。再看一段代码,我就用C语言的代码来表示好了,意思是一样的。
    问题是要输出一个4*5的矩阵,要求是第三列的第一个数字不输出,分别用break和continue来终止,会得到不一样的结果。

    for(i=1;i<=4;i++)                    
    for(i=1;i<=4;i++)                     结果如下
    for(i=1;j<=5;j++)                   1   2   3   4    5
    {                                   2   4   6   8    10
       if(n%5==0)printf("\n");         
    if(i==3&&j==1)break;                4   8   12  16   20
    printf("%d\t",i*j);
    }
    printf("\n");
    
    
    for(i=1;j<=5;j++)                  结果如下
    {                                   1    2    3    4     5
       if(n%5==0)printf("\n");          2    4    6    8     10
    if(i==3&&j==1)continue;             6    9    12   15
    printf("%d\t",i*j);                 4    8    12   16    20
    }
    printf("\n");
    出现这两种不同结果的原因很简单,当执行到if(i==3&&j==1)时,循环直接终止,所以就算j=2,3,4,5也不会执行了,所以那一行的数字都没有了,而在continue代码段中,只会把两个条件同时满足的情况终止,而其他的还会依次执行下去,所以在第三行只会缺少一个数字。
       除了for语句,if else 语句也会出现很多类似的问题。if else语句嵌套的过程中,很容易将对应的ifelse搞混,从而导致程序出现难以料到的结果,这时候,一般要加上{},把每条代码分隔开来,这样才可以清楚地看到内部的结构。
    这里仍旧放上一段代码,供你们参考一下
    if(i<5)
                 printf("hello");
                if(j<4)
                   printf("world");
                  else
                   printf("let go");
     为了方便,我只是举了一个很简单的例子。就在这段代码里面,出现了两个if一个else,所以说,这一个else应该和前一个if匹配还是和后一个匹配呢,按理说,应该和前一个匹配,但是,基于if else的匹配原则,if应该和最近的else匹配,所以是第二个if,为了避免歧义,建议在每一段if else语句中都加上一对大括号,这样,就算嵌套的内容再多,代码也依然可以一目了然。  
    合理的运用for循环和数组的结合,可以处理大量的数据并且可以提高程序的执行效率,但是在运用数组的时候,应该记得一些基本的方法,这样才可以趋利避害,让程序可以更顺利地跑起来。关于数组,又可以用指针的替换来进一步进行优化,但是相对来说,指针不适合新手,比如我,因为指针是一个复杂的东西,它可以分为好几级指针,并且可以定义为常量指针和变量指针,实在灵活多变,应该在充分熟悉数组以及元素存储原理的基础上来调用指针。
    

    
    
    
    

    展开全文
  • 死循环代码例子如下 (代码目的为得到文本中的行数、词数、字符个数) #include &lt;stdio.h&gt; #include &lt;windows.h&gt; int main(void) { int c,nl,nw,nc,inword; nl=nw=nc=inword=0; while...

    死循环代码例子如下

    (代码目的为得到文本中的行数、词数、字符个数)

    #include <stdio.h>
    #include <windows.h>
    int main(void)
    {
    	int c,nl,nw,nc,inword;
    	nl=nw=nc=inword=0;
    	while((c=getchar())!=EOF)
    	{
    		nc++;
    		if(c=='\n') nl++;
    		if(c==' '||c=='\t'||c=='\n')
    			inword=0;
    		else if(inword==0)
    		{
    			inword = 1;
    			nw++;
    		}
    
    	}
    	printf("nl=%d,nw=%d,nc=%d\n",nl,nw,nc);
    	system("pause");
    
    	return 0; 
    }
    

    首先打印EOF(我们得先知道EOF是个what ghost??)
    代码如下:

    #include <stdio.h>
    int main(void)
    {
    	printf("%d",EOF);
    
    	return 0; 
    }
    

    输出结果为-1
    我们发现这个没办法通过键入哪个键来表示的,因为ASCII码没有对应-1的键


    所以到底怎么解决呢??

    在windows中,常用ctrl+z来标识流的结束(据说专业的叫法是阻塞式检查ctrl+Z

    注意:Ctrl+Z产生的不是一个普通的ASCII码值,也就是说它产生的不是一个字符,所以不会跟其它从键盘上输入的字符一样能够存放在输入缓冲区

    从键盘上输入:一段文字+^z + 回车 之后,在Windows系统上是这样处理的:由于回车的作用,前面的文段被送到输入缓冲区(注意:^z不会产生字符,所以更不会存储到输入缓冲区,缓冲区中没有 ^z 的存在)。这时,getchar() 检测到输入缓冲区中已经有数据存在(因此不再检查是否有^z 的输入),于是从缓冲中读取相应的数据。如果都读取完了,则输入缓冲区重新变为空,getchar() 等待新的输入。可见,尽管有 ^z 按下,但是由于在此之前还有其它的字符(也就是说,不算是新起的一行),所以流也不会结束。
    因此,输入流结束的条件就是:^z 之前不能有任何字符输入(回车除外) 换句话说,就是要回车(新起一行,打入ctrl+Z)(回显为^Z,否则 ^z 起不到流结束的作用。


    网上收集知识点拓展(忘记从哪里找到的资料了)

    关于Ctrl+Z、Ctrl+D

    • Windows系统中一般采用阻塞式检查 Ctrl+Z、Unix/Linux系统下一般采用非阻塞式的检查 Ctrl+D。因为是在Windows系统下,因此使用阻塞式的 Ctrl+Z 来标识流的结束。
      阻塞式检查方式指的是只有在回车键按下之后才对此前是否有 Ctrl+Z 组合键按下进行检查,非阻塞式样指的是按下Ctrl+D
      之后立即响应的方式。如果在按 Ctrl+D 之前已经从键盘输入了字符,则 Ctrl+D的作用就相当于回车,即把这些字符送到输入缓冲区供读取使用,此时Ctrl+D不再起流结束符的作用。如果按Ctrl+D
      之前没有任何键盘输入,则 Ctrl+D 就是流结束的信号。
    展开全文
  • C语言关键字

    千次阅读 2012-09-16 16:31:01
    C语言do、while、for关键字—循环 ...while(1)表示死循环死循环有没有用呢? 看下面例子:比如你开发一个系统要日夜不停的运行,但是只有操作员输入某个特定的字符‘#’才可以停下来。 while(1

    C语言do、while、for关键字—循环

    C 语言中循环语句有三种:while 循环、do-while 循环、for 循环。

    while 循环:先判断while 后面括号里的值,如果为真则执行其后面的代码;否则不执行。while(1)表示死循环。死循环有没有用呢?

    看下面例子:比如你开发一个系统要日夜不停的运行,但是只有操作员输入某个特定的字符‘#’才可以停下来。
    while(1)
    {
       if(‘#’== GetInputChar())
       {
          break;
       }
    }

    一、break 与continue 的区别

    break 关键字很重要,表示终止本层循环。现在这个例子只有一层循环,当代码执行到break 时,循环便终止。

    如果把break 换成continue 会是什么样子呢?continue 表示终止本次(本轮)循环。当代码执行到continue 时,本轮循环终止,进入下一轮循环。

    while(1)也有写成while(true) 或者while(1==1) 或者while((bool) 1)等形式的,效果一样。

    do-while 循环:先执行do 后面的代码,然后再判断while 后面括号里的值,如果为真,循环开始;否则,循环不开始。其用法与while 循环没有区别,但相对较少用。

    for 循环:for 循环可以很容易的控制循环次数,多用于事先知道循环次数的情况下。

    留一个问题:在switch case 语句中能否使用continue 关键字?为什么?

    二、循环语句的注意点

    1、在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少CPU 跨切循环层的次数。例如:
    2、建议for 语句的循环控制变量的取值采用“半开半闭区间”写法。
    半开半闭区间写法和闭区间写法虽然功能是相同,但相比之下,半开半闭区间写法写法更加直观。
    3、不能在for 循环体内修改循环变量,防止循环失控。
    4、循环要尽可能的短,要使代码清晰,一目了然。
    如果你写的一个循环的代码超过一显示屏,那会让读代码的人发狂的。解决的办法由两个:第一,重新设计这个循环,确认是否这些操作都必须放在这个循环里;第二,将这些代码改写成一个子函数,循环中只调用这个子函数即可。一般来说循环内的代码不要超过20行。

    5、把循环嵌套控制在3 层以内。

    国外有研究数据表明,当循环嵌套超过3 层,程序员对循环的理解能力会极大的降低。如果你的循环嵌套超过3 层,建议你重新设计循环或是将循环内的代码改写成一个字函数。


    C语言switch、case完美组合

    既然有了if、else 组合为什么还需要switch、case 组合呢?

    一、不要拿青龙偃月刀去削苹果

    那你既然有了菜刀为什么还需要水果刀呢?你总不能扛着云长的青龙偃月刀(又名冷艳锯)去削苹果吧。如果你真能做到,关二爷也会佩服你的。^_^。

    if、else 一般表示两个分支或是嵌套表示少量的分支,但如果分支很多的话……还是用switch、case 组合吧。其基本格式为:
    switch(variable)
    {
       case Value1:
          //program code
          break;
       case Value2:
          //program code
          break;
       case Value3:
          //program code
          break;
       …
       default:
          break;
    }

    很简单,但有两个规则:
    1. 每个case 语句的结尾绝对不要忘了加break,否则将导致多个分支重叠(除非有意使多个分支重叠)。
    2. 最后必须使用default 分支。即使程序真的不需要default 处理,也应该保留语句:
         default :
         break;
      这样做并非画蛇添足,可以避免让人误以为你忘了default 处理。

    二、case 关键字后面的值有什么要求吗?

    好,再问问:真的就这么简单吗?看看下面的问题:
    Value1 的值为0.1 行吗?-0.1 呢?-1 呢?0.1+0.9 呢? 1+2 呢?3/2 呢?‘A’呢?“A”呢?变量i(假设i 已经被初始化)呢?NULL 呢?等等。这些情形希望你亲自上机调试一下,看看到底哪些行,哪些不行。

    记住:case 后面只能是整型或字符型的常量或常量表达式(想想字符型数据在内存里是怎么存的)。

    三、case 语句的排列顺序

    似乎从来没有人考虑过这个问题,也有很多人认为case 语句的顺序无所谓。但事实却不是如此。如果case 语句很少,你也许可以忽略这点,但是如果case 语句非常多,那就不得不好好考虑这个问题了。比如你写的是某个驱动程序,也许会经常遇到几十个case 语句的情况。一般来说,我们可以遵循下面的规则:

    1、按字母或数字顺序排列各条case 语句。
    如果所有的case 语句没有明显的重要性差别,那就按A-B-C 或1-2-3 等顺序排列case语句。这样做的话,你可以很容易的找到某条case 语句。比如:
    switch(variable)
    {
       case A:
          //program code
          break;
       case B:
          //program code
          break;
       case C:
          //program code
          break;
       …
       default:
       break;
    }
    2、把正常情况放在前面,而把异常情况放在后面。
    如果有多个正常情况和异常情况,把正常情况放在前面,并做好注释;把异常情况放在后面,同样要做注释。比如:
    switch(variable)
    {
       ///
       //正常情况开始
       case A:
          //program code
          break;
       case B:
          //program code
          break;
       //正常情况结束
       /
       //异常情况开始
       case -1:
          //program code
          break;
       //异常情况结束
       //
       …
       default:
          break;
    }
    3、按执行频率排列case 语句:把最常执行的情况放在前面,而把最不常执行的情况放在后面。
    最常执行的代码可能也是调试的时候要单步执行的最多的代码。如果放在后面的话,找起来可能会比较困难,而放在前面的话,可以很快的找到。

    三、使用case 语句的其他注意事项

    1、简化每种情况对应的操作。
    使得与每种情况相关的代码尽可能的精炼。case 语句后面的代码越精炼,case 语句的结果就会越清晰。你想想,如果case 语句后面的代码整个屏幕都放不下,这样的代码谁也难看得很清晰吧。如果某个case 语句确实需要这么多的代码来执行某个操作,那可以把这些操作写成一个或几个子程序,然后在case 语句后面调用这些子程序就ok 了。一般来说case语句后面的代码尽量不要超过20 行。

    2、不要为了使用case 语句而刻意制造一个变量。
    case 语句应该用于处理简单的,容易分类的数据。如果你的数据并不简单,那可能使用ifelseif 的组合更好一些。为了使用case 而刻意构造出来的变量很容易把人搞糊涂,应该避免这种变量。比如:
    char action = a[0];
    switch (action)
    {
       case ‘c’:
          fun1();
          break;
       case ‘d’:
          …
          break;
       default:
          break;
    }
    这里控制case 语句的变量是action。而action 的值是取字符数组a 的一个字符。但是这种方式可能带来一些隐含的错误。一般而言,当你为了使用case 语句而刻意去造出一个变量时,真正的数据可能不会按照你所希望的方式映射到case 语句里。在这个例子中,如果用户输入字符数组a 里面存的是“const”这个字符串,那么case 语句会匹配到第一个case上,并调用fun1()函数。然而如果这个数组里存的是别的以字符c 开头的任何字符串(比如:“col”,“can”),case 分支同样会匹配到第一个case 上。但是这也许并不是你想要的结果,这个隐含的错误往往使人抓狂。如果这样的话还不如使用if-else if 组合。比如:
    if(0 == strcmp(“const”,a))
    {
       fun1();
    }
    else if
    {
       …
    }
    三、把default 子句只用于检查真正的默认情况。
    有时候,你只剩下了最后一种情况需要处理,于是就决定把这种情况用default 子句来处理。这样也许会让你偷懒少敲几个字符,但是这却很不明智。这样将失去case 语句的标号所提供的自说明功能,而且也丧失了使用default 子句处理错误情况的能力。所以,奉劝你不要偷懒,老老实实的把每一种情况都用case 语句来完成,而把真正的默认情况的处理交给default 子句。



    C语言if、else超级组合

    if 语句很简单吧。嗯,的确很简单。那我们就简单的看下面几个简单的问题:

    一、bool 变量与“零值”进行比较

    bool 变量与“零值”进行比较的if 语句怎么写?bool bTestFlag = FALSE;//想想为什么一般初始化为FALSE 比较好?
    A), if(bTestFlag == 0); if(bTestFlag == 1);
    B), if(bTestFlag == TRUE); if(bTestFlag == FLASE);
    C), if(bTestFlag); if(!bTestFlag);

    哪一组或是那些组正确呢?我们来分析分析:
    A)写法:bTestFlag 是什么?整型变量?如果要不是这个名字遵照了前面的命名规范,肯怕很容易让人误会成整型变量。所以这种写法不好。
    B)写法:FLASE 的值大家都知道,在编译器里被定义为0;但TRUE 的值呢?都是1吗?很不幸,不都是1。Visual C++定义为1,而它的同胞兄弟Visual Basic 就把TRUE 定义为-1.那很显然,这种写法也不好。

    大家都知道if 语句是靠其后面的括号里的表达式的值来进行分支跳转的。表达式如果为真,则执行if 语句后面紧跟的代码;否则不执行。那显然,本组的写法很好,既不会引起误会,也不会由于TRUE 或FLASE 的不同定义值而出错。记住:以后写代码就得这样写。

    二、float 变量与“零值”进行比较

    float 变量与“零值”进行比较的if 语句怎么写?
       float fTestVal = 0.0;
    A), if(fTestVal == 0.0); if(fTestVal != 0.0);
    B), if((fTestVal >= -EPSINON) && (fTestVal <= EPSINON)); //EPSINON 为定义好的精度。

    哪一组或是那些组正确呢?我们来分析分析:
    float 和double 类型的数据都是有精度限制的,这样直接拿来与0.0 比,能正确吗?明显不能,看例子:  的值四舍五入精确到小数点后10位为:3.1415926536,你拿它减去0.00000000001 然后再四舍五入得到的结果是多少?你能说前后两个值一样吗?

    EPSINON 为定义好的精度,如果一个数落在[0.0-EPSINON,0.0+EPSINON] 这个闭区间内,我们认为在某个精度内它的值与零值相等;否则不相等。扩展一下,把0.0 替换为你想比较的任何一个浮点数,那我们就可以比较任意两个浮点数的大小了,当然是在某个精度内。

    同样的也不要在很大的浮点数和很小的浮点数之间进行运算,比如:
       10000000000.00 + 0.00000000001
    这样计算后的结果可能会让你大吃一惊。

    三、指针变量与“零值”进行比较

    指针变量与“零值”进行比较的if 语句怎么写?
       int* p = NULL;//定义指针一定要同时初始化,指针与数组那章会详细讲解。
    A), if(p == 0); if(p != 0);
    B), if(p); if(!p);
    C) , if(NULL == p); if(NULL != p);

    哪一组或是那些组正确呢?我们来分析分析:
    A)写法:p 是整型变量?容易引起误会,不好。尽管NULL 的值和0 一样,但意义不同。
    B)写法:p 是bool 型变量?容易引起误会,不好。
    C)写法:这个写法才是正确的,但样子比较古怪。为什么要这么写呢?是怕漏写一个“=”号:if(p = NULL),这个表达式编译器当然会认为是正确的,但却不是你要表达的意思。所以,非常推荐这种写法。

    四、else 到底与哪个if 配对呢?

    else 常常与if 语句配对,但要注意书写规范,看下面例子:
    if(0 == x)
    if(0 == y) error();
    else
    {
       //program code
    }
    这个else 到底与谁匹配呢?让人迷糊,尤其是初学者。还好,C 语言有这样的规定:else始终与同一括号内最近的未匹配的if 语句结合。虽然老手可以区分出来,但这样的代码谁都会头疼的,任何时候都别偷这种懒。关于程序中的分界符‘{’和‘}’,建议如下:

    提示:程序中的分界符‘{’和‘}’对齐风格如下:
    注意下表中代码的缩进一般为4 个字符,但不要使用Tab 键,因为不同的编辑器Tab 键定义的空格数量不一样,别的编辑器打开Tab 键缩进的代码可能会一片混乱。

    五、if 语句后面的分号

    关于if-else 语句还有一个容易出错的地方就是与空语句的连用。看下面的例子:
       if(NULL != p) ;
       fun();
    这里的fun()函数并不是在NULL != p 的时候被调用,而是任何时候都会被调用。问题就出在if 语句后面的分号上。在C 语言中,分号预示着一条语句的结尾,但是并不是每条C 语言语句都需要分号作为结束标志。if 语句的后面并不需要分号,但如果你不小心写了个分号,编译器并不会提示出错。因为编译器会把这个分号解析成一条空语句。也就是上面的代码实际等效于:
    if(NULL != p)
    {
       ;
    }
    fun();
    这是初学者很容易犯的错误,往往不小心多写了个分号,导致结果与预想的相差很远。所以建议在真正需要用空语句时写成这样:
       NULL;
    而不是单用一个分号。这就好比汇编语言里面的空指令,比如ARM 指令中的NOP 指令。这样做可以明显的区分真正必须的空语句和不小心多写的分号。

    六、使用if 语句的其他注意事项

    1、先处理正常情况,再处理异常情况。
    在编写代码是,要使得正常情况的执行代码清晰,确认那些不常发生的异常情况处理代码不会遮掩正常的执行路径。这样对于代码的可读性和性能都很重要。因为,if 语句总是需要做判断,而正常情况一般比异常情况发生的概率更大(否则就应该把异常正常调过来了),如果把执行概率更大的代码放到后面,也就意味着if 语句将进行多次无谓的比较。另外,非常重要的一点是,把正常情况的处理放在if 后面,而不要放在else 后面。当然这也符合把正常情况的处理放在前面的要求。

    2、确保if 和else 子句没有弄反。
    这一点初学者也容易弄错,往往把本应该放在if 语句后面的代码和本应该放在else 语句后面的代码弄反了。

    C语言ned、unsigned关键字

    我们知道计算机底层只认识0、1.任何数据到了底层都会变计算转换成0、1.那负数怎么存储呢?

    肯定这个“-”号是无法存入内存的,怎么办?很好办,做个标记。把基本数据类型的最高位腾出来,用来存符号,同时约定如下:最高位如果是1,表明这个数是负数,其值为除最高位以外的剩余位的值添上这个“-”号;如果最高位是0,表明这个数是正数,其值为除最高位以外的剩余位的值。

    这样的话,一个32位的signed int类型整数其值表示法范围为:- 231~231 -1;8 位的char类型数其值表示的范围为- 27~27 -1。一个32位的unsigned int类型整数其值表示法范围为:0~ 232 -1;8位的char类型数其值表示的范围为0~28 -1。同样我们的signed 关键字也很宽恒大量,你也可以完全当它不存在,编译器缺省默认情况下数据为signed 类型的。

    上面的解释很容易理解,下面就考虑一下这个问题:
    int main()
    {
       char a[1000];
       int i;
       for(i=0; i<1000; i++)
       {
          a[i] = -1-i;
       }
       printf("%d",strlen(a));
       return 0;
    }
    此题看上去真的很简单,但是却鲜有人答对。答案是255。别惊讶,我们先分析分析。

    for 循环内,当i 的值为0 时,a[0]的值为-1。关键就是-1 在内存里面如何存储。

    我们知道在计算机系统中,数值一律用补码来表示(存储)。主要原因是使用补码,可以将符号位和其它位统一处理;同时,减法也可按加法来处理。另外,两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃。正数的补码与其原码一致;负数的补码:符号位为1,其余位为该数绝对值的原码按位取反,然后整个数加1。

    按照负数补码的规则,可以知道-1 的补码为0xff,-2 的补码为0xfe……当i 的值为127时,a[127]的值为-128,而-128 是char 类型数据能表示的最小的负数。当i 继续增加,a[128]的值肯定不能是-129。因为这时候发生了溢出,-129 需要9 位才能存储下来,而char 类型数据只有8 位,所以最高位被丢弃。剩下的8 位是原来9 位补码的低8 位的值,即0x7f。

    当i 继续增加到255 的时候,-256 的补码的低8 位为0。然后当i 增加到256 时,-257 的补码的低8 位全为1,即低八位的补码为0xff,如此又开始一轮新的循环……

    按照上面的分析,a[0]到a[254]里面的值都不为0,而a[255]的值为0。strlen 函数是计算字符串长度的,并不包含字符串最后的‘\0’。而判断一个字符串是否结束的标志就是看是否遇到‘\0’。如果遇到‘\0’,则认为本字符串结束。

    分析到这里,strlen(a)的值为255 应该完全能理解了。这个问题的关键就是要明白char类型默认情况下是有符号的,其表示的值的范围为[-128,127],超出这个范围的值会产生溢出。另外还要清楚的就是负数的补码怎么表示。弄明白了这两点,这个问题其实就很简单了。

    留三个问题:
    1)按照我们上面的解释,那-0 和+0 在内存里面分别怎么存储?

    2)
    int i = -20;
    unsigned j = 10;
    i+j
    的值为多少?为什么?

    3) 
    下面的代码有什么问题?
    unsigned i ;
    for (i=9;i>=0;i--)
    {
       printf("%u\n",i);
    }

    C语言sizeof关键字—被冤枉的关键字

    一、常年被人误认为函数。

    sizeof 是关键字不是函数,其实就算不知道它是否为32 个关键字之一时,我们也可以借助编译器确定它的身份。看下面的例子:
       int i=0;
    A),sizeof(int); B),sizeof(i); C),sizeof int; D),sizeof i;

    毫无疑问,32 位系统下A),B)的值为4。那C)的呢?D)的呢?在32 位系统下,通过Visual C++6.0 或任意一编译器调试,我们发现D)的结果也为4。

    咦?sizeof 后面的括号呢?没有括号居然也行,那想想,函数名后面没有括号行吗?由此轻易得出sizeof 绝非函数。

    好,再看C)。编译器怎么怎么提示出错呢?不是说sizeof 是个关键字,其后面的括号可以没有么?那你想想sizeof int 表示什么啊?int 前面加一个关键字?类型扩展?明显不正确,我们可以在int 前加unsigned,const 等关键字但不能加sizeof。好,记住:sizeof 在计算变量所占空间大小时,括号可以省略,而计算类型(模子)大小时不能省略。一般情况下,咱也别偷这个懒,乖乖的写上括号,继续装作一个“函数”,做一个“披着函数皮的关键字”。

    做我的关键字,让人家认为是函数去吧。

    二、sizeof(int)*p 表示什么意思?

    留几个问题(讲解指针与数组时会详细讲解),32 位系统下:
       int *p = NULL;
    sizeof(p)的值是多少?
    sizeof(*p)呢?

       int a[100];
    sizeof (a) 的值是多少?
    sizeof(a[100])呢?//请尤其注意本例。
    sizeof(&a)呢?
    sizeof(&a[0])呢?

    int b[100];
    void fun(int b[100])
    {
       sizeof(b);// sizeof (b) 的值是多少?
    }

    c语言基本数据类型short、int、long、char、float、double

    C 语言包含的数据类型如下图所示:
    一、数据类型与“模子”
    short、int、long、char、float、double 这六个关键字代表C 语言里的六种基本数据类型。

    怎么去理解它们呢? 举个例子:见过藕煤球的那个东西吧?(没见过?煤球总见过吧)。那个东西叫藕煤器,拿着它在和好的煤堆里这么一咔,一个煤球出来了。半径12cm,12 个孔。不同型号的藕煤器咔出来的煤球大小不一样,孔数也不一样。这个藕煤器其实就是个模子。

    现在我们联想一下,short、int、long、char、float、double 这六个东东是不是很像不同类型的藕煤器啊?拿着它们在内存上咔咔咔,不同大小的内存就分配好了,当然别忘了给它们取个好听的名字。

    在32 位的系统上short 咔出来的内存大小是2 个byte;
    int 咔出来的内存大小是4 个byte;
    long 咔出来的内存大小是4 个byte;
    float 咔出来的内存大小是4 个byte;
    double 咔出来的内存大小是8 个byte;
    char 咔出来的内存大小是1 个byte。
    (注意这里指一般情况,可能不同的平台还会有所不同,具体平台可以用sizeof 关键字测试一下)


    很简单吧?咔咔咔很爽吧?是很简单,也确实很爽,但问题就是你咔出来这么多内存块,你总不能给他取名字叫做x1,x2,x3,x4,x5…或者长江1 号,长江2 号…吧。它们长得这么像(不是你家的老大,老二,老三…),过一阵子你就会忘了到底哪个名字和哪个内存块匹配了(到底谁嫁给谁了啊?^_^)。所以呢,给他们取一个好的名字绝对重要。下面我们就来研究研究取什么样的名字好。

    二、变量的命名规则
    1、命名应当直观且可以拼读,可望文知意,便于记忆和阅读。
    标识符最好采用英文单词或其组合,不允许使用拼音。程序中的英文单词一般不要太复杂,用词应当准确。

    2、命名的长度应当符合“min-length && max-information”原则。
    C 是一种简洁的语言, 命名也应该是简洁的。例如变量名MaxVal 就比MaxValueUntilOverflow 好用。标识符的长度一般不要过长,较长的单词可通过去掉“元音”形成缩写。

    另外,英文词尽量不缩写,特别是非常用专业名词,如果有缩写,在同一系统中对同一单词必须使用相同的表示法,并且注明其意思。

    3、当标识符由多个词组成时,每个词的第一个字母大写,其余全部小写。比如:
       int CurrentVal;
    这样的名字看起来比较清晰,远比一长串字符好得多。

    4、尽量避免名字中出现数字编号,如Value1,Value2 等,除非逻辑上的确需要编号。比如驱动开发时为管脚命名,非编号名字反而不好。
    初学者总是喜欢用带编号的变量名或函数名,这样子看上去很简单方便,但其实是一颗颗定时炸弹。这个习惯初学者一定要改过来。

    5、对在多个文件之间共同使用的全局变量或函数要加范围限定符(建议使用模块名(缩写)作为范围限定符)。
    (GUI_ ,etc)标识符的命名规则:

    6、标识符名分为两部分:规范标识符前缀(后缀) + 含义标识。非全局变量可以不用使用范围限定符前缀。
    7、作用域前缀命名规则。
    8、数据类型前缀命名规则。
    9、含义标识命名规则,变量命名使用名词性词组,函数命名使用动词性词组。例如:
    变量含义标识符构成:目标词+ 动词(的过去分词)+ [状语] + [目的地];
    函数含义标识符构成:动词(一般现时)+目标词+[状语]+[目的地];

    10、程序中不得出现仅靠大小写区分的相似的标识符。例如:
       int x, X; 变量x 与X 容易混淆
       void foo(int x); 函数foo 与FOO 容易混淆
       void FOO(float x);
    这里还有一个要特别注意的就是1(数字1)和l(小写字母l)之间,0(数字0)和o(小写字母o)之间的区别。这两对真是很难区分的,我曾经的一个同事就被这个问题折腾了一次。

    11、一个函数名禁止被用于其它之处。例如:
    #include "c_standards.h"
    void foo(int p_1)
    {
       int x = p_1;
    }
    void static_p(void)
    {
       int foo = 1u;
    }

    12、所有宏定义、枚举常数、只读变量全用大写字母命名,用下划线分割单词。例如:
    const int MAX_LENGTH = 100; //这不是常量,而是一个只读变量,具体请往后看
       #define FILE_PATH “/usr/tmp”

    13、考虑到习惯性问题,局部变量中可采用通用的命名方式,仅限于n、i、j 等作为循环变量使用。
    一定不要写出如下这样的代码:
       int p;
       char i;
       int c;
       char * a;
    一般来说习惯上用n,m,i,j,k 等表示int 类型的变量;c,ch 等表示字符类型变量;a 等表示数组;p 等表示指针。当然这仅仅是一般习惯,除了i,j,k 等可以用来表示循环变量外,别的字符变量名尽量不要使用。

    14、定义变量的同时千万千万别忘了初始化。定义变量时编译器并不一定清空了这块内存,它的值可能是无效的数据。这个问题在内存管理那章有非常详细的讨论,请参看。

    15、不同类型数据之间的运算要注意精度扩展问题,一般低精度数据将向高精度数据扩展。



    C语static关键字—言最名不符实的关键字

    不要误以为关键字static 很安静,其实它一点也不安静。这个关键字在C 语言里主要有两个作用,C++对它进行了扩展。

    一、修饰变量

    第一个作用:修饰变量。变量又分为局部和全局变量,但它们都存在内存的静态区。
    静态全局变量,作用域仅限于变量被定义的文件中,其他文件即使用extern 声明也没法使用他。准确地说作用域是从定义之处开始,到文件结尾处结束,在定义之处前面的那些代码行也不能使用它。想要使用就得在前面再加extern ***。恶心吧?要想不恶心,很简单,直接在文件顶端定义不就得了。

    静态局部变量,在函数体里面定义的,就只能在这个函数里用了,同一个文档中的其他函数也用不了。由于被static 修饰的变量总是存在内存的静态区,所以即使这个函数运行结束,这个静态变量的值还是不会被销毁,函数下次使用时仍然能用到这个值。
    static int j;
    void fun1(void)
    {
       static int i = 0;
       i ++;
    }
    void fun2(void)
    {
       j = 0;
       j++;
    }
    intmain()
    {
       for(k=0; k<10; k++)
       {
          fun1();
          fun2();
       }
       return 0;
    }
    i 和j 的值分别是什么,为什么?

    二、修饰函数

    第二个作用:修饰函数。函数前加static 使得函数成为静态函数。
    但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件(所以又称内部函数)。使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名。

    关键字static 有着不寻常的历史。起初,在C 中引入关键字static 是为了表示退出一个块后仍然存在的局部变量。随后,static 在C 中有了第二种含义:用来表示不能被其它文件访问的全局变量和函数。为了避免引入新的关键字,所以仍使用static 关键字来表示这第二种含义。

    当然,C++里对static 赋予了第三个作用,这里先不讨论,有兴趣的可以找相关资料研究。


    C语言register关键字—最快的关键字

    register:这个关键字请求编译器尽可能的将变量存在CPU 内部寄存器中而不是通过内存寻址访问以提高效率。注意是尽可能,不是绝对。你想想,一个CPU 的寄存器也就那么几个或几十个,你要是定义了很多很多register 变量,它累死也可能不能全部把这些变量放入寄存器吧,轮也可能轮不到你。

    一、皇帝身边的小太监----寄存器

    不知道什么是寄存器?那见过太监没有?没有?其实我也没有。没见过不要紧,见过就麻烦大了。^_^,大家都看过古装戏,那些皇帝们要阅读奏章的时候,大臣总是先将奏章交给皇帝旁边的小太监,小太监呢再交给皇帝同志处理。这个小太监只是个中转站,并无别的功能。

    好,那我们再联想到我们的CPU。CPU 不就是我们的皇帝同志么?大臣就相当于我们的内存,数据从他这拿出来。那小太监就是我们的寄存器了(这里先不考虑CPU 的高速缓存区)。数据从内存里拿出来先放到寄存器,然后CPU 再从寄存器里读取数据来处理,处理完后同样把数据通过寄存器存放到内存里,CPU 不直接和内存打交道。这里要说明的一点是:小太监是主动的从大臣手里接过奏章,然后主动的交给皇帝同志,但寄存器没这么自觉,它从不主动干什么事。一个皇帝可能有好些小太监,那么一个CPU 也可以有很多寄存器,不同型号的CPU 拥有寄存器的数量不一样。

    为啥要这么麻烦啊?速度!就是因为速度。寄存器其实就是一块一块小的存储空间,只不过其存取速度要比内存快得多。进水楼台先得月嘛,它离CPU 很近,CPU 一伸手就拿到数据了,比在那么大的一块内存里去寻找某个地址上的数据是不是快多了?那有人问既然它速度那么快,那我们的内存硬盘都改成寄存器得了呗。我要说的是:你真有钱!

    二、使用register 修饰符的注意点

    虽然寄存器的速度非常快,但是使用register 修饰符也有些限制的:register 变量必须是能被CPU 寄存器所接受的类型。意味着register 变量必须是一个单个的值,并且其长度应小于或等于整型的长度。而且register 变量可能不存放在内存中,所以不能用取址运算符“&”来获取register 变量的地址。


    C语言auto—最宽恒大量的关键字

    auto:它很宽恒大量的,你就当它不存在吧。编译器在默认的缺省情况下,所有变量都是auto 的。


    C语言关键字有哪些

    每次讲关键字之前,我总是问学生:C 语言有多少个关键字?sizeof 怎么用?它是函数吗?有些学生不知道C 语言有多少个关键字,大多数学生往往告诉我sizeof 是函数,因为它后面跟着一对括号。当投影仪把这32 个关键字投到幕布上时,很多学生表情惊讶。有些关键字从来没见过,有的惊讶C 语言关键字竟有32 个之多。更有甚者,说大学老师告诉他们sizeof 是函数,没想到它居然是关键字!由此可想而知,大学的计算机教育是多么失败!

    C 语言标准定义的32 个关键字:
    auto 声明自动变量,缺省时编译器一般默认为auto
    int 声明整型变量
    double 声明双精度变量
    long 声明长整型变量
    char 声明字符型变量
    float 声明浮点型变量
    short 声明短整型变量
    signed 声明有符号类型变量
    unsigned 声明无符号类型变量
    struct 声明结构体变量
    union 声明联合数据类型
    enum 声明枚举类型
    static 声明静态变量
    switch 用于开关语句
    case 开关语句分支
    default 开关语句中的“其他”分支
    break 跳出当前循环
    register 声明寄存器变量
    const 声明只读变量
    volatile 说明变量在程序执行中可被隐含地改变
    typedef 用以给数据类型取别名(当然还有其他作用)
    extern 声明变量是在其他文件正声明(也可以看做是引用变量)
    return 子程序返回语句(可以带参数,也可不带参数)
    void 声明函数无返回值或无参数,声明空类型指针
    continue 结束当前循环,开始下一轮循环
    do 循环语句的循环体
    while 循环语句的循环条件
    if 条件语句
    else 条件语句否定分支(与if 连用)
    for 一种循环语句(可意会不可言传)
    goto 无条件跳转语句
    sizeof 计算对象所占内存空间大小

    下面的篇幅就一一讲解这些关键字。但在讲解之前先明确两个概念:

    什么是定义?什么是声明?它们有何区别?举个例子:
    A)int i;
    B)extern int i;(关于extern,后面解释)

    哪个是定义?哪个是声明?或者都是定义或者都是声明?我所教过的学生几乎没有一人能回答上这个问题。这个十分重要的概念在大学里从来没有被提起过!

    什么是定义:所谓的定义就是(编译器)创建一个对象,为这个对象分配一块内存并给它取上一个名字,这个名字就是我们经常所说的变量名或对象名。但注意,这个名字一旦和这块内存匹配起来(可以想象是这个名字嫁给了这块空间,没有要彩礼啊。^_^),它们就同生共死,终生不离不弃。并且这块内存的位置也不能被改变。一个变量或对象在一定的区域内(比如函数内,全局等)只能被定义一次,如果定义多次,编译器会提示你重复定义同一个变量或对象。

    什么是声明:有两重含义。如下:
    第一重含义:告诉编译器,这个名字已经匹配到一块内存上了(伊人已嫁,吾将何去何从?何以解忧,唯有稀粥),下面的代码用到变量或对象是在别的地方定义的。声明可以出现多次。

    第二重含义:告诉编译器,我这个名字我先预定了,别的地方再也不能用它来作为变量名或对象名。比如你在图书馆自习室的某个座位上放了一本书,表明这个座位已经有人预订,别人再也不允许使用这个座位。其实这个时候你本人并没有坐在这个座位上。这种声明最典型的例子就是函数参数的声明,例如:
       void fun(int i, char c);
    好,这样一解释,我们可以很清楚的判断:A)是定义;B)是声明。那他们的区别也很清晰了。记住,定义声明最重要的区别:定义创建了对象并为这个对象分配了内存,声明没有分配内存(一个抱伊人,一个喝稀粥。^_^)。


    展开全文
  • 很多人在使用scanf的时候都不会去检查它的返回值, 包括我在这之前也没有去注意它, 如果你方便去翻翻c语言的教科书, 估计也很难找到有检查scanf的例子代码段, 至少我还没有见过, 当然, 书上的代码毕竟只是一个练习,...
  • 你必须知道的495个C语言问题

    千次下载 热门讨论 2015-05-08 11:09:25
    1.3 因为C语言没有精确定义类型的大小,所以我一般都用typedef定义int16和int32。然后根据实际的机器环境把它们定义为int、short、long等类型。这样看来,所有的问题都解决了,是吗? 1.4 新的64位机上的64位类型...
  • 难道在C语言中一个结构不能包含指向自己的指针吗? o 2.7 怎样建立和理解非常复杂的声明?例如定义一个包含 N 个指向返回指向字符的指针的函数的指针的数组? o 2.8 函数只定义了一次, 调用了一次, 但编译器提示...
  • 《你必须知道的495个C语言问题》

    热门讨论 2010-03-20 16:41:18
    《你必须知道的495个C语言问题》以问答的形式组织内容,讨论了学习或使用C语言的过程中经常遇到的一些问题。书中列出了C用户经常问的400多个经典问题,涵盖了初始化、数组、指针、字符串、内存分配、库函数、C预...
  •  13.3 死循环 202  13.4 switch语句 205  13.5 退出程序 212  13.5.1 exit( )函数 212  13.6 在程序中执行操作系统命令 213  13.7 总结 214  13.8 问与答 215  13.9 作业 215  13.9.1 小测验 215 ...
  • 1.3 因为C语言没有精确定义类型的大小,所以我一般都用typedef定义int16和int32。然后根据实际的机器环境把它们定义为int、short、long等类型。这样看来,所有的问题都解决了,是吗? 2  1.4 新的64位机上的64位...
  • 17.3.4 死代码删除 275 17.4 加快数据流分析 276 17.4.1 位向量 276 17.4.2 基本块 276 17.4.3 结点排序 277 17.4.4 使用-定值链和定值-使用链 277 17.4.5 工作表算法 278 17.4.6 增量式数据流分析 278 17.5 别名...
  • 19.3.1死代码删除 19.3.2简单的常数传播 19.3.3条件常数传播 19.3.4保持必经结点性质 19.4数组、指针和存储器 19.5控制依赖图 19.6从SSA形式转变回来 19.7函数式中间形式 推荐阅读 习题 第20章 流水和...
  • 现代编译原理C语言描述-虎书中文版

    热门讨论 2010-04-11 16:47:52
    19.3.1 死代码删除 315 19.3.2 简单的常数传播 316 19.3.3 条件常数传播 317 19.3.4 保持必经结点性质 319 19.4 数组、指针和存储器 320 19.5 控制依赖图 321 19.6 从SSA形式转变回来 323 19.7 函数...
  • 你必须知道的495个C语言问题(PDF)

    热门讨论 2009-09-15 10:25:47
    难道在C语言中一个结构不能包含指向自己的指针吗? . . . . 3 1.7 怎样建立和理解非常复杂的声明?例如定义一个包含N 个指向返 回指向字符的指针的函数的指针的数组? . . . . . . . . . . . . . . 3 1.8 函数只定义...
  • C++程序设计语言(特别版)--源代码

    热门讨论 2012-04-23 07:33:51
    已出版多部著作和译著,包括《程序设计语言基础》(译著,1990),《Mathematica数学软件系统的应用与程序设计》(1994),《从问题到程序——程序设计与C语言引论》(1999) [同作者作品] 计算机基础教程(上下)...
  • 已出版多部著作和译著,包括《程序设计语言基础》(译著,1990),《Mathematica数学软件系统的应用与程序设计》(1994),《从问题到程序——程序设计与C语言引论》(1999) [同作者作品] 计算机基础教程(上下)...
  • 死循环(Infinite loops) 数据声明(Data declarations) 位操作(Bit manipulation) 访问固定的内存位置(Accessing fixed memory locations) 中断(Interrupts) 代码例子(Code examples) 13. 评价下面的代码...
  • 死循环。这让我很费解,我用了fflush(stdin)来排除,发现没有效果。后来我想起之前写过关于scanf()的原理详解的文章。 又看了一看才猛然发现了其中的原理。 c语言scanf()函数的原理 问题描述 随便的举个例子: 输入...
  • 所有语言编写的代码最终要运行,都要转化成机器码。在更短的时间内完成相同的事那么效率就高。 关于如何提高C语言程序的执行效率,有如下建议: 1.尽量避免调用延时函数 没有带操作系统的程序只能在while(1)里面...
  • 9.1.4 死代码删除 9.1.5 代码外提 9.1.6 强度削弱和归纳变量删除 9.1.7 优化编译器的组织 9.2 流图中的循环 9.2.1 必经结点 9.2.2 自然循环 9.2.3 前置结点 9.2.4 可归约流图 9.3 全局数据流分析...
  • 编译原理全套

    2011-12-03 11:17:21
    9.1.4 死代码删除 9.1.5 代码外提 9.1.6 强度削弱和归纳变量删除 9.1.7 优化编译器的组织 9.2 流图中的循环 9.2.1 必经结点 9.2.2 自然循环 9.2.3 前置结点 9.2.4 可归约流图 9.3 全局数据流分析介绍 9.3.1...
  • VFP中实现选择排序

    2009-08-08 00:18:01
    不过后来发现自己似乎是走进了胡同:我试图走进VC的世界,可是被MFC、SDK这些东西折磨得痛苦之极;当我为自己开始能用asp做些东西而沾沾自喜的时候,一个强调代码与界面分离的asp.net一天比一天时髦起来,而它的...
  • 水仙花数的vfp实现

    2009-08-08 00:19:47
    不过后来发现自己似乎是走进了胡同:我试图走进VC的世界,可是被MFC、SDK这些东西折磨得痛苦之极;当我为自己开始能用asp做些东西而沾沾自喜的时候,一个强调代码与界面分离的asp.net一天比一天时髦起来,而它的...
  • 在测试时发现程序循环之中,得到的错误记录是"Bus Error"(总线出错),由此可以说明出现了内存操作异常。 经过跟踪变量值发现循环变量i的阀值pSysHead->dbf_count的数值为0xFFFFFFFF,该值是从被破坏的内存...

空空如也

空空如也

1 2
收藏数 35
精华内容 14
关键字:

c语言死循环代码例子

c语言 订阅