C语言总结_1.关键字

C 语言易错点知识总结

1.关键字

  • 变量声明与定义
    定义声明最重要的区别:
     <1 定义创建了对象并为这个计算对象所占内存空间大小对象分配了内存,声明没有分配内存。
     <2 定义只能一次,声明可以多次。
     <3 声明的2个作用:
         a.告诉编译器,这个名字已经匹配到一块内存上,下面的代码用到变量或对象是在别的地方定义的。
         b.告诉编译器,我这个名字我先预定了,别的地方再也不能用它来作为变量名或对象名。
    
    eg:
    函数声明 void fun(int i, char c); 变量声明 extern int i;
    函数定义 void fun(int i, char c){} 变量定义 int i = 0  ;
    
  • 命名规则
    <1 对在多个文件之间共同使用的全局变量或函数要加范围限定符
    <2 所有宏定义、枚举常数、只读变量全用大写字母命名,用下划线分割单词。
    <3 定义变量的同时千万千万别忘了初始化。定义变量时编译器并不一定清空了这块内存,它的值可能是无效的数据。

  • static 关键字
     <1 第一个作用:修饰变量。变量又分为局部和全局变量,但它们都存在内存的静态区。
    静态全局变量,作用域仅限于变量被定义的文件中,其他文件即使用 extern 声明也没法
    使用他。准确地说作用域是从定义之处开始,到文件结尾处结束,在定义之处前面的那些
    代码行也不能使用它。
     静态局部变量,在函数体里面定义的,就只能在这个函数里用了,同一个文档中的其他
    函数也用不了。由于被 static 修饰的变量总是存在内存的静态区,所以即使这个函数运行结
    束,这个静态变量的值还是不会被销毁,函数下次使用时仍然能用到这个值。
     <2 第二个作用:修饰函数。函数前加 static 使得函数成为静态函数。但此处“static”的含义
    不是指存储方式,而是指对函数的作用域仅局限于本文件(所以又称内部函数)。使用内部函
    数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件
    中的函数同名。
     static 在 C 中有了二种含义:

      1.static 是为了表示退出一个块后仍然存在的局部变量。
      2.用来表示不能被其它文件访问的全局变量和函数。
    

    代码1:

    #include< stdio.h>
    static int j;
    void fun1(void)
    {
      static int i = 0;
      i++;
    }
    void fun2(void)
    {
      j = 0;
      j++;
    }
    int main()
    {
      for (int k = 0; k < 10; k++)
      {
          fun1();
          fun2();
      }
      return 0;
    }

解析: i=10, j=1

  1. i 声明在函数内部,且有static,所以它是属于局部静态变量。
    静态变量的特点是:第一次调用会初始化,且会一直保留最后的值,后面每次调用的时候不再重新初始化。所以第一次进来执行 i=0 和 i++,以后每次进来都只执行 i++。但是其他函数不能访问与使用,本函数下次使用仍然能使用该值。
  2. j 属于全局静态变量,文件内皆可以使用,每次进入到 fun 函数,皆执行 j=0; j++两句,所以值最终仍为 1

代码2:

#include< stdio.h>
static int a = 1;
void fun1(void)
{
    a = 2;
}
void fun2(void)
{
    int a = 3;
}
void fun3(void)
{
    static int a = 4;
}
int main()
{
    printf("%d", a);
    fun1();
    printf("%d", a);
    fun2();
    printf("%d", a);
    fun3();
    printf("%d", a);
}

