读书笔记-C语言关键字

001 关键字

  • C语言一共32个关键字

    1. 声明和定义

  • 在开始认识关键字前,必须要明白什么是声明,什么事定义:
    • 定义:(编译器)创建一个对象,为这个对象分配一段内存并给他取上一个名字。在一个作用域内,一个变量或者对象只能定义一次,并且定以后为它分配的内存不可变;
    • 声明:1、告诉编译器这个名字已经和一片内存匹配上了,并且这个内存是在其他地方定义的,声明可以多次;2、告诉编译器,这个名字已经占用,不能再用来定义其他变量或者对象了。
  • 备注:定义创建了对象,并为它分配了内存,声明没有分配内存

2. 关键字

2.1. auto(最宽宏大量的关键字)

  • 在默认情况下,编译器默认所有变量都是auto的,所以我们可以当它不存在。

2.2. register(最快的关键字)

  • 请求编译器,尽可能的把使用register定义的变量发到CPU的寄存器中,不保证一定放。(CPU的寄存器有限)
  • 注意:register变量长度应该小于等于int,并且不能使用&(register变量不存放在内存中)

2.3. static (最名不符实的关键字)

  • 修饰变量

    • 修饰全局变量,从定义开始到文件结束可用,其他文件即使使用extern 也不能使用
    • 修饰局部变量,只能在函数体里面使用
    • 由于被static修饰的变量都是存放在内存的静态区,所以即使函数运行结束,这个变量也不会被销毁,函数下次使用时任然能用到这个值。
  • 修饰函数
    • 函数使用static修饰表示作用域仅限于本文件,又称内部函数

2.4. short、int、long、char、float、double(基本数据类型)

  • 变量的命名规则

    • min length && max information
    • 望而知意,便于记忆
    • 由多个单词组成是,要么用下划线隔开,要么每个单词首字母大写
    • 尽量避免出现数字编号,除非逻辑上需要
    • 所有宏定义、枚举常数、只读变量全部用大写字母命名,用下划线分割单词
    • 循环变量 n 、m、i、j、k;char c; int a[]; int *p 。

2.5. sizeof (最冤枉的关键字)

  • sizeof 是 32 个关键字中的一个,而不是一个函数。
  • sizeof(int) * p 表示什么意思呢?

2.6. signed unsigned 关键字

  • 先来分析一段代码
#include <stdio.h>
#include <string.h>

int main()
{
    signed char a[1000];
    int i = 0;

    for (i = 0; i < 1000; i++)
    {
        a[i] = -1 - i;
    }
    printf("%d\n", strlen(a));

    return 0;
}

当 i = 128 的时候,a[128] = -1 - 128 = -129 ,signed char 的取值范围是 -128~127,所以超出了范围,-129 源码: 1 1000 0001 ,补码:1 0111 1111 ,低 8 位 0111 1111 ,即 127。

当 i = 255 的时候,a[255] = -1 - 255 = -256 , -256 的原码 11 0000 0000 补码:11 0000 0000 ,低 8 位全为 0, strlen 遇到 ‘\0‘ 就结束,所以上述代码输出 255。

所以,char 类型如果直接使用就用来表示字符,如果加了 signed 和 unsigned 限定符的话就用来表示数字。

2.7. if else 组合

  • bool变量与“零值”比较
bool b = FALSE;

if(b)
    printf("TRUE");
if(!b)
    printf("FALSE");

上面这种写法是值得推荐的,其他方式会有问题。

  • float 变量与“零值”比较

    if(test > -EPSINON) || (test < EPSINON); // EPSINON 为定义好的精度
  • 指针变量与“零值”比较
    if(NULL == p)
    if(NULL != p)
  • 先处理正常情况后处理异常情况或者说先处理概率大的,再处理概率小的
  • 赋值运算不能用在产生 bool 值得表达式上
    if((x = y) != 0 )
    printf("------");

    这样写是错误的,应该这样写

    x = y;
    if(x != 0)
    printf("-----");

2.8. switch case 组合

