C语言学习

一、关键字:
const:声明制度变量
register:声明寄存器变量
volatile:说明变量在程序执行中可被隐含的改变
goto:无条件跳转语句
sizeof:计算对象所占内存空间

1.sizeof
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在计算变量所占空间大小时,括号可以省略,而计算类型(模子)大小时不能省略。一般情况下,咱也别偷这个懒,乖乖的写上括号,继续装作一个“函数” ,做一个“披着函数皮的关键字” 。做我的关键字,让人家认为是函数去吧。

2.volatile
volatile是易变的、不稳定的意思。很多人根本就没见过这个关键字,不知道它的存在。也有很多程序员知道它的存在,但从来没用过它。我对它有种“杨家有女初长成,养在深闺人未识” 的感觉。volatile关键字和 const一样是一种类型修饰符, 用它修饰的变量表示可以被某些编译器未知的因素更改,比如操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。先看看下面的例子:
int i=10;
int j = i;//(1)语句
int k = i;//(2)语句
这时候编译器对代码进行优化,因为在(1) 、 (2)两条语句中,i 没有被用作左值。这时候编译器认为 i 的值没有发生改变,所以在(1)语句时从内存中取出的值赋给 j 之后,这个值并没有被丢掉,而是在(2)语句时继续用这个值给 k赋值。编译器不会生成出汇编代码重新从内存里取 i 的值,这样提高了效率。但要注意: (1) 、 (2)语句之间 i没有被用作左值才行。再看另一个例子:
volatile int i=10;
int j = i;//(3)语句
int k = i;//(4)语句
volatile关键字告诉编译器 i是随时可能发生变化的,每次使用它的时候必须从内存中取出 i的值,因而编译器生成的汇编代码会重新从 i 的地址处读取数据放在k 中。这样看来,如果 i 是一个寄存器变量或者表示一个端口数据或者是多个线程的共享数据,就容易出错,所以说 volatile可以保证对特殊地址的稳定访问。但是注意:在 VC++6.0中,一般Debug模式没有进行代码优化,所以这个关键字的作用有可能看不出来。你可以同时生成 Debug版和 Release版的程序做个测试。

3.extern
extern,外面的、外来的意思。那它有什么作用呢?举个例子:假设你在大街上看到一个黑皮肤绿眼睛红头发的美女(外星人?)或者帅哥。你的第一反应就是这人不是国产的。extern就相当于他们的这些区别于中国人的特性。extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,下面的代码用到的这些变量或函数是外来的,不是本文件定义的,提示编译器遇到此变量和函数时在其他模块中寻找其定义。就好比在本文件中给这些外来的变量或函数带了顶帽子,告诉本文件中所有代码,这些家伙不是土著。那你想想 extern修饰的变量或函数是定义还是声明?
看列子:
A.c文件中定义: B.c 文件中用extern修饰:
int i = 10; extern int i;//写成 i=10;行吗?
void fun(void) extern void fun(void) ;//两个 void可否省略?
{
//code
}
C.h文件中定义: D.c文件中用 extern修饰:
int j = 1; extern double j;//这样行吗?为什么?
int k = 2; j = 3.0;//这样行吗?为什么?
至于 extern“C”的用法, 一般认为属于 C++的范畴, 这里就先不讨论。当然关于extern的讨论还远没有结束,在指针与数组那一章,你还会和它亲密接触的。

4.struct
平时我们要求函数的参数尽量不多于 4 个,如果函数的参数多于4 个使用起来非常容易出错 (包括每个参数的意义和顺序都容易弄错) , 效率也会降低 (与具体 CPU有关, ARM芯片对于超过 4 个参数的处理就有讲究,具体请参考相关资料) 。这个时候,可以用结构体压缩参数个数。

