c语言的标识符不能和标准库中的关键字重名,除此外,它由字母、数字、以及下划线组成,只能以字母或者下划线开头。这里两种命名方法,各举一个例子(这两种没有优劣之别,使用完全看个人习惯):
还有,#define重命名的类型支持扩展,而typedef重命名的类型不支持扩展,看以下代码:
在c++的编译器上这个输出结果是: 但是如果把定义num的那行代码改成这种 那么输出结果就会是我们想要的 一、基本数据类型(以下描述均在32位cpu环境下)
一切整数在内存中都是以它的补码的形式存储的,而读取的时候则转换回原码。这样的存储方式有两个好处 2>浮点型数据在内存中的存储方式 对于32位的浮点数来说(float):最高的一个bit位(示意图如下)是符号位,用1来表示负数,用0来表示正数。接下来的8个bit位用来表示指数,剩下来的23位均用来表示数值。对于64位的浮点数来说(double),大体和32位相同
struct定义的结构体类型、union定义的联合体(共用体)类型、enum定义的枚举类型
复杂表达式的求值是由三个因素决定的:操作符的优先级,操作符的结合性,以及操作符是否控制求值顺序,相邻两个操作符究竟哪个先执行取决于他们的优先级,若是优先级相同,则再看结合性。这里我强调一下,优先级的那个表最好还是背过,因为虽然自己可以在所有要执行的运算两边加上括号,以避免优先级的问题,但是不代表别人也会那样,而背过那个表,就会避免很多不必要的麻烦。
1.概念:一组类型相同的数据的集合,在内存地址上从低到高依次排列(也就是说,定义数组时在内存上的栈空间申请一段连续的空间)2.分类:一维数组和多维数组(注意:这个维数可不是空间上的维数,所有类型的数组在内存上都是呈线性排列的,读者千万别被和谭书上类似的说法给误导了)
2.可以只给一部分元素赋值 3.想使一个数组中全部元素值为0,可以写成:a[10]={0}; 需要注意 int a[10] = {1}; 并不能把数组初始化为全1,只是将第一位初始化为1,后面全部都是0滴. 4.在对全部数组元素赋初值时,可以不指定数组长度。
4.定义:类型+标识符+[常量]/[(常量)][常量]……这里需要注意的地方有以下几点: 1)数组名只有在:sizeof()里面,还有取地址的时候代表整个数组的长度,其他时候都代表数组首元素的首地址 2)数组在使用的时候可以采用下标的方式(eg:arr[3]),也可以采用解引用的方式(eg:*(arr+3))1>在多文件编程中的声明上来说,在A文件中定义为数组,在B文件中声明为指针是会出错的,定义为指针,声明……也一样会出错。 2>从举藕法来说,两个指针指向同一个字符串,更改一个指针的内容是会改变另一个指针指向的内容,但是两个数组若是都存放同一个字符串,更改一个数组的内容对另一个数组的内容并没有影响。 这是因为两个指针指向的字符串内容存储在字符常量区,而数组中存放的内容则存储在内存上的栈区。 3>看代码 因为数组名放在sizeof里面代表整个数组,而指针的名字永远代表一块地址,而地址在内存中永远占4个字节(windows32位平台下) 7.(柔性数组(和结构体))柔性数组是什么呢?它定义在结构体的最后一个成员,但是它的前面至少要有一个成员。使用方法如下: 柔性数组的优点主要有这两点:
在实际操作中,我们常常需要将一些长度不等的字符串保存在一个二维数组中,你大概会这么做:
这样做确实达成了我们的要求,但是这样操作明显有两个缺点: 这种操作就被称为“锯齿形数组”,由于 [] 的优先级高于 *,所以dst先和 [] 结合形成一个数组,然后数组里面存放的元素就是指针,指向要存储的字符串。 1.概念:实现某一个功能的一段代码块2.原型(我把一些包括返回值类的注意事项都写在这个条目下)
作用:给编译器声明函数的信息,使函数不用重复定义就可以直接使用 3.函数调用原理(引出栈帧)
由于在CPU中,计算机没有办法知道一个函数调用需要多少个、什么样的参数,也没有硬件可以保存这些参数。也就是说,计算机不知道怎么给这个函数传递参数,传递参数的工作必须由函数调用者和函数本身来协调。为此,计算机提供了一种被称为栈(在调用的时候形成栈帧)的数据结构来支持参数传递。
在参数传递中,有两个很重要的问题必须得到明确说明:
而调用约定就是用来解释说明这两个问题的。 5.库函数相关(常见的函数)
恐怕这句“OK!”哪种情况都是无法输出的,因为strlen()的返回值是一个无符号类型整数,而两个无符号类型整数运算的结果也是一个正整数,所以……所以如果有需要,则要将strlen的返回值强制类型转换成int型 函数说明:从字符串dst中查找字符cc(注意这里是整形,用的时候要用单引号括起来),函数返回第一次找到cc的位置,若未找到,则返回NULL,类似的还有strrchr(这个函数是从后向前找)
函数说明:它从字符串str1中隔离各个单独的称为标记的部分,并且丢弃分隔符。 这个函数若用于将一串字符中想要去掉的一些字符拿走,将这些字符前后的字符连起来,则可以封装成一个函数,需要用的时候直接将字符串作为参数传进去还是很方便的! 3) 字符分类函数和字符转换函数 这里我列举几个常用的函数
4)I/O家族(具体放到文件那里讲) 7.不定参数(它其实是由宏实现的,但是它的用法又体现在函数这里)详情参见我的另一篇博客:
这里易出现的问题有:带歧义的宏、宏的副作用(i++和i+1) 按照命名约定,定义宏名时通常为大写字母。 下面做一下函数和宏的优缺对比
1、从代码量上来说,函数是将一段代码块封装成一条代码(即函数调用),所以它的使用可以减少一个项目中的代码量。但是宏替换每一次都会将一段代码插入到项目代码中,从而增加项目代码量。所以除非宏非常短,否则就使用函数来代替宏的功能 这里说明一下#和##的作用 “#”:把一宏参数变成对应的字符串//这里利用了相邻的字符串自动连接在一起的特性(c99)“##”:将两个位于它两边的标识符连接在一起形成一个新的符号(当然,连接后的新标识符必须合法),它还允许宏定义从分离的文本片段创建标识符(示例如下)上面这段代码的结果是给sum1加上10
在文件的开头加条件编译指令: 代码块(函数声明,定义的宏,定义的类型……)1.概念:内存中字节的编号即为指针指向自定义类型的指针(eg:结构体指针……)
两个指向相同数组的指针相减的值是他们相差的元素个数,指针不可以相加。 我在这里举几个复杂声明的例子来说明问题(附解析)
这是一个什么东西!?别晕,听我讲。 我们来看,取arr的地址加1代表将数组的地址整个向后偏移一个数组长度,然后再强转成int * ,此时的地址类型由数组变成了整形,再赋值给ptr1。ptr-1,根据指针运算,减一就代表减一个类型的步长,它的类型是整形,也就是此时ptr1由指向arr数组后面那数组大小的地址的开始向前偏移一个整形大小,那么它此时就指向arr数组的末尾元素,解引用的结果就是10。第二个,(arr+1),从数组那里我们可以知道,数组名大多数情况下都代表数组首元素的地址,这里数组arr的首元素是一个一维数组,给它加一,就相当于偏移了一个一维数组的长度,此时它的地址就是数组arr第二个元素,对它解引用,翻译成另一种你容易理解的形式就是arr[1],而这代表第二个元素的地址,再将其转换成int *,输出的时候减一就相当于向前偏移一个整形的长度,所以第二个输出结果为5
c是一个指针数组,cp是一个二级指针数组,cpp是一个三级指针。第一个输出:先对cpp进行自加一,此时它指向cp[1],而cp[1]又指向c+2,所以对其两次解引用的结果是:POINT。第二个输出:cpp前面的操作符的优先级都高于加法操作符的优先级,所以我们先看前面,先对cpp自加1,此时它指向cp[2],对cpp一次解引用得到c+1,然后再自减1得到的是c,对其解引用再加3,它的输出是:ER。第三个输出:cpp[-2]存放的是c+3的地址,对其解引用得到的结果加三也就是:ST。第四个输出:cpp[-1][-1]的意思翻译成另一种形式就是 * ( *(cpp-1)-1 ),也就是c+1,然后对解引用结果再加1,所以输出结果就是:EW。
1.概念:将一堆类型相同的数据或者类型不相同的数据放在一(注意不能放函数,这里不同于c++)2.结构体大小(内存对齐->原因?方法?)说到结构体的大小,就不得不提一下内存对齐
内存对齐(性能原因):数据结构中内存需要对齐到自然边界上,原因在于由于CPU访问未对齐的内存需要两次访问,而访问对齐的内存只需要一次,提高了效率 2。其他成员需要对齐到对齐数的整数倍的地址处。(对其数:编译器默认的一个对齐数字和该成员本身大小的较小值< Linux下:4 Windows下:8>) 3。结构体的总大小等于成员最大对齐数的整数倍(包括第一个成员,这就是前面为什么是实际操作上) 4。如果是嵌套结构体的结构体求大小,内部结构体对齐到它成员的最大对齐数的整数倍处,而外部结构体的大小则是所有成员的最大对其数的整数倍(包括内部的结构体的最大对其数)。
当数值的低位段存储在内存的低地址处,这种计算机模式被称为小端模式,反之则被称为大端模式。(这里列举一种用联合体判断大小端的方法) 位段的声明和普通的结构类型相同,但是它的成员是一个或者多个bit位的字段。这些不同长度的字段实际上存储在一个或者多个整形变量中。(概念看不懂不要紧,往下看,最后学会位段的使用就行)
从它的概念中我们就可以看出,位段必须声明为int,signed int或unsigned int类型,其次,在成员的后面加上冒号,冒号后面为位段所占的位数。(这里我强调一下,最好将位段显式声明为signed /unsigned ,不要为了省事而只写一个int,因为只声明为一个int,它被解释为unsigned
还是signed事要根据编译器决定的!)
1.最大的好处在于,它可以节省存储空间,尤其是需要成千上万个这种结构的时候! 向函数传递结构参数的效率非常低下,通常可以采用传递指向结构的指针的方法 位段在本质上是不可移植的 联合在实现变体记录(内存中的某个特定区域在不同时刻具有不同类型的值)的时候非常有用 联合变量初始化的时候必须与第一个变量的类型相匹配 九、内存管理(分配的内存都在堆区)malloc的实现原理涉及到内存池的概念 所谓野指针即没有确定指向的指针,这时候若是对其解引用操作就会出现操作异常失败。野指针通常出现在使用free释放一块内存后,忘记将指针指向NULL,而后面却对其解引用操作。 注意每一段申请的内存在使用完毕后都要使用free来释放掉,否则,就会导致内存泄露的问题 进行内存分配的时候要对函数的返回值进行检查,eg: 这里要注意realloc的返回值有三种可能:其一为原内存后面空闲内存的大小足够新分配的内存,所以直接返回原内存的首地址;其二为原内存后的闲置内存不够分配,所以重新找了一块空间分配,而原来的内存则丢失掉;其三为新找了一块内存还放不下,所以返回NULL,而原来的内存而也会丢失掉,所以在使用realloc的时候,事先准备一个指针来接受新分配的内存,若是分配成功,则赋值给原来的指针,否则就报错,这样原来分配的内存就不至于丢失。
1.打开文件和关闭文件
r: 以只读的形式打开文件,文件必须存在
“关闭”函数说明:文件正常关闭时,fclose() 的返回值为0,如果返回非零值则表示有错误发生。 最后一定要记得对打开的文件使用fclose()进行关闭。
2.以字符形式进行文件操作 2.fgets函数不能用于读取二进制文件的内容!
什么是随机读写文件呢?顾名思义,就是可以在文件的任意位置进行文件读写。要实现这个操作的关键就在于怎样按照想法移动文件内部的位置指针,称为文件的定位 PS:这是作者的脑力劳动成果,希望广大网友转载可以注明出处 |
版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。