解析: 打印 1222
首先声明了一个静态全局变量i,首次输出肯定是 1。
第二次输出,访问到了早已定义的全局变量i,并改写值,第二次为 2。
第三次输出,内部定义了一个同名的变量, 它并没有返回值或者被该次执行程序以外任何程序读取a值,所以第三次输出仍然是2。
第四次输出,定义了一个静态的局部变量,静态局部变量在函数调用结束后仍然存在,及它的内存空间不会被释放,但其他函数是不能引用它的,所以,两个静态变量虽然同名,但是并不是一样的东西,输出为2。

  • sizeof
    sizeof 为关键字,在计算变量所占空间大小时,括号可以省略,而计算类型(模子)大小时不能省略。
    eg:1.int a[100]; sizeof (a) 的值是多少? sizeof(a[100])呢? //请尤其注意本例。 sizeof(&a)呢? sizeof(&a[0])呢?
      int a[100]; //声明了一个有100个int类型元素的数组, 数组下标从0~99, 所以a是数组名, 代表数组的首地址, 也就是&a[0]
      sizeof (a) //返回数组a在内存中所占的空间大小,以字节为单位, 也就是sizeof(a) = sizeof(int) * 100 = 4 * 100 = 400bytes
                //a本身是个地址,用int存放,占用4个字节。int就是4,a[100]一共是100个int变量,就是400.
      sizeof(a[100])//求第100个元素的大小 ,用int存放,占用4个字节
      sizeof(&a)//表示存放a的地址的空间内存的地址的大小,即&a也是个地址值。一个地址用int存放也是4个字节
      sizeof(&a[0])//取第一个元素的地址,用int存放,占用4个字节
    
  1. int b[100];
     void fun(int b[100])
     {
      sizeof(b); //等于4
     }
     void fun(int* b)
     {
       sizeof(b); //等于4
     }

    两个是等价的。
    当你在调用fun函数时,他们内部实际上是这样做的,先将b[100]数组的首个元素的地址赋值给了函数参数列表中的那个b指针,虽然都是b,但是却是在不同额作用域,故你可以理解成fun参数列表中的b是一个指针,在32位系统中,指针永远是4个字节。

  • signed 和 unsigned关键字
    表示范围:一个 32 位的 signed int 类型整数其值表示法范围为: - 2^31~ (2^31)-1; 8 位的char 类型数其值表示的范围为- 2^7 ~ (2^7) -1。
     一个 32 位的 unsigned int 类型整数其值表示法范围为: 0~ (2^32) -1; 8 位的 char 类型数其值表示的范围为 0~ (2^8) -1
    #include< stdio.h>
    #include< string.h>
    int main()
    {
      char a[1000];
      int i;
      for (i = 0; i<1000; i++)
      {
          a[i] = -1 - i;
      }
      printf("%d", strlen(a));
      return 0;
    }

