C语言学习总结(二)

来源:互联网 发布:手机录屏软件 编辑:程序博客网 时间:2024/05/23 15:44

函数参数表

关于函数参数表中的数组的一些问题,主要有:

(1)作为函数参数传递值的时候,普通变量传递的是值,指针变量传递的是地址的值,而数组作为一个参数传递时其实是传递了该数组的地址(也就是头元素的首地址),所以其实不是将整个数组传递到函数中的,相反,仅仅传递了首元素地址;(2)作为数组的概念,本身其实就是相当于指针,也就是说a[n]相当于在内存中开辟了一段连续的内存空间,首地址的指针*a就是指向了第一个元素a[0]所在的内存位置,但是数组只不过是一个const静态定义的指针(const是定义静态变量类型的,也就是说从定义开始const所定义的变量就不能改变值,一个数组对应有一个独特的指针),它传递给函数的是数组对应的地址,这个时候整个数组对应的const指针就是*a,它是没办法指向其他地址的,也就是只能指向数组a[0](关于const定义类型,可以参考百度百科)

const *和*const

当const在 * 的前面或后面定义时,意义是完全不同的,当const在 * 前面时指的是指针所指向的东西不可修改;const在 * 的后面是指针不可修改。


指针的运算

指针的运算:指针做加减运算时,其实是进行了对应的sizeof()单元长度的运算,比如char是1,int为4;当然!如果指针加减运算不是运用于一段连续的空间比如数组,那么指针的加减运算是完全没意义的。同样,指针也可以进行逻辑运算,但是运算的对象就是地址的值。关于指针的作用,在函数需要多值传递或者多值返回时,亦或者需要用到动态内存分配时,就得需要用到指针运算。关于指针的进一步学习,可以看c和指针进行进一步的学习!

关于指针的大小问题,这是一个非常基本但是很多人都会模糊的概念!

指针也就是地址,无论什么类型的指针,说到底就是一个内存地址,那么内存地址一般大小有多大呢?这个就与计算机CPU的地址总线宽度有关,一般我们80x86体系的计算机都是32位地址总线宽度,所以自然而然内存所能访问的地址范围就是0-4G,也就是说,在这种体系结构下的指针大小统一都是32位4个字节的大小,无论指针变量的类型是什么,哪怕是一个结构体指针,也都是32位的大小,这是绝对的!


动态内存分配malloc函数的使用

需要用到头文件stdlib.h,函数的原型是void* malloc(size_t size)

这里要注意的是用malloc函数向系统申请到的内存空间的大小是以字节为单位的,也就是说最小的申请空间是一个字节大小的空间(不能再小了,也就是相当于一个char),而且malloc函数返回的结果是void*,这个时候我们要根据具体需要对返回的指针做强制类型转换的操作,比如(int)*malloc(n*sizeof(int))就是向系统申请n个int长度字节大小的内存空间(也就是说申请4n个字节地址)。

而一旦malloc申请失败,则返回一个0指针或者null指针。而对应的free()函数则是负责将malloc申请的空间释放,将占用的内存资源还给系统。


指针的指针

char **a 代表的是一个指针a指向另一个指针,而那个指针指向的是一个字符或者字符串!具体的关于双重指针的内容,应该参考一下c和指针这本名著!

而另一个是关于char *a[ ]这种类型的定义,这种是定义了一个字符串指针的数组,叫做字符串数组,其中a[0]就是一个指向字符(串)的指针,同理其他a[1],a[2]也一样。


强制类型转换注意点

强制类型转换的结果是不会改变原有的值的(也就是说位值是不变的,改变的只是解释这些位的方式)。


枚举

枚举类型:首先应该介绍一下,在c语言中,如果我们要用到一些数字代表特定的含义,但是数字本身最好就不应该直接出现在代码中,所以,我们一开始是可以这样做的:

        int const red     = 0;        int const green  = 1;        int const yellow = 2;

由于int const定义的是一个常量整型数据(即常量符号),所以这样定义后red,green和yellow在代码中可以直接使用而且特定代表012这样的值,而且这三个常量甚至已经无法修改了。
同样的,为了引入这样的效果,所以就有枚举类型的定义方法:

        enum  枚举类型名字(名字0,名字1,名字2.........)

