C语言的一些基础知识

这是在看了谭浩强的《C语言程序设计》的一些笔记,贴出来方面自己有空看看

1.void与void*在函数声明时的区别:

        void:是指该函数无返回值; 
        void*:是指该函数有返回值为指针类型,且该类型为空指针类型;

2.可执行文件生成的过程:
         先由编译程序将源文件(source file)编译生成目标文件(object file),
         再将目标文件与系统库函数及其他相关目标文件连接,生成可执行文件(execute file);

系统库函数内存放的都为相应函数的目标文件;


3.算法特点:
         有穷性(即有上下界,使程序最终能够停止运行);
         确定性(排除歧义);
         零个或多个输入;
         一个或多个输出(没有输出的算法是没有意义的);

         有效性(如除零运算);


4.算法的表示:
         流程图:传统流程图、顺序结构、选择结构、循环结构(当结构/直到结构)、N-S结构化流程图;
                 流程图主要用于表示一个算法,而设计算法时需要用到伪代码表示算法;

         伪代码:便于修改,但不直观; 


5.结构化程序设计方法: 
         基本思路:把复杂问题的分解过程分阶段进行,每个阶段的问题都控制在人们容易理解和处理的范围内;
  注意点:(1)在程序设计中,在划分模块时要求模块的”内聚性“强,于其它模块的”耦合性“弱;(2)需要把C程序中的函数做成一个封闭体,除了可以通过“实参—行参”的渠道与外界发生联系外,没有其他渠道,这样的程序移植性好,可读性强;
         基本步骤:(1)自顶而下;(2)逐步细化;(3)模块化设计;(4)结构化编码; 
         (1)(2):是将问题求解过程由抽象逐步具体化;即将抽象问题具体化,具体问题分阶段化;
          优势为便于验证算法的正确性;
          注意:在向下一层展开之前应仔细检查本层设计是否正确,只有在上一层是正确的基础上才能相下一层细化;
         (3)模块设计方法:是指写程序时,先根据程序功能将它分为若干个模块,若分的模块嫌大,还可以根据每个模块的功能
          ,再分为若干个子模块,这个过程就是自顶而下的方法实现;
          注意:子模块一般不超过50行,划分模块时需注意模块的独立性,即使用一个模块完成一项功能,耦合性越少越好,
          主要思想就是“分而治之”;

         (4)是指将已设计好的算法用计算机语言表示;


6.变量名与变量值的区别:
变量名:是指一个名字,代表内存中的一个地址;编译连接时由编译器给每个变量名分配对应的内存地址;

变量值:是指存放在某个内存单元内的数据,实际上从变量中取值是指通过变量名找到相应的内存地址,从该存储单元中读取数据;


7.数据在运算时会发生隐式转换,规则:
char/short -> int -> unsigned -> long ->double;

float -> double;;


8.强制转换的注意点:

(int)x,x为double类型:进行强制转换运算后得到一个int型的中间变量,它的值等于x的整数部分,而x的类型不变;


9.float类型和double类型:

在Visual C++ 6.0中,float数据类型有6位有效数字,而double数据类型有15位有效数字,超过相应有效数字位数被视为为无意义数字;


10.建议不要用全局变量的原因:
(1).全局变量在程序执行的全部时间内都占用存储单元;
(2).使函数的通用性降低,即函数在执行时需要依赖于外部变量,所以函数程序移植时,需要将外部变量也移植过去;
(3).使用全局变量过多,会降低程序的清晰性,因为每个函数都可以改变变量的值,程序容易出错;

(4).如果移植在同一个源文件全局变量和局部变量同名时,全局变量就会被屏蔽,不起作用;


11.指针和指针变量问题
指针:是指地址的形象化;一个变量的地址即为该变量的指针;
指针变量:是指用于存放变量指针(地址)的变量;
定义指针变量:基类型 * 指针变量名
例:int *pointer; “*”只表示该变量为指针变量;”int”为指针变量的基类型,表示所定义的变量为指向整形数据的指针变量数;一个指针变量只能指向同一类型的变量;

*pointer; “*”表示指针运算符,表示指针变量pointer指向的存储单元;