答案是 255
分析:按照负数补码的规则,可以知道-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’,则认为本字符串结束。

  • bool、float、int、指针与"0"值比较
  1. if(bTestFlag); if(!bTestFlag);
    bool bTestFlag = FALSE; 为了安全考虑,bool 值初始化false比较好,如果调用发生异常,不会被误调用
  2. if((fTestVal >= -EPSINON) && (fTestVal <= EPSINON)); //EPSINON 为定义好的精度。
  3. if(i == 0);
  4. if(NULL == p); if(NULL != p);
  • 堆和栈
    栈:保存局部变量。栈上的内容只在函数的范围内存在,当函数运行结束,这些内容会自动被销毁。其特点是效率高,但空间大小有限。栈是向由高地址低地址扩展的数据结构。
    堆:由 malloc 系列函数或 new 操作符分配的内存。其生命周期由 free 或 delete 决定。
    没有释放之前一直存在,直到程序结束。其特点是使用灵活,空间比较大,但而且容易产生内存碎片,容易出错。堆是由低地址向高地址扩展的数据结构

  • if 语句
    1.先处理正常情况,再处理异常情况。
    在编写代码是,要使得正常情况的执行代码清晰,确认那些不常发生的异常情况处理代码不会遮掩正常的执行路径。这样对于代码的可读性和性能都很重要。
    2.if 语句后不能写分号,走则默认执行语句。
    3.if 语句代码块应用大括号{}包含在内,否则默认只执行第一句,容易出错。

  • switch-case关键字
    1.每个 case 语句的结尾绝对不要忘了加 break,否则将导致多个分支重叠(除非
    有意使多个分支重叠)。
    2.最后必须使用 default 分支。即使程序真的不需要 default 处理,也应该保留
    语句:default :
          break;
    这样做并非画蛇添足,可以避免让人误以为你忘了 default 处理。
    3.case 后面只能是整型或字符型的常量或常量表达式。
    4.case 语句排列顺序:按字母或数字顺序排列各条 case 语句;把正常情况放在前面,而把异常情况放在后面;按执行频率排列

  • do、while、for 关键字
     while 循环:先判断 while 后面括号里的值,如果为真则执行其后面的代码;否则不执行。 while( 1)表示死循环。
     do-while 循环:先执行 do 后面的代码,然后再判断 while 后面括号里的值,如果为真,循环开始;否则,循环不开始。其用法与 while 循环没有区别,但相对较少用。
     for 循环: for 循环可以很容易的控制循环次数,多用于事先知道循环次数的情况下。
    tips:

      1.将最长的循环放在最内层,最短的循环放在最外层,以减少 CPU 跨切循环层的次数。长循环在最内层,效率高。
      2.建议 for 语句的循环控制变量的取值采用“半开半闭区间”写法。
      3.不能在 for 循环体内修改循环变量,防止循环失控。
      4.循环要尽可能的短,要使代码清晰,一目了然。采取重新设计循环或者封装到子函数
      5.把循环嵌套控制在 3 层以内。
    
  • break 与 continue关键字
     break 关键字很重要,表示终止本层循环。
     continue 表示终止本次(本轮) 循环。当代码执行到 continue 时,本轮循环终止,进入下一轮循环

  • 禁用goto 关键字

  • void 关键字

    1.任何类型的指针都可以直接赋值给void *,无需进行强制类型转换;但这并不意味着, void *也可以无需强制类型转换地赋给其它类型的指针。
      void *p1;
      int *p2;
      p1 = p2; //正确
      p1 = p2; //错误 提示“'=' : cannot convert from 'void *' to 'int *'”
    2.如果函数没有返回值,那么应声明为 void 类型
      在 C 语言中,凡不加返回值类型限定的函数,就会被编译器作为返回整型值处理
    3.如果函数无参数,那么应声明其参数为 void
    
  • return 关键字
     return 语句不可返回指向“栈内存”的“指针”,因为该内存在函数体结束时被自动销毁。

  • const 关键字
    1.定义 const 只读变量,具有不可变性。
    2.const 修饰的只读变量必须在定义的同时初始化,case 语句后面不可以是 const 修饰的只读变量。
    3.const修饰指针

    const int p; // p 可变, p 不可变,即指针可以指向别的内存,指针的值不可改变
    int const p; // p 可变, p 不可变,即指针可以指向别的内存,指针的值不可改变
    int const p; // p 不可变,p 可变,即指针不可以指向别的内存,指针的值可以改变
    const int *const p; //指针 p 和 *p 不可变,即指针和指针的值都不可以改变
    先忽略类型名(编译器解析的时候也是忽略类型名),我们看 const 离哪个近。“近水楼
    台先得月”,离谁近就修饰谁。
    4.const 修饰符也可以修饰函数的参数,当不希望这个参数值被函数体内意外改变时使
    用。
    5.const 修饰符也可以修饰函数的返回值,返回值不可被改变。

  • volatile 关键字
    到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问
    如果变量 i 是一个寄存器变量或者表示一个端口数据或者是多个线程的共享数据,就容易出错,所以说 volatile 可以保证对特殊地址的稳定访问。

  • extern 关键字
    extern 可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,下面的代码用到的这些变量或函数是外来的,不是本文件定义的,提示编译器遇到此变量和函数时在其他模块中寻找其定义。

  • struct 关键字
    结构体所占的内存大小是其成员所占内存之和,空结构体的大小就定为 1 个 byte

    typedef structst_type
    {
      int i;
      int a[];
    }type_a;
    type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));

