指针高级
指针高级
练习:编写程序得到一个长方形,并把面积求出来(使用结构体),以及打印中心点坐标
#if 0
/*
*结构体练习
*
* */
#include<stdio.h>
typedef struct {
int row;
int col;
} pt;
typedef struct {
pt pt1;
pt pt2;
} rect;
int main (){
int area = 0;
rect r = {0},*p_r = &r;
printf("请输入水平长方形的位置:");
/*scanf("%d%d%d%d",&(r.pt1.row),&(r.pt1.col),&(r.pt2.row),&(r.pt2.col));
area = (r.pt1.col - r.pt2.col) * (r.pt1.row - r.pt2.row);
*/
scanf("%d%d%d%d",&(p_r->pt1.row),&(p_r->pt1.col),&(p_r->pt2.row),&(p_r->pt2.col));
area = (p_r->pt1.col - p_r->pt2.col) * (p_r->pt1.row - p_r->pt2.row);
area = area >= 0 ? area : 0 - area;
printf("%d\n",area);
}
#elif 0
/*
*计算长方形中心点
*
* */
#include<stdio.h>
typedef struct {
int row;
int col;
} pt;
typedef struct {
pt pt1;
pt pt2;
} rect;
int main (){
int left = 0,right = 0;
rect r ,*p_r = &r;
printf("请输入水平长方形的位置:");
scanf("%d%d%d%d",&(p_r->pt1.row),&(p_r->pt1.col),&(p_r->pt2.row),&(p_r->pt2.col));
left = (p_r->pt1.col + p_r->pt2.col) / 2;
right = (p_r->pt1.row + p_r->pt2.row) / 2;
printf("中心点位置为( %d , %d )",left,right);
return 0;
}
#endif
结构体(续)
可以使用结构体类型的形式参数从调用函数向被调用函数传递结构体数据,采用这种方法从调用函数传递结构体数据会造成时间和空间的浪费。
采用结构体指针作为形式参数可以避免这种浪费,声明结构体指针形式参数的时候尽量使用const关键字。可以把结构体变量作为返回值使用把结构体数据传递给调用函数,这个时候被调用函数需要提供一个结构体类型存储区用来存放返回值。
这样也会造成时间和空间的浪费,使用结构体存储区的地址作为返回值可以避免这种浪费(这个时候被调用函数需要提供一个结构体指针类型的存储区用来存放作为返回值的地址),不可以把非静态局部结构体类型存储区的地址当作返回值使用。
练习:编写两个函数,一个函数可以根据水平长方形的位置计算他的面积并把结果传递给调用函数,另外一个函数可以根据水平长方形的位置计算它中心点的位置并把结果传递给调用函数
#if 0
/*
*结构体
*
* */
#include<stdio.h>
typedef struct {
int row;
int col;
} pt;
/*void print (pt pt1){
printf("点的位置(%d,%d)\n",pt1.row,pt1.col);
}*/
void print (const pt *p_pt){
printf("点的位置(%d,%d)",p_pt->row,p_pt->col);
}
/*pt read (void){
pt pt1 = {0};
printf("请输入一个坐标:");
scanf("%d%d",&(pt1.row),&(pt1.col));
return pt1;
}*/
/*pt *read (void){
static pt pt1 = {0};
printf("请输入一个坐标:");
scanf("%d%d",&(pt1.row),&(pt1.col));
return &pt1;
}*/
pt *read (pt *p_pt){
printf("请输入一个坐标:");
scanf("%d%d",&(p_pt->row),&(p_pt->col));
return p_pt;
}
int main (){
pt pt1 = {0},*p_pt = NULL;
/*printf("请输入一个坐标:");
scanf("%d%d",&(pt1.row),&(pt1.col));
*/
p_pt = read(&pt1);
print(p_pt);
print(&pt1);
return 0;
}
#elif 1
/*
*
*结构体练习
*
* */
#include<stdio.h>
typedef struct {
int row;
int col;
} pt;
typedef struct {
pt pt1;
pt pt2;
} rect;
rect *read (const rect *p_rect){
printf("请输入水平长方形的位置:");
scanf ("%d%d%d%d",&(p_rect->pt1.row),&(p_rect->pt1.col),&(p_rect->pt2.row),&(p_rect->pt2.col));
return p_rect;
}
pt *midpt(const rect *p_r,pt *p_mid) {
p_mid->row = (p_r->pt1.row + p_r->pt2.row) / 2;
p_mid->col = (p_r->pt1.col + p_r->pt2.col) / 2;
return p_mid;
}
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);
}
int main (){
int val = 0;
rect r = {0},*p_r = NULL;
pt mid = {0},*p_mid = NULL;
p_r = read(&r);
val = area(p_r);
printf("面积是:%d\n",val);
val = area(&r);
printf("面积是:%d\n",val);
p_mid = midpt(p_r,&p_mid);
printf("中心点位置为(%d,%d)\n",p_mid->row,p_mid->col);
printf("中心点位置为(%d,%d)\n",mid.row,mid.col);
return 0;
}
#endif
数据对齐和补齐
一个存储区的地址必须是他自身大小的整数倍(double类型的存储区地址只需要是4的整数倍),这个规则叫做数据对齐,结构体的子存储区通常也需要遵守数据对齐的规则,结构体的子存储区之间可能存在空隙
/*
*
*数据对齐和补齐
*
* */
#include<stdio.h>
typedef struct {
char buf[2];
int num;
} tmp;
typedef struct {
char ch1;
int num;
char ch2;
} tmp1;
int main (){
printf("%ld\n",sizeof(tmp));
printf("%ld\n",sizeof(tmp1));
return 0;
}
结构体存储区的大小必须是它所包含的占地最大的基本类型子存储区大小的整数倍(如果占地最大基本类型子存储区的类型是double则结构体存储区的大小只需要是4的整数倍),这个规则叫数据补齐,数据补齐可能造成结构体存储区的最后有多余的字节。
枚举
枚举也可以用来创建新的数据类型,枚举类型的存储区就是整数类型的存储区,只不过这种类型的存储区只能用来记录有限的几个整数,枚举类型也需要先声明然后才能使用,声明枚举类型的时候需要使用enum关键字,声明枚举类型的时候需要提供一组名称,计算机为每个名称分配一个对应的整数。
枚举类型存储区使用的时候只能用来记录这些整数,不同枚举类型包含的整数范围不同,计算机把从0开始的非负数依次分配给枚举类型里的名称,可以在声明枚举类型的时候规定某个名称所分配的整数是多少(这个时候后面每个名称对应的整数都会随着改变)。
/*
*枚举
*
* */
#include<stdio.h>
int main (){
enum {CHUN,XIA = 5,QIU,DONG};
printf("QIU = %d\n",QIU);
return 0;
}
联合
联合也可以用来创建数据类型,联合也需要先声明然后才可以使用,声明联合的时候需要使用union关键字,联合成员变量对应的存储区是互相重叠的。
联合的所有子存储区开始地址一样,联合存储区的大小就是最大存储区的大小,联合类型的存储区可以当作多种不同类型的存储区使用,每个成员变量代表了一种可能的类型。
/*
*联合
*
* */
#include<stdio.h>
typedef union /*tmp*/{
int num;
float fnum;
}tmp;
int main (){
tmp utmp = {0};
printf("%p\n",&(utmp.num));
printf("%p\n",&(utmp.fnum));
printf("%ld\n",sizeof(tmp));
return 0;
}
二级指针
用来记录普通类型存储区地址的指针叫一级指针,二级指针用来记录一级指针的地址,声明二级指针的时候需要写******,二级指针前面加 ** 表示它捆绑的普通类型存储区或里面的数字,二级指针前加一个 * 表示它捆绑的一级指针存储区或里面的地址数据,把指针数组里第一个指针类型存储区的地址记录到二级指针里就可以通过这个二级指针找到指针数组里的每个指针类型存储区,这个时候可以使用二级指针代表整个指针数组。
#if 0
/*
*二级指针
*
* */
#include<stdio.h>
int main (){
int num = 0;
int *p_num = #
int **pp_num = &p_num;
**pp_num = 10;
printf("num = %d\n",num);
*pp_num = NULL;
printf("p_num = %p\n",p_num);
return 0;
}
#elif 0
/*
*
*二级指针
*
* */
#include<stdio.h>
int main (int argc,char **argv){
int num = 0;
for (num = 0;num <= argc - 1;num++){
printf("%s\n",*(argv + num));
}
return 0;
}
#elif 0
/*
*
* 二级指针演示
*
*
* */
#include<stdio.h>
int main (){
char ch = 'y';
char *p_ch = &ch;
int num = 27;
int *p_num = #
float fnum = 3.14f;
float *p_fnum = &fnum;
void *p_v = &p_ch;
printf("%c\n",**(char **)p_v);
p_v = &p_num;
printf("%d\n",**(int **)p_v);
p_v = &p_fnum;
printf("%g\n",**(float **)p_v);
return 0;
}
#elif 0
/*
*二级指针形式参数
*
* */
#include<stdio.h>
void set_null (int **pp_num){
*pp_num = NULL;
}
int main (){
int num = 0;
int *p_num = #
set_null(&p_num);
printf("p_num = %p\n",p_num);
}
#endif
二级指针不可以代表二维数组,无类型指针可以和一级指针存储区捆绑,这个时候需要先强制类型转换成二级指针然后才能使用,二级指针通常作为函数的形式参数使用,使用二级指针形式参数可以从被调用函数向调用函数传递一个地址数据。
练习:编写两个函数从两个圆里找到面积比较大的那个圆并把传递给调用函数。函数不能使用返回值
/*
*
*
*圆面积大小比较练习
*
*
* */
#include<stdio.h>
typedef struct {
int row;
int col;
} pt;
typedef struct {
pt center;
int radius;
} circle;
void larger (const circle *p_cl1,const circle *p_cl2,circle **pp_cl){
*pp_cl = (circle *)p_cl1->radius > p_cl2->radius ? p_cl1 : p_cl2;
}
int main (){
circle cl1 = {0},cl2 = {0},*p_cl = NULL;
printf("请输入一个圆的位置:");
scanf("%d%d%d",&(cl1.center.row),&(cl1.center.col),&(cl1.radius));
printf("请输入另一个圆的位置:");
scanf("%d%d%d",&(cl2.center.row),&(cl2.center.col),&(cl2.radius));
larger(&cl1,&cl2,&p_cl);
printf("比较大的圆圈是((%d,%d),%d)\n",p_cl->center.row,p_cl->center.col,p_cl->radius);
return 0;
}