12.变量的存储类别:
(1).按变量的生存期可以将存储方式分为静态存储方式和动态存储方式;静态存储方式为在程序执行过程中给变量分配固定的存储空间的方式;而动态存储方式是指在程序执行过程中动态的分配变量的存储空间;
(2).变量的存储类别可分为:静态存储类、动态存储类;
(3).从作用域角度可将变量分为:局部变量和全局变量;
局部变量包括自动变量(程序运行到本函数时每次初始化,离开函数,值就消失)、静态局部变量(编译时就已经分配空间但只能在执行到本函数时才能被访问,离开函数,值还存在)、寄存器变量(程序运行到本函数时每次初始化,离开函数,值就消失)、形式参数(调用函数时分配空间,可以为自动变量和寄存器变量);
全局变量包括静态外部变量(只限于本文件中使用)、外部变量(即非静态变量,可被其他文件使用);
(4).从生存期角度可将变量分为动态存储和静态存储;
动态存储(本函数内有效):自动变量、寄存器变量、形式参数、调用函数时的现场保护和返回地址;
静态存储:静态局部变量(只函数内有效)、静态外部变量(只本文件中有效)和外部变量(可被其他文件使用);
(5).从变量存放的位置可分为:
内存中动态存储区:自动变量、形式参数;
内存中静态存储区:   静态局部变量、静态外部变量、外部变量;

CPU的寄存器中:寄存器变量;


13.定义与声明:
(1).对于函数来说,声明与定义非常明显,声明只对函数名,形参类型、返回值类型进行说明;而定义还包括函数的具体功能,有函数体;

(2).对于变量来说,如”int a=1;”为定义性声明,如”extern a;”为引用性声明,定义性声明简称定义;而狭义的声明即引用性声明简称为声明;


14. C语言中数组名只是一个标号,没有实际存储空间,用它来表示数组首元素的地址;

int a[10],a为标号,表示元素a[0]的地址,int *p = &a[0]等价于int *p=a;


15.C语言规定:
(1)如果指针变量p指向数组中的某个元素,则p+1指向同一数组的下一个元素,而不是将p的值简单的加1;

(2)形参数组名实际上是一个指针变量,并不是真正地开辟一个数组;而定义实参数组时必须指定数组大小,因为要开辟相应的存储空间;


16.二维数组指针理解 :
二维数组可看成每个元素都是一维数组的一维数组;如int a[3][4],可看成一维数组a,即包括a[0],a[1],a[2]三个元素,每个元素为一维数组;则a[2][1]的地址为*(a+2)+1或a[2]+1,其值为*(*(a+2)+1)或*(a[2]+1);所以二维数组元素a[i][j]的可表示为*(a[i]+j)或*(*(a+i)+j);
a为二维数组名,指向一维数组a[0],即0行首地址;a[0]为0行0列元素地址;a与a[0]的值虽然相同,但是含义不同即指针类型不同,a是指向一维数组即指向行的指针,a[0]是指向a[0][0]元素即指向列的指针,因而对指针加1运算得到的结果也不相同;

(1)在二维数组中a+i与*(a+i)的值相等,但是含义不同,a+i是二维数组中序号为i的行的首地址(序号从0算起),而*(a+i)并不是a+i单元的内容(值),因为a+i并不是一个变量的存储单元,因为并不实际存在a[i]这样的变量;(2) a+i是指向行的指针,*(a+i)是指向列的指针,指向行的指针可以通过前面加*,转换成指向列的指针,而指向列的指针可以通过在前面加&,转换成指向行的指针,*(a+i)->a[i]//&a[i]->&*(a+i)->a+i;(3)a[i],*(a+i),&a[i],&a[i][0]的值时相等的,但含义不同,也是不同类型的指针变量;


17.多维数组指针理解:
(1).指向多维数组元素的指针变量;如int a[3][4];int *p=a[0];即p指向二维数组a第0行0列元素;
(2).指向由m个元素组成的一维数组的指针变量int (*p)[m];如int a[3][4];int (*p)[4];p=a;即p指向二维数组第0行;int (*p)[m]表示p为一个指针变量,它指向包括m个元素的一维数组,也就是p所指的对象是有m个整型元素的数组,即p是指向一维数组的指针,p不能指向一维数组的某一元素,但可通过(*p)[0]~(*p)[m-1]来访问该数组中的元素;

(3).指向数组的指针做函数的参数方法:用指向变量的指针;用指向一维数组的指针;