当嵌套比较少量的分支的时候可以使用 if else 组合,但是当嵌套的分支多的时候就要使用 switch case 组合了,但是也不要刻意去创造一个 switch 变量。

  • 每个case 的结尾不要忘了 break; 除非你有意让多个分支重合。
  • 最后必须使用 default 分支,也需要 break; 。
  • 在 switch case 组合中禁止使用 return 语句。
  • switch 表达式不应该是有效的 bool 值,例如下面的表达式是不允许的。
    switch (x == 0)
    {
    ......
    }
  • case 后面的值只能是×××或者字符型的常量或者常量表达式。
  • case 语句的拍立顺序
    • 按照字母顺序排列
    • 把正常情况放在前面,把异常情况放在后面,并做好注释,哪里是开始,哪里是结束。
    • 把常执行的情况放在前面,不常执行的情况放在后面。
  • 没种情况的相关代码要尽量简洁,必要时可以使用函数。

2.9. do while for 关键字

  • break 和 continue 的区别

    • break 跳出循环
    • continue 终止本次循环,进入下一次循环
  • 多重循环的时候应该把最长的循环放在最内层,最短的循环放在最外层,这样可以有效降低CPU切换循环层数的次数。
  • for 循环中,半开区间比闭区间直观,能写成 a < 10,绝不写成 a <= 9。
  • 不要在 for 循环体内改变循环变量,否则会导致循环失控,像下面这种代码是不好的。
    for(int i = 0; i < n; i++)
    {
    n = 10;
    }
  • 循环嵌套控制在3层以内。
  • for 循环的控制语句不能包含任何浮点类型的变量。

2.10. goto 关键字

goto 关键字可以在代码中灵活的跳转,存在很大的争议,有的建议慎用 goto ,有的建议不要用。我觉得用得好还是可以用。

int *p = NULL;
...
goto error;
...
error:
    return -1;

2.11. void 关键字

void *p = NULL;
int *p_int = NULL;
p = p_int;    //不会报错,是正确的
p_int = p;    //报错,是错误的
  • void 修饰函数返回值和函数参数

    • 如果函数没有返回值应该将其声明为 void 类型,函数没有参数也应该将其声明为 void 类型
    • 如果函数的参数可以是任意类型的指针,则声明为 void *
      void *memcpy(void *dest, const void *src, size_t len)
  • void 不能代表一个真实的变量,例如下面的代码是错的
    void a;
    fun(void a);

2.12. return 关键字

  • return 用来终止一个函数,并返回其后跟的值。

    return(value);    //括号可以省略,但一般不省略,尤其是返回一个表达式的值得时候
  • return 不能返回指向栈内存的指针,因为该内存在函数体结束的时候就被销毁了。

2.13. const 关键字

  • const 修饰的只读变量必须在定义的同事初始化(首先 const 修饰的变量,其次是只读,两层含义)
  • 想一想 switch case 中 case 语句后面就可以是 const 修饰的只读变量吗?

先忽略类型,看 const 离谁近就修饰谁

const int *p;           // const *p ,修饰 *p , p 可以改变, p 指向的内容不可变
int const *p;           // const *p ,同上
int * const p;          // * const p ,修饰 p,p 不可以改变,p 指向的内容可以改变
const int * const p;    // const * const p ,第一个 const 修饰 *p,第二个 const 修饰 p,所以 p 和 p 指向的内容都不能改变
  • 修饰函数的参数,当不希望函数的参数在函数体中被改变的时候可以使用 const 修饰函数的参数。
  • 修饰函数的返回值(const int fun(void))。
  • 在另一个文件引用 const 变量时使用 extern const int i; 声明即可,而不是定义。

2.14. volatile 最易变的关键字

  • volatile 关键字和 const 一样,是一种类型修饰符。它修饰的变量表示可以被某些编译器未知的因素更改,比如操作系统,硬件或其他线程。遇到这个关键字修饰的变量,编译器就不对它做优化,每次需要访问的时候都重新去内存中读取。

在这里 volatile 只是告诉编译器 a 的值可能会被改变,需要访问时每次都要重新到内存中去取。

2.15. extern 最会戴帽子的关键字

  • 修饰函数和变量,表示函数和变量不是在本文件定义的,在本文件只是使用 extern 声明。

2.16. struct 关键字

空结构体占多大内存?大多数编译器是 1,gcc 是 0。所以不要太相信书本上的东西,一定要自己亲自验证。

  • 柔性数组

    • 不要惊讶,C语言中确实有柔性数组这个说法的。
    • C99 中,结构体最后一个成员允许是未知大小的数组,这就是柔性数组。但是柔性数组成员前必须至少有一个其他成员。sizeof 计算的大小不包含柔性数组所占的空间。为包含柔性数组的结构体分配内存要使用 malloc ,并且分配的内存应该大于 sizeof计算的值。
