[C++系列] C语言基础 day14 数据对齐和补齐,枚举和联合,二级指针,函数指针,回调函数
in C/C++ with 1 comment

[C++系列] C语言基础 day14 数据对齐和补齐,枚举和联合,二级指针,函数指针,回调函数

in C/C++ with 1 comment

#存储区数据对齐

一个存储区的地址必须是它自身大小的整数倍(double类型的存储区的地址只需要是4的整数倍)
这个规则叫数据对齐。

结构体里的子存储区通常需要遵守数据对齐的规则。

数据对齐可能导致结构体不同子存储区之间有空隙

结构体存储区的大小必须是它所包含的占地最大的基本类型存储区大小的整数倍。(如果这个基本类型子存储区的类型是double,则结构体的大小只需要是4的整数倍。)
这个规则叫数据补齐

数据补齐可能造成结构体最后有一些浪费的字节。

结构体内子存储区顺序不同,所占的存储区大小可能不同(造成浪费)

数据对齐并不是操作系统的内存结构的一部分,而是C P U结构的一部分。

当C P U访问正确对齐的数据时,它的运行效率最高。数据对齐是为了读取数据的效率

#include<stdio.h>
typedef struct{
    char buf[2];
    int num;
    char ch1;
}tmp;
typedef struct{
    double num;
    int num1;
}tmp2;
int main(){
printf("%d\n",sizeof(tmp));
return 0;
}

#枚举

枚举也可以用来创建新的数据类型
枚举类型存储区就是整数类型存储区,这种类型的存储区使用的时候只能往里面放有限的几个整数。

枚举类型需要先声明再使用。
声明枚举类型需要使用enum关键字
声明枚举类型的时候需要提供一组名称,计算机为每个名称分配一个对应的整数。只有这些整数才可以存放在枚举类型的存储区里。
声明不同枚举类型时提供的名称个数不同所以不同枚举类型存储区里所能存放的数字范围不同。
计算机为第一个名称分配的整数是0,向后依次递增。

可以直接在程序中把名称当作整数使用。
可以在声明枚举类型的时候指定某个名称对应的整数。这个时候后面的名称对应的整数也应该发生变化。

/*
 * 枚举演示
 * */
#include <stdio.h>
int main() {
    enum /*season*/ {CHUN, XIA = 5, QIU, DONG};
    printf("QIU是%d\n", QIU);
    return 0;
}

#联合

联合也可以用来创建新的数据类型。
联合也需要先声明再使用。
声明联合的时候需要使用union关键字。

联合的所有子存储区在内存中互相重叠。
联合的所有子存储区的开始地址都一样。
联合类型的存储区可以当作多种不同类型的存储区使用,每个成员变量代表了一种可能的类型。

联合存储区的大小就是最大成员变量的大小。

#include<stdio.h>
typedef union{
    int num;
    float fnum;
}tmp;
int main(){
    tmp tmp1;
    printf("%p\n",&(tmp1.num));
    printf("%p\n",&(tmp1.fnum));
return 0;
}

#二级指针

用来记录普通变量地址的指针叫做一级指针。
二级指针用来记录一级指针的地址。
声明二级指针的时候需要写两个*

二级指针变量名称前加**表示捆绑的普通变量存储区。

#include<stdio.h>
int main(){
    int num = 0;
    int *p_num = &num;
    int **pp_num = &p_num;
    **pp_num = 10;
    printf("%d\n",num);
    printf("p_num is %p\n",p_num);
    printf("*pp_num is %p\n",*pp_num);
    return 0;
}

二级指针变量名称前加*可以表示一级指针存储区。

二级指针可以用来代表指针数组

#include<stdio.h>

int main(int argc,char **argv){
    int num;
    for(num =0;num <= argc-2;num++){
            printf("%s\n",*argv+num);

    }
return 0;
}
int num;
char ch =0;
int *p_num = &num;
void *p_v = NULL;
*(char *)p_v;
p_v = &p_num;  //无类型指针是可以和一级指针地址捆绑的
**(int **)p_v;  //使用时需要把无类型指针转换为二级指针

无类型指针有可能记录一级指针存储区的地址,这个时候需要先把无类型指针强制转换成二级指针然后才能使用。

#include<stdio.h>
int  main(){
    int num =0;
    int *p_num = &num;
    void *p_v = &p_num;
      **(int **)p_v = 10;
     printf("num is %d \n",num);
}  //结果   num is 10