二、指针
1.概念
(1)一个指针是一个地址,是一个常量。而一个指针变量却可以被赋予不同的指针值,是变量。
(2)变量的指针就是变量的地址。存放变量地址的变量是指针变量。
2.定义一个指针
对指针变量的定义包括三个内容:
(1) 指针类型说明,即定义变量为一个指针变量;
(2) 指针变量名;
(3) 变量值(指针)所指向的变量的数据类型。
其一般形式为:
类型说明符 *变量名;
其中,*表示这是一个指针变量,变量名即为定义的指针变量名,类型说明符表示本指针变量所指向的变量的数据类型。
例如: int *p1; //p1 是指向整型变量的指针变量
表示 p1 是一个指针变量,它的值是某个整型变量的地址。或者说 p1 指向一个整型变量。至于 p1 究竟指向哪一个整型变量,应由向 p1 赋予的地址来决定。
3.指针变量的引用
指针变量同普通变量一样,使用之前不仅要定义说明,而且必须赋予具体的值。
两个有关的运算符:

  1. &:取地址运算符。

  2. *:指针运算符(或称“间接访问” 运算符) 。
    C语言中提供了地址运算符&来表示变量的地址。

    设有指向整型变量的指针变量 p,如要把整型变量 a 的地址赋予 p 可以有以下两种方式:
    (1) 指针变量初始化的方法
    int a;
    int *p=&a;
    (2) 赋值语句的方法
    int a;
    int p;
    p=&a;
    不允许把一个数赋予指针变量,故下面的赋值是错误的:
    int p;
    p=1000;
    被赋值的指针变量前不能再加“
    ”说明符,如写为
    p=&a 也是错误的。
    假设:
    int i=200, x;
    int *ip;
    指针变量可以进行某些运算,但其运算的种类是有限的。它只能进行赋值运算和部分算术运算及关系运
    算。

  1. 指针运算符
  1. 取地址运算符&:取地址运算符&是单目运算符,其结合性为自右至左,其功能是取变量的地址。在 scanf函数及前面介绍指针变量赋值中,我们已经了解并使用了&运算符。
  2. 取内容运算符*:取内容运算符是单目运算符,其结合性为自右至左,用来表示指针变量所指的变量。在运算符之后跟的变量必须是指针变量。 需要注意的是指针运算符和指针变量说明中的指针说明符不是一回事。在指针变量说明中,“”是类型说明符,表示其后的变量是指针类型。而表达式中出现的“”则是一个运算符用以表示指针变量所指的变量。
    【例】
    main(){
    int a=5,*p=&a;
    printf ("%d",*p);
    }
    表示指针变量 p 取得了整型变量 a 的地址。printf("%d",*p)语句表示输出变量 a 的值。
  1. 指针变量的运算
  1. 赋值运算:指针变量的赋值运算有以下几种形式。
    ① 指针变量初始化赋值,前面已作介绍。
    ② 把一个变量的地址赋予指向相同数据类型的指针变量。
    例如:
    int a,*pa;
    pa=&a; /把整型变量a 的地址赋予整型指针变量 pa/
    ③ 把一个指针变量的值赋予指向相同类型变量的另一个指针变量。
    如:
    int a,*pa=&a,*pb;
    pb=pa; /把a 的地址赋予指针变量 pb/
    由于 pa,pb均为指向整型变量的指针变量,因此可以相互赋值。
    ④ 把数组的首地址赋予指向数组的指针变量。
    例如:
    int a[5],*pa;
    pa=a;
    (数组名表示数组的首地址,故可赋予指向数组的指针变量 pa)
    也可写为:
    pa=&a[0]; //数组第一个元素的地址也是整个数组的首地址,也可赋予 pa
    当然也可采取初始化赋值的方法:
    int a[5],*pa=a;
    ⑤ 把字符串的首地址赋予指向字符类型的指针变量。
    例如:
    char *pc;
    pc=“C Language”;
    或用初始化赋值的方法写为:
    char *pc=“C Language”;
    这里应说明的是并不是把整个字符串装入指针变量,而是把存放该字符串的字符数组的首地址装入指针变量。在后面还将详细介绍。
    ⑥ 把函数的入口地址赋予指向函数的指针变量。
    例如:
    int (*pf)();
    pf=f; /f 为函数名/
    2) 加减算术运算
    对于指向数组的指针变量,可以加上或减去一个整数 n。设 pa 是指向数组 a 的指针变量,则pa+n,pa-n,pa++,++pa,pa–,--pa 运算都是合法的。指针变量加或减一个整数 n 的意义是把指针指向的当前位置(指向某数组元素)向前或向后移动 n 个位置。应该注意,数组指针变量向前或向后移动一个位置和地址加 1 或减1在概念上是不同的。因为数组可以有不同的类型,各种类型的数组元素所占的字节长度是不同的。如指针变量加 1,即向后移动 1 个位置表示指针变量指向下一个数据元素的首地址。而不是在原地址基础上加 1。
    例如:
    int a[5],*pa;
    pa=a; /pa 指向数组a,也是指向a[0]/
    pa=pa+2; /pa 指向 a[2],即pa 的值为&pa[2]/
    指针变量的加减运算只能对数组指针变量进行,对指向其它类型变量的指针变量作加减运算是毫无意义的。
  2. 两个指针变量之间的运算:只有指向同一数组的两个指针变量之间才能进行运算,否则运算毫无意义。
    ① 两指针变量相减:两指针变量相减所得之差是两个指针所指数组元素之间相差的元素个数。实际上是两个指针值(地址)相减之差再除以该数组元素的长度(字节数)。例如 pf1 和 pf2 是指向同一浮点数组的两个指针变量,设 pf1 的值为 2010H,pf2 的值为 2000H,而浮点数组每个元素占 4 个字节,所以 pf1-pf2 的结果为(2000H-2010H)/4=4,表示pf1 和 pf2 之间相差 4 个元素。两个指针变量不能进行加法运算。 例如,pf1+pf2 是什么意思呢?毫无实际意义。
    ② 两指针变量进行关系运算:指向同一数组的两指针变量进行关系运算可表示它们所指数组元素之间的关系。
    例如:
    pf1pf2 表示 pf1 和pf2 指向同一数组元素;
    pf1>pf2 表示 pf1 处于高地址位置;
    pf1<pf2 表示 pf2 处于低地址位置。
    指针变量还可以与 0 比较。
    设 p 为指针变量,则 p
    0 表明p 是空指针,它不指向任何变量;
    p!=0 表示 p不是空指针。
    空指针是由对指针变量赋予 0 值而得到的。
    例如:
    #define NULL 0
    int *p=NULL;
    对指针变量赋 0 值和不赋值是不同的。指针变量未赋值时,可以是任意值,是不能使用的。否则将造成意外错误。而指针变量赋 0 值后,则可以使用,只是它不指向具体的变量而已。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值