typedef struct st_type{
    int i;
    a[0];
}type_a;
//有些编译器会报错,可以改成
typedef struct st_type{
    int i;
    int a[];
}type_a;
//可以使用下面的代码为柔性数组分配内存,但是分配好之后使用 sizeof 计算结构体的大小依然是不包含柔性数组所占的内存的。
type_a *p = (type_a *)malloc(sizeof(type_a) + 100 * sizeof(int));

//记得用完之后要 free
free(p);
  • 结构体里面能不能放函数呢?

2.17. union 关键字

  • union 中所有数据成员共用一块内存,同一时间只能存储其中一个数据成员。union 的大小是最大成员所占内存的大小。

    2.17.1. 大小端对 union 类型数据的影响

union {
        int i;
        char ch;
}c;

c.i = 1;

//这时候 ch 只需要一个字节存储,在低地址,如果 ch 的值等于 1, 说明 i 的值得低字节 1 存储在低地址,是小端模式。Ubuntu、Windows 等 x86 架构都是小端模式。
int main()
{
    int a[5] = { 1, 2, 3, 4, 5 };
    int *p1 = (int *)(&a + 1);
    int *p2 = (int *)((int)a + 1);
    printf("%x %x\n", p1[-1], *p2);

    return 0;
}
// 5 2000000 小端模式 大端模式 5 2

2.18. enum 关键字

enum enum_type_name{
    ENUM_CONST_1,
    ENUM_CONST_2,
    ENUM_CONST_3,
    ...
    ENUM_CONST_n
} enum_variable_name;
  • enum_type_name 是自定义的枚举类型,而 enum_variable_name 是 enum_type_name 类型的一个变量。
  • sizeof(enum_variable_name) 的值是多少呢? 想想枚举成员的类型就会知道是 4 了, 空枚举类型的大小也是 4。

2.19. typedef 关键字,最伟大的缝纫师

  • typedef 是给已经存在的数据类型取一个别名,而不是重新定义新的数据类型。
typedef struct student{
    int age;
    char name;
} stu_1, *stu_2;

const stu_2 student_1;    // 其实是 const (struct student *)student_1; 所以 修饰的是 student_1 本身,而不是指向的内容
stu_2 const student_2;    // 其实是 (struct student *)const student_2; 所以 修饰的也是 student_2 这个指针本身,而不是指向的内容
  • typedef 和 #define 之间的区别
#defien INT32 int
typedef int INT32_t

unsigned INT32 i = 10;    //没问题,只是替换
unsigned INT32_t j = 10;  //错误,不支持

#defien PCHAR char*
typedef char* pchar

PCHAR ch1, ch2;    //ch1 是 char * 类型,ch2 却是 char 类型
pchar ch2, ch4;    //ch3, ch4 都是 char * 类型

3. 总结

不复习不知道,一复习吓一跳,原来 C语言 关键字还有这么多知识点,以前也没怎么注意,当然不止这些,我只是记录了我认为比较重要而且容易搞混淆的。

如果你觉得我的读书笔记对你有用,可以关注微信公众号 kalier 哦,最新的文章和读书笔记都将在这里首发。

原文地址:http://blog.51cto.com/8780862/2096288

时间: 2024-10-12 23:01:00

读书笔记-C语言关键字的相关文章

R语言实战读书笔记1—语言介绍

第一章 语言介绍 1.1 典型的数据分析步骤 1.2 获取帮助 help.start() help("which") help.search("which") 1.3 管理R工作区间函数 1.3 输入和输出 1.3.1 输入 函数 source("filename") 可在当前会话中执行一个脚本.如果文件名中不包含路径,R将假设此脚本在当前工作目录中.举例来说, source("1.R") 将执行包含在文件1.R中的R语句集合

网摘读书笔记----javascript语言精粹

原文链接:http://www.cnblogs.com/Cohlint/archive/2012/11/26/2788790.html 1.6种值会为假(==false),分别是false,null,undefined,' ',0,NaN 2.typeof有6种值,分别是'number','string','boolean','undefined','function','object';其中typeof(null),结果是'object' 3.number类型总是64位浮点数,两个整数相除也可

[读书笔记] R语言实战 (十三) 广义线性模型