无类型指针通常作为函数参数。

#二级指针地使用

程序里通常把二级指针当作形式参数使用。
被调用函数可以通过二级指针形式参数把一个地址传递给调用函数。

* 编写一个函数,这个函数将两个水平长方形中面积较大地传递给调用函数。
* 
#include<stdio.h>
typedef struct {
    int row;
    int col;
}pt;
typedef struct{
    pt pt1;
    pt pt2;
}rect;
int area(const rect *p_r){
        int ret = (p_r->pt1.row - p_r->pt2.row)*(p_r->pt1.col-p_r->pt2.col);
        return ret >= 0 ? ret : 0 - ret;
}
rect *max(const rect *p_r1,const rect *p_r2){

        return (rect *)(area(p_r1) >= area(p_r2) ? p_r1 : p_r2);       
}
void max1(const rect *p_r1,const rect *p_r2,rect **pp_r){ //这个函数不能使用返回值
        *pp_r=(rect *)(area(p_r1) >= area(p_r2) ? p_r1 : p_r2);
}
int main(){
    rect r1,r2 ,*p_rect;
    printf("输入一个水平长方形的位置:");
    scanf("%d%d%d%d",&(r1.pt1.row),&(r1.pt1.col),&(r1.pt2.row),&(r2.pt2.col));
    printf("再输入一个水平长方形:");
    scanf("%d%d%d%d",&(r2.pt1.row),&(r2.pt1.col),&(r2.pt2.row),&(r2.pt2.col));
    p_rect = max(&r1,&r2);
    printf("结果是(%d,%d),(%d,%d)\n",p_rect->pt1.row,p_rect->pt1.col,p_rect->pt2.row,p_rect->pt2.col);
    return 0;
}

#练习

编写函数从两个圆里找到面积比较大地圆并把它传递给调用函数,函数没有返回值。

#include<stdio.h>
typedef struct{
    int row;
    int col;
}point;
typedef struct{
    point pt;
    int bj;
}yuan;
void max(const yuan *p_y1,const yuan *p_y2,yuan **p_yuan){
    *p_yuan =(yuan *)(p_y1->bj > p_y2->bj ? p_y1 : p_y2);
}
int main(){
 yuan yu1,yu2,*p_y;
printf("输入圆心和半径:");
scanf("%d%d%d",&(yu1.pt.row),&(yu1.pt.col),&(yu1.bj));
printf("输入圆心和半径:");
scanf("%d%d%d",&(yu2.pt.row),&(yu2.pt.col),&(yu2.bj));
max(&yu1,&yu2,&p_y);
printf("半径为%d的面积大\n",p_y->bj);
}

#函数指针

在C语言里函数也有地址
函数名称可以表示函数地地址
函数指针用来记录函数地地址
函数指针也需要先声明后使用

函数指针声明可以根据函数声明变化得到。

函数指针也分类型,不同类型的函数指针适合不同类型的函数捆绑。

## 回调函数

/*
 *回调函数
 **/
#include<stdio.h>
void print(int *p_num,int size){
    int num =0;
    for(num =0;num<=size-1;num++){
        printf("%d ",*(p_num+num));
    }
    printf("\n");
}
void print_cb(int *p_num){//负责对单个存储区处理。
    printf("%d ",*p_num);

}
void for_each(int *p_num,int size,void (*p_func)(int *)){
    int num =0;
    for(num=0;num <=size-1;num++){
         p_func(p_num+num);
    }
}
int main(){
    int arr[] = {1,2,3,4,5};
    for_each(arr,5,print_cb);
    //print(arr,5);
    printf("\n");
    return 0;
}

函数指针可以作为形式参数使用。
可以作为实际参数使用的函数叫做回调函数。

#练习

使用for_each函数把数组里每个存储区的内容变成相反数。

#include<stdio.h>
void reverse(int *p_num){
    *p_num = 0-*p_num;
}
void for_each(int *p_num,int size,void (*p_func)(int *)){
    int i =0;
    for(i=0;i<=size-1;i++){
        p_func(p_num+i);
    }
}
void print_cb(int *p_num){
    printf("%d ",*p_num);
}
int main(){
    int arr[]={1,2,3,-4,-5};
    for_each(arr,5,reverse);
    for_each(arr,5,print_cb);
    printf("\n");
    return 0;
}

#预习

* 动态内存分配
* 文件操作
Responses
  1. http://geek.csdn.net/news/detail/231592

    Reply