指针、内存管理、预处理、结构体

1#definetypedef的区别

typedefC语言语句,其功能是用户为已有数据类型取别名

关键字typedef在编译阶段有效,由于是在编译阶段,因此typedef有类型检查的功能。

define则是宏定义,发生在预处理阶段,也就是编译之前,它只进行简单而机无脑的字符串替换,而不进行任何检查。Typedef用来定义类型的别名,这些类型不只包含内部类型(intchar等),还包括自定义类型(如struct),可以起到使类型易于记忆的功能。

 

2、内存对齐

分配的首地址可以被该变量大小整除。如果默认是4字节对齐,那double这样超过4字节的,还是按4字节算。

    总大小必须为结构体中类型宽度最大的数据成员的宽度的整数倍。一样,如果默认是4字节对齐,那double这样超过4字节的,还是按4字节算。

也就是算出来是6,但是你最大的int4,那么就要补成8。也就是后面空着的两位也算你的。

结构体A中有结构体B的成员,A的成员按结构体B中类型宽度最大的数据成员对齐。

 

3、结构体如何定义

结构体是一种集合,它里面包含了多个变量或数组,它们的类型可以相同,也可以不同,每个这样的变量或数组都称为结构体的成员(Member)。请看下面的一个例子:

struct stu{

    char *name;  //姓名

    int num;  //学号

    int age;  //年龄

    char group;  //所在学习小组

    float score;  //成绩

};

stu 为结构体名,它包含了 5 个成员,分别是 namenumagegroupscore。结构体成员的定义方式与变量和数组的定义方式相同,只是不能初始化。

结构体也是一种数据类型,它由程序员自己定义,可以包含多个其他类型的数据。

既然结构体是一种数据类型,那么就可以用它来定义变量。例如:

struct stu stu1, stu2;

定义了两个变量 stu1 stu2,它们都是stu 类型,都由 5 个成员组成。注意关键字struct不能少。

 

stu 就像一个模板,定义出来的变量都具有相同的性质。也可以将结构体比作图纸,将结构体变量比作零件,根据同一张图纸生产出来的零件的特性都是一样的。

我们也可以在定义结构体的同时定义结构体变量:

struct stu{

    char *name;  //姓名

    int num;  //学号

    int age;  //年龄

    char group;  //所在学习小组

    float score;  //成绩

} stu1, stu2;

 

4、结构体和联合体的区别

struct 中所有成员同时都在struct中,它们都有属于自己的空间。struct变量的长度是所有成员长度的总和。union 中只能同时存在一个成员,所有成员不能同时占用内存的空间。所以为了满足所以成员的要求,union的长度是长度最长的成员的长度。

 

5、位段

   

位段是指信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有01 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为位段。所谓位段是把一个字节中的二进位划分为几个不同的区域, 并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。位域变量的声明与结构变量声明的方式相同。如:

Struct sample{

  int a:7;        //类型说明符位域名:位段长度a 7

  int b:2;        // b 2

  int c:6;        // c 6

}data;

 

6、宏中"#""##的用法

###两种操作。#用来把参数转换成字符串,##则用来连接两个前后两个参数,把它们变成一个字符串。

我们使用#把宏参数变为一个字符串,##把两个宏参数贴合在一起.  用法:

using namespace std;

#define STR(s)     #s 

#define

CONS(a,b) int(a##e##b) 

int main()

{  

printf(STR(vck));           // 输出字符串

"vck" 

printf("%d", CONS(2,3));    // 2e3输出:2000 

return 0; 

} 

 

## 是连接符号,由两个井号组成,其功能是在带参数的宏定义中将两个子串(token)联接起来,从而形成一个新的子串。但它不可以是第一个或者最后一个子串。所谓的子串(token)就是指编译器能够识别的最小语法单元。具体的定义在编译原理里有详尽的解释。

#符是把传递过来的参数当成字符串进行替代。

假设程序中已经定义了这样一个带参数的宏:

#define PRINT( n ) printf("token" #n " = %d", token##n )

同时又定义了二个整形变量:

int token9 = 9;

现在在主程序中以下面的方式调用这个宏:

PRINT( 9 );

那么在编译时,上面的这句话被扩展为:

printf( "token" "9"" = %d", token9 );

注意到在这个例子中,PRINT(9);中的这个”9”被原封不动的当成了一个字符串,与”token”连接在了一起,从而成为了token9。而#n也被”9”所替代。

可想而知,上面程序运行的结果就是在屏幕上打印出token9=9

 

7、堆空间

对指针赋施操作时,需要辨清是操作为指针变量本身分配的内存(32 bit下为4 bytes)还是操作指针指向的内存块。所谓,指针所指向的内存是指此块内存的首地址值是指针的值,也是系统为指针变量分配内存内的值。

    对前者进行操作如对指针赋值,这种操作要保证所赋的值所代表的内存块可用。在内存块可用的情况下定要保证内存块经初始化后再使用,若内存块不可用则就造就了野指针。

    对后者进行操作时,若指针原本就是一野指针(即指针所指的内存块不可用),则应将指针赋值为NULL以避免使用野指针,消除野指针的潜在危险。若指针所指内存块可用,一定要认清此内存块,不要有越界的操作。

另一个方面,指针若是指向堆内存块,则一定要让堆内存块得到准确的释放。

 

8、数组指针的使用

数组指针指向的是数组, 以上面定义为例, 数组指针使用如下, int *p[3] = a; p就指向了a这个数组,[3]代表了指向数组的成员的个数, 也就是说对p进行+ -等操作, 都是以3个成员的大小进行的,在某个程度上, 它与数组名一样, 使用具体元素, 也需要进行* 或者[]操作,选定具体的数组然后进行操作。

 

9、指针数组的使用

首先他是一个数组,数组的元素是指针。

返回值也为指针,(*P[ ]

         INT *P[25]

           INT I[10][25]

            P=I

           *P++//p指向了i[1]

           *P[12]=12//I[1][12]赋值。

指针数组和数组指针的区别就在与此,归跟结底就是运算符优先级的问题。如I[10[20]10就是优先级别最高的。优先级对优先级的对应关系看来这些定义就迎刃而解了。

 

10、指针函数的使用

函数指针的赋值操作:使用函数名给指向函数的指针变量赋值。其赋值的一般格式如下:函数指针 = [ &] 函数名;其中,函数名后不能带括号和参数,函数名前的&是可选,建议不要使用。

函数指针调用格式:函数指针变量([实参列表]); (*函数指针变量)([实参列表]);

推荐第二种用法。这种方法可以很好的表明这是一个函数。而第一种方法则很容易造成误导。

之所以容忍一种调用方法是因为ANSI C 委员会决定容许这种普通函数调用句法。这是因为编译器知道它是一个指向函数的指针,并且它还知道在该环境下所能做的惟一的一件事就是调用函数,因此这里没有任何模糊不清的表达。

使用场景:函数指针的常见用途就是把函数指针作为参数传递给函数。

一个函数通过由运行时决定的指针来调用另一个函数的行为叫做回调(callback)。用户将一个函数指针作为参数传递给其它函数,后者将回调用户的函数。这样就可实现通过同一接口实现对不同类型数据、不同功能的处理。

 

猜你喜欢

转载自blog.csdn.net/canvas_kin/article/details/79200719