18.指向函数的指针
(1).一个函数在编译时,会被分配一个入口地址,该函数的函数名即代表该函数的入口地址,它类似于数组中的数组名,只是一个标示,没有实际的存储单元;
(2).每个函数都占用一段内存,它们有一个入口地址,可以通过一个指针变量指向一个函数(即该变量保存该函数的入口地址),则可通过指针来访问它指向的函数;
(3).函数指针的定义:数据类型  (*指针变量名)  (函数参数表列)
int (*p)(int), *p两侧的括号不能省略,表示p先与*结合,是指针变量,再与后面的()结合,表示该指针变量指向函数,这个函数的返回值是整型的,且函数有一个int型的形参;
int *p(int)为指针函数,p先与()结合,表示声明一个函数,p是函数的函数名,代表该函数的入口地址,函数的返回值为指向整型的指针变量;
(4).用函数指针变量调用函数时,只需将*(指针变量名)代替原来的函数名即可;

(5).指向函数的指针也可以做函数的参数,以实现函数地址的传递,使在被调函数能过调用实参函数;这个主要用在被调函数中调用的函数不确定,而被调函数的函数体不变的情况;


19.指针数组和指向指针的指针理解:
(1).指针数组定义:类型名 * 数组名 [数组长度];
int *p[4];由于[]的优先级较高,p先与[4]结合,形成p[4]形式,即为数组形式,共有4个元素,再与*结合,*表示此数组为指针类型;
指针数组比较适合用来指向若干个字符串;通过指针数组的元素分别指向各字符串,想对字符串排序,不必改动字符串的位置,只需改动指针数组中各元素的指向即可;
(2).指向指针的指针是指:指向指针数据的指针变量;如char **p;**p相当于*(*p),*p是指针变量的定义,前面又有一个*表示指针p是指向一个字符指针的指针变量;通常将char **p和char *op[m]一起使用,将p指向op指针数组的元素;

(3).main函数中含参数形式:int main(int argc,char * argv[]);其形参通过命令行的方式传入;形参argc是指命令行中参数的个数(包括文件名),形参argv是一个指向字符串的指针数组;


20.空指针和野指针的区别:
(1).空指针是一个特殊的指针值,也是唯一一个对任何指针类型都合法的指针值。指针变量具有空指针值,表示它当时处于闲置状态,没有指向有意义的东西。空指针用0表示,C语言保证这个值不会是任何对象的地址。给指针值赋零则使它不再指向任何有意义的东西。为了提高程序的可读性,标准库定义了一个与0等价的符号常量NULL.    程序里可以写 p = 0;     或者 p = NULL; 两种写法都把p置为空指针值;
(2).野指针,也就是指向不可用内存区域的指针。通常对这种指针进行操作的话,将会使程序发生不可预知的错误。 “野指针”不是NULL指针,是指向“垃圾”内存的指针。人们一般不会错用NULL指针,因为用if语句很容易判断。但是“野指针”是很危险的,if语句对它不起作用;野指针错误成因: a.指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存;
b.指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。别看free和delete的名字恶狠狠的(尤其是delete),它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。通常会用语句if(p !=NULL)进行防错处理。很遗憾,此时if语句起不到防错作用,因为即便p不是NULL指针,它也不指向合法的内存块;
例:char *p = (char *) malloc(100); 
strcpy(p, “hello”);
free(p);//p所指的内存被释放,但是p所指的地址仍然不变
if(p != NULL)没有起到防错作用 

strcpy(p, “world”); // 出错 


21.位运算基础:
(1).位预算符:”~”取反运算符,”&”按位与,”|”按位或,”^”按位异或;
(2).特殊用途:
按位与”&”:a.清零,用原操作数和将其取反做”与”运算,例:b &= ~b;
b.取一个数中某些指定位,即让该数和要取的位为1不要位为0的数做”与”运算,例:取char c中的后4个位,可将c  &= 017(00001111);
c.想要取某数n位上的数,只要就该数与另外一数做&运算,另外一数在该位处为1;
按位或”|”:a.一般用于将某个数的某些位置1;
异或”^”:    a.使特定位翻转(相应位于1做异或);
b.与0相^异或,保留原值;
c.交换两个数的值,不用临时变量:有数a,b,通过a = a ^ b; b = b ^ a ; a = a ^ b;三个语句将a,b值交换;第二句b = b ^ a = b ^ (a ^ b) = b ^ a ^ b = a ^ b ^ b = a ^ (b ^ b) = a ^ 0 = a;第三句a = a ^ b =(a ^ b)^a = a ^ b ^ a = b ^ a ^a = b ^ 0 = b;
取反”~”:优先级高于算术运算符;
取一指定位时可采用取反来实现,如取a中第n位,a &=  ~(1<<n);
左移”<<”:
左移运算是指将某数的二进制数往左偏移,二进制数右侧补零,左侧溢出位舍弃;左移n位,相当于将某数乘以2的n次方(在没有溢出情况),a <<= n,a = a %pow(2,32),即包括左侧最高位溢出情况;
右移”>>”:
右移预算是指将某数的二进制数往右偏移,二进制数右侧溢出位舍弃,对于左边移进位分情况讨论:a.对于无符号数,二进制数左侧移进位为0;