(注意:此处的枚举类型名字在c语言中可以省略)

enum类型在c中其实就是一组整型数组的特殊集合,在枚举类型定义的括号内,从名字0~名字n代表的数字分别是从0依次递增到n,这是绝对肯定的!枚举的意义在于当程序中需要用到一些可以排列起来的常量值时候,通过枚举可以给出一组常量值名字。


结构体变量

结构体是数据类型的一个特殊集合,在c语言中为了满足一些特定的需要复合数据类型的数据对象,就要用到结构体变量来进行定义。

struct 结构体变量名称{        数据类型1   变量1;数据类型2  变量2;数据类型3  变量3........};              //(这里必须注意!!!结构体变量定义时要加上分号;)

结构体成员的引用是用到了.这个运算符,这个点也是运算符。

结构体变量还可以被当做函数参数进行传输,但是要注意的是: 在使用结构体变量A充当函数实参进行值传递时,其实在函数体内是没有接收到A的值的,而且在函数体内重新构建了个相同类型的结构体变量B,而将A中的成员值都依次传递到B中,所以此时如果对B进行任何操作,其实都不会影响到A,因为它们就是完全不同的两个结构体。

另一方面,从效率考虑,最好不要直接用结构体变量进行函数参数的传递,我们可以选择指针进行高效率的值传递(事实上,在c语言中,指针作为灵魂所在,在充当多值传递和追求编译效率以及系统开销的节省方面,都是不二选择!),所以可以选择指向结构体的指针,当做传递的对象,但因为结构体变量本身并不是代表地址(与数组完全不同,数组名本身代表地址),所以必须要用到取地址符号&进行指针赋值。具体定义如下:

结构体定义:    struct  结构体名    {    类型1  成员1;    类型2 成员2;    ……}结构体变量名称;
结构体指针定义:    struct 结构体名 *p = &结构体变量名称;    此时p指针指向的是结构体变量的指针,可以有两种类型的操作:    1.(*p).成员1 = num;    2.p->成员1 = num;

这两种类型操作本质上是一样的。第二种在一些高级源代码中较为常见,比如Linux内核源码。

而且,在结构体相关知识中,还有一个结构体数组的定义方式,就是定义一个结构体变量的数组。定义方式如下:

struct date date[100];      //定义了一个下标为100的结构体数组,其中每一个元素都是一个struct date类型的结构体变量。一共有100个,从0下标一直到99.

而另一个,关于结构体的嵌套使用,也就是说可以在一个定义的结构体内嵌套使用另一个已定义好的结构体变量。


自定义数据类型关键字

typedef:它的作用是可以声明新的类型的名字,也即是用一个新的名字替换掉某种类型的关键字,从而成为该关键字类型的别名.

而关于union联合类型定义,这个比较少见,其中用union联合类型定义的变量是共用同一个内存空间的,也就是说所有的成员都是共享一个空间,而且同一时间内只有一个成员是有效的,而union的大小具体要看其所定义的成员类型大小的最大值。


可变长数组

可变长数组的构建:具体实现步骤可参考

http://mooc.study.163.com/course/ZJU-1000004000#/info

翁凯教授的视频教程,详细无比。
其原理主要是用到了数组的拷贝,也就是说一开始默认一个固定长度数组(数组也必须是固定长度,C99后可引入变量下标),然后等数组用完空间后,使用malloc构造一个新的更长的数组,将旧数组的数据拷贝到新数组中,free旧数组空间,以这种开销较大的方式实现了数组的动态增长。


全局变量和局部变量,动态存储与静态存储

全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用, 因此可以避免在其它源文件中引起错误。

static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用;

static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值;

static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝。


C程序结构布局

C程序一直由下列部分组成:
( 1)正文段——CPU执行的机器指令部分;一个程序只有一个副本;只读,防止程序由于意外事故而修改自身指令;

( 2)初始化数据段(数据段)——在程序中所有赋了初值的全局变量,存放在这里。

( 3)非初始化数据段(bss段)——在程序中没有初始化的全局变量;内核将此段初始化为0。