广义线性模型扩展了线性模型的框架,它包含了非正态的因变量分析 广义线性模型拟合形式: $$g(\mu_\lambda) = \beta_0 + \sum_{j=1}^m\beta_jX_j$$ $g(\mu_\lambda)为连接函数$. 假设响应变量服从指数分布族中某个分布(不仅仅是正态分布),极大扩展了标准线性模型,模型参数估计的推导依据是极大似然估计,而非最小二乘法. 可以放松Y为正态分布的假设,改为Y服从指数分布族中的一种分布即可 glm()函数:glm(formula,family=f

《C专家编程》读书笔记

<C专家编程>读书笔记 C语言的设计哲学: 一切工作程序员自己负责. 语言中的所有特性都不需要隐式的运行时支持. 程序员所做的都是对的. 程序员应该知道自己在干什么,并保证自己的所作所为是正确的. --第1章-- C: 穿越时空的迷雾 小即是美.事物发展都有个过程,由简入繁,不能一开始就想得太复杂,Multics, IBM的OS/360都是因此而失败. C语言的许多特性是为了方便编译器设计者而建立的.----唉,怎么这个样子 C语言的基本数据类型直接与底层硬件相对应.----确实如此 regi

C#入门经典(v6) 读书笔记 (第一部分 C#语言)

前言 我说会C#,实际都是皮毛.买了<C#入门经典>(v6)和<C#高级编程>(v9),决定系统地学一遍.这是我的读书笔记而已,不是什么教程,如果有错误,还请各路大神指正. 希望自己坚持看完这两本书,做好笔记,补缺补漏. 第1章 C#简介 Framework 框架 Compact 精简的 OOP(Object-Oriented Programming,面向对象编程) CTS(Common Type System,通用类型系统)指如int等最基础的数据类型 CLR(Common La

读书笔记之:C语言深度剖析

读书笔记之:C语言深度剖析 <C 语言深度解剖>这本书是一本“解开程序员面试笔试的秘密”的好书.作者陈正冲老师提出“以含金量勇敢挑战国内外同类书籍”,确实,这本书中的知识点都是一些在面试中常见的考点,并且很多都是我们平常不注意的点,对于我们深入理解C语言确实很有帮助. 第1章关键字 1.register虽然寄存器的速度非常快,但是使用register修饰符也有些限制的:register变量必须是能被CPU寄存器所接受的类型.意味着register变量必须是一个单个的值,并且其长度应小于或等于整

程序语言的奥妙:算法解读 &mdash;&mdash;读书笔记

算法(Algorithm) 是利用计算机解决问题的处理步骤. 算法是古老的智慧.如<孙子兵法>,是打胜仗的算法. 算法是古老智慧的结晶,是程序的范本. 学习算法才能编写出高质量的程序. 懂得了算法,游戏水平会更高. 比如下棋,如果懂得棋谱,就不需要每次考虑"寻找最好的一步棋",按照棋谱 就可以走出最好的几步棋.棋谱是先人们智慧的结果,因此掌握多种棋谱的人更 容易在对弈中获得胜利. 算法的学习类似学习游戏攻略. 算法是编写好程序的"棋谱". 算法必须满足&

《30天自制操作系统》读书笔记(3) 引入C语言

这一次的学习相当曲折, 主要是因为粗心, Makefile里面的错误导致了文件生成出现各种奇奇怪怪的问题, 弄得心力交瘁, 因此制作过程还是尽量按着作者的路子来吧. 作者提供的源码的注释在中文系统下是乱码, 而且代码的分隔用了两个Tab, 在这里要处理一下: :%s/;.*//g 删除所有的注释; :%s/\t\t/\t 把两个Tab替换为一个Tab; 要让作者的nas文件和asm文件拥有相同的语法规则, 在_vimrc文件的最后一行添加 au BufNewFile,BufRead *.nas

JavaScript语言精粹读书笔记- JavaScript对象

JavaScript 对象 除了数字.字符串.布尔值.null.undefined(都不可变)这5种简单类型,其他都是对象. JavaScript中的对象是可变的键控集合(keyed collections). 对象是属性的容器,其中每个属性都拥有名字和值. JavaScript中的对象是无类别的(class-free)的.它对新属性的名字和值没有约束. JavaScript包括一个原型链特性,允许对象继承另一对象的属性. 对象的检索: stooge[“first-name”]或者stooge.