b.a为有符号数,且最高位为0,则二进制数右侧移进0;c.a为有符号数,且最高位为1(负数),则右侧移进位为0还是1,要看计算机系统,移入0的为逻辑右移,移入1的为算术右移;


22.不同长度的数据进行位预算时:

     如果两个不同长度的数进行位运算(如a为short int型,b为int型,如a&b),系统会自动将两者按右端对齐,若a为正数,则补齐16位0,;若a为负数,则补齐16位1,;若a为无符号整型,则补齐16位0;


23.位运算举例:
(1).取一个数a,从右端开始的4~7位:b = (a >> 4)&((1U<<4)-1)或者b = (a >> 4) &~(~0 << 4);
(2)循环移位:假设a为int型
对a进行右循环移n位设计: b = a << 32-n ; c = (a >> n)&((1U << 32-n)-1); a = b | c;
 b = a << 32-n; c = (a >> n) &~(~0 << 32-n);a = b |c ;
对a进行左循环移n位设计: b = (a >> 32-n)&((1U << n)-1);c = a << n; a = b|c;

 B = (a >> 32-n)&~(~0 << n); c = a << n;a = b | c;


24.字符串长度和字符数组长度
(1) 字符串结束标志’\0’,在’\0’之前的字符为有效字符;
(2) 字符串常量会在字符串末尾自动加一个’\0’作为结束符;
(3) 字符串长度是指字符数组中有效的字符个数,不包括’\0’;
(4) 字符数组的长度是指字符串占用的存储空间大小;

例:char x[] = “hello”; char y[] = {‘h’,’e’,’l’,’l’,’o’};x数组的大小大于y数组的大小,因为字符串常量系统会在末尾自动加上’\0’;


25.循环语句的效率:
建议1:在多重循环下,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少CPU跨切循环的次数;
建议2:如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断放在循环体外面;否则程序将多执行N-1次逻辑判断,并且每次进行逻辑判断都会打断循环的“流水线”作业,使得编译器不能对循环进行优化处理,降低了效率;
26.for语句的循环控制变量:
规则1: 不可在for循环体内修改循环变量,防止for循环失去控制;

建议1: 建议for语句的循环控制变量的取值采用“半开半闭区间”写法;


27.switch语句:
存在原因:switch为多分支选择语句,而if语句只有两个分支,用if语句对于多分支语句程序冗长难懂;
规则1:每个case语句的结尾不要忘了加break,否则将导致多个分支重叠(除非有意使多个分支重叠);

规则2:不要忘记最后那个default分支。即使程序真的不需要default处理,也要加上default:break语句;


28.常量:
规则1:尽量使用含义直观的常量来表示哪些将在程序中多次出现的数字或字符串;
#define   MAX    100          ///        const    int      MAX   =  100;
Const 与 #define 的比较:
(1). const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误(边际效应);
(2). 有些集成化的调试工具可以对const常量进行调试,但是不能对宏进行调试;
规则2:在C++程序中只使用const常量而不使用宏常量,即const常量完全取代宏常量;
规则3:需要对外公开的常量放在头文件中,不需要对外公开的常量放在定义文件的头部;

规则4:如果某一常量与其它常量密切相关,应在定义中包含这种关系,而不应给出一些孤立的值;


29.类中的常量:
(1).用const修饰的数据成员只在本对象内是常量,但对于类而言却是可变的,因为类可以创建多个对象,不同对象其const数据成员的值可以不同;
(2).不能在类声明中初始化const数据成员,只能通过类构造函数的参数初始化表来进行初始化;
(3).可以在类中通过枚举常量来实现类的常量,枚举常量不会占用对象的存储空间,它们在编译时被全部求值,枚举常量的缺点是:它的隐含数据类型是整型,其最大值有限,且不能表示浮点数;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值