( 4)栈——增长方向:自顶向下增长;自动变量以及每次函数调用时所需要保存的信息(返回地址;环境信息)。

( 5)堆——动态存储分配。

在全局变量之前加上关键字static,全局变量就被定义成为一个全局静态变量。
(1)内存中的位置:静态存储区(静态存储区在整个程序运行期间都存在)

(2)初始化:未经初始化的全局静态变量会被程序自动初始化为0(自动对象的值是任意的,除非他被显示初始化)

(3)作用域:全局静态变量在声明他的文件之外是不可见的。准确地讲从定义之处开始到文件结尾。

好处:
定义全局静态变量的好处:
<1>不会被其他文件所访问,修改
<2>其他文件中可以使用相同名字的变量,不会发生冲突。

局部静态变量
在局部变量之前加上关键字static,局部变量就被定义成为一个局部静态变量。
1)内存中的位置:静态存储区
2)初始化:未经初始化的全局静态变量会被程序自动初始化为0(自动对象的值是任意的,除非他被显示初始化)
3)作用域:作用域仍为局部作用域,当定义它的函数或者语句块结束的时候,作用域随之结束。

注:当static用来修饰局部变量的时候,它就改变了局部变量的存储位置,从原来的栈中存放改为静态存储区。但是局部静态变量在离开作用域之后,并没有被销毁,而是仍然驻留在内存当中,直到程序结束,只不过我们不能再对他进行访问。
当static用来修饰全局变量的时候,它就改变了全局变量的作用域(在声明他的文件之外是不可见的),但是没有改变它的存放位置,还是在静态存储区中。


静态函数

在函数的返回类型前加上关键字static,函数就被定义成为静态函数。
函数的定义和声明默认情况下是extern的,但静态函数只是在声明他的文件当中可见,不能被其他文件所用。

定义静态函数的好处:
<1> 其他文件中可以定义相同名字的函数,不会发生冲突
<2> 静态函数不能被其他文件所用。

存储说明符auto,register,对应两种存储期:自动存储期和静态存储期。 auto和register对应自动存储期。具有自动存储期的变量在进入声明该变量的程序块时被建立,它在该程序块活动时存在,退出该程序块时撤销。

关键字extern和static用来说明具有静态存储期的变量和函数。用static声明的局部变量具有静态存储持续期(static storage duration),或静态范围(static extent)。虽然他的值在函数调用之间保持有效,但是其名字的可视性仍限制在其局部域内。静态局部对象在程序执行到该对象的声明处时被首次初始化。

由于static变量的以上特性,可实现一些特定功能。


统计次数功能

声明函数的一个局部变量,并设为static类型,作为一个计数器,这样函数每次被调用的时候就可以进行计数。这是统计函数被调用次数的最好的办法,因为这个变量是和函数息息相关的,而函数可能在多个不同的地方被调用,所以从调用者的角度来统计比较困难。


C语言中使用静态函数的好处

静态函数会被自动分配在一个一直使用的存储区,直到退出应用程序实例,避免了调用函数时压栈出栈,速度快很多。
关键字“static”,译成中文就是“静态的”,所以内部函数又称静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件。 使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名,因为同名也没有关系。


static变量

1).局部
a.静态局部变量在函数内定义,生存期为整个源程序,但作用域与自动变量相同,只能在定义该变量的函数内使用。退出该函数后, 尽管该变量还继续存在,但不能使用它。
b.对基本类型的静态局部变量若在说明时未赋以初值,则系统自动赋予0值。而对自动变量不赋初值,则其值是不定的。

2).全局全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。但是他们的作用域,非静态全局 变量的作用域是整个源程序(多个源文件可以共同使用); 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。


static函数(也叫内部函数)

只能被本文件中的函数调用,而不能被同一程序其它文件中的函数调用。区别于一般的非静态函数(外部函数) static在c里面可以用来修饰变量,也可以用来修饰函数。 先看用来修饰变量的时候。变量在c里面可分为存在全局数据区、栈和堆里。其实我们平时所说的堆栈是栈而不包含对,不要弄混。

0 0
原创粉丝点击