为结构体指针 p 分配了一块内存。用 p->a[n]就能简单地访问可变长元素。

  • union 关键字
    union 维护足够的空间来置放多个数据成员中的“一种”,而不是为每一个数据成员配置空间,
    在 union 中所有的数据成员共用一个空间,同一时间只能储存其中一个数据成员,所有的数据成员具有相同的起始地址。
    union 主要用来压缩空间。如果一些数据不可能在同一时间同时被用到,则可以使用 union。
    应用:大小端问题
    1.定义
    大端模式( Big_endian):字数据的字数据的低字节则存放在高地址中,而高字节存储在低地址中。
    小端模式( Little_endian):字数据的字数据的低字节则存放在低地址中,而高字节存储在高地址中。
    2.程序判断当前系统的存储模式
    变量 i 占 4 个字节,但只有一个字节的值为 1,另外三个字节的值都为 0。如果取出低
    地址上的值为 0,毫无疑问,这是大端模式;如果取出低地址上的值为 1,毫无疑问,这是
    小端模式。既然如此,我们完全可以利用 union 类型数据的特点:所有成员的起始地址一致。
    参考答案如下:

    int checkSystem( )
    {
      union check
      {
          int i;
          char ch;
      } c;
      c.i = 1;
      return (c.ch ==1);
    }
  • enum 关键字
    1.例子

    enum Color
    {
      GREEN = 1,
      RED, //2
      BLUE, //3
      GREEN_RED = 10,
      GREEN_BLUE //11
    }ColorVal;
    Color 是自定义的一种数据数据类型名,而 ColorVal 为Color 类型的一个变量,也就是我们平时常说的枚举变量。
    2.下面再看看枚举与#define 宏的区别:
    1), #define 宏常量是在预编译阶段进行简单替换。枚举常量则是在编译的时候确定其值。
    2),一般在编译器里,可以调试枚举常量,但是不能调试宏常量。
    3),枚举可以一次定义大量相关的常量,而#define 宏一次只能定义一个。
    3.A),枚举能做到事, #define 宏能不能都做到?如果能,那为什么还需要枚举?
     B), sizeof( ColorVal)的值为多少?为什么?
    解析:a.枚举可以自增1,这样不用每一个值都定义,而宏必须每个值都定义。而且枚举是一个集合,代表一类值,像你代码中的颜色归为一类,方便使用,而宏不能形成集合。
       b.sizeof(ColorVal)是4,因为ColorVal是一个枚举变量,而枚举变量代表一个整数(如ColorVal = RED),而整数是4个字节。

  • typedef 关键字
    1.定义:typedef 的真正意思是给一个已经存在的数据类型(注意:是类型不是变量)取一个别
    名,而非定义一个新的数据类型。
    2.例子

    typedef struct student
    {
      //code
    }Stu_st,*Stu_pst;
    A) struct student stu1;和 Stu_st stu1;没有区别。
    B) struct student *stu2;和 Stu_pst stu2;和 Stu_st *stu2;没有区别

    3.与 const 连用
    const Stu_pst stu3;=》指针所指对象的值不能修改。
    Stu_pst const stu4;=》指针变量本身不允许被修改。
    4.typedef 与 define
    C)#define INT32 int
    unsigned INT32 i = 10;
    D)typedef int int32;
    unsigned int32 j = 10;
    其中 D)编译出错,为什么呢? C)不会出错,这很好理解,因为在预编译的时候 INT32
    被替换为 int,而 unsigned int i = 10;语句是正确的。但是,很可惜,用 typedef 取的别名不支持这种类型扩展。
    typedef static int int32 不可以
    typedef 用来定义一个变量类型的别名。static 不是变量类型。它定义存放方式。


阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xiaob_bai/article/details/79956592
文章标签: C语言
个人分类: C语言
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