项目需求
中国福利彩票“双色球”是一种联合发行的“乐透型”福利彩票。采用计算机网络系统发行 销售,定期电视开奖。
游戏规则
1.“双色球”彩票投注区分为红色球号码区和蓝色球号码区。
2.“双色球”每注投注号码由 6 个红色球号码和 1 个蓝色球号码组成。红色球号码从 1--33 中选择;蓝色球号码从 1--16 中选择。
3.“双色球”每注 2 元。
项目需求
双色球出奖结果是随机的,但现实生活中有个很神奇的概念叫“概率”,并且偶尔会出现规律,让你和百万大奖擦肩!
比如:如果某个号子历史出现的机率高,那它出现的机率就相应会高!现在要求我们编写程序找出最近一段时间每个号码出现的次数并把结果保存到一个数组,供其它分析模块调用
1. 为什么要使用指针
- 函数的值传递,无法通过调用函数,来修改函数的实参
- 被调用函数需要提供更多的“返回值”给调用函数
- 减少值传递时带来的额外开销,提高代码执行效率
#include <iostream>
#include <stdlib.h>
#include <time.h> using namespace std;//1.函数的值传递,无法通过调用函数,来修改函数的实参
void add_blood(int blood){ blood +=1000;
}//2. 被调用函数需要提供更多的“返回值”给调用函数
//加血成功返回ture ,否则返回false ;
bool add_blood2(int blood){ if(blood >= 1000) {
return false;
}else {
blood +=1000;}return true;
//另外还需要返回最新的血量,不能实现}
//3. 减少值传递时带来的额外开销,提高代码执行效率
struct _hero_stat{
int blood; //英雄的血量
int power; //英雄攻击力
int level; //英雄级别
char name[64]; //英雄名字
char details[1024];//状态描述
};//3.1 Martin 's power: -1474835680 using 16struct _hero_stat upgrade1(
struct _hero_stat hero, int type){
switch(type){
case 1://攻击型的英雄hero.blood += 1000; hero.power += 200; hero.level++; break;
case 2://防御型的英雄hero.blood += 2000; hero.power += 50; hero.level++; break;
default:break;
}return (hero);}
//3.2.指针实现升级 Martin 's power: -1474835680 using 3
void upgrade2(
struct _hero_stat *hero, int type){
switch(type){
case 1://攻击型的英雄hero->blood += 1000; hero->power += 200; hero->level++; break;
case 2://防御型的英雄hero->blood += 2000; hero->power += 50; hero->level++; break;
default:break;
}
} int main(void){//int martin = 1000;time_t start, end; struct _hero_stat martin; strcpy(martin.name, "martin");martin.blood = 1000;martin.level = 100; martin.power = 1000;time(&start);//1970年1月1日0时0分0秒 至今的秒数for(int i=0; i<99999999; i++){ //martin = upgrade1(martin, 1); upgrade2(&martin, 1);} time(&end);cout<<"Martin 's power: "<<martin.power<<endl; cout<<"using "<<end-start<<endl;//add_blood(martin);//martin = add_blood(martin);//cout<<"martin 's blood: "<<martin<<endl; system("pause"); return 0;
}
2. 指针定义
// demo 8-2.c #include <stdio.h> #include <stdlib.h> int main(void){ int age; char ch; //定义了一个指针 //指针本身也是一个变量 //名称是p, 它是一个指针,可以指向一个整数 //也就是说: p的值就是一个整数的地址!!! int *p ; char * c; //指针p 指向了age //p的值,就是age 的地址 p = &age; c = &ch; //scanf_s("%d", &age); scanf_s("%d", p); printf("age: %d\n", age); system("pause"); return 0; } |
指针的定义
int *p; // int *p1, *p2; 或者
int* p; // int* p1,p2; //p1 是指针, p2 只是整形变量或者
int * p; 或者
int*p;//不建议
3. 指针的初始化、访问
指针的初始化
// demo 8-3.c #include <stdio.h> #include <stdlib.h> int main(void){ int room = 2; //定义两个指针变量指向 room int *p1 = &room; int *p2 = &room; printf("room 地址:0x%p\n", &room); printf("p1 地址:0x%p\n", &p1); printf("p2 地址:0x%p\n", &p2); printf("room 所占字节:%d\n", sizeof(room)); printf("p1 所占字节:%d\n", sizeof(p1)); printf("p2 所占字节:%d\n", sizeof(p2)); system("pause"); return 0; } |
注意:
32 位系统中,int 整数占 4 个字节,指针同样占 4 个字节 64 位系统中,int 整数占 4 个字节,指针同样占 8 个字节
指针的访问
访问指针
// demo 8-4.c #include <stdio.h> #include <stdlib.h> int main(void){ int room = 2 ; int room1 = 3 ;//3p int *p1 = &room; int *p2 = p1;//int *p2 = &room; //1.访问(读、写)指针变量本身的值,和其它普通变量的访问方式相同 int *p3 = p1; printf("room 的地址: %d\n", &room); printf("p1的值: %d p2的值: %d\n", p1, p2); printf("p3的值: %d\n", p3); p3 = &room1; printf("p3 的值: %d, room1 的地址:%d\n", p3, &room1);//不建议用这 种方式 //使用16进制打印,把地址值当成一个无符号数来处理 printf("p1=0x%p\n", p1); printf("p1=0x%x\n", p1); rintf("p1=0x%X\n", p1); system("pause"); return 0; } |
访问指针所指向的内容
// demo 8-5.c #include <stdio.h> #include <stdlib.h> int main(void){ int room = 2 ; int * girl = &room; int x = 0; x = *girl;// *是一个特殊的运算符,*girl 表示读取指针girl所指向的 变量的值, *girl 相当于 room printf("x: %d\n", x); *girl = 4; //相当于 room = 4 printf("room: %d, *girl: %d\n", room, *girl); system("pause"); return 0; } |
4. 空指针和坏指针
//8-6.c #include <stdio.h> #include <stdlib.h> int main(void){ int room1 = 666; int room2 = 888; int girl ; int *select ; scanf_s("%d", &girl); if(girl == 666){ select = &room1; }else if(girl == 888) { select = &room2; } printf("选择的房间是: %d\n", *select); system("pause"); return 0; } |
1.什么是空指针?
空指针,就是值为 0 的指针。(任何程序数据都不会存储在地址为 0 的内存块中,它是被操作系统预留的内存块。) int *p = 0;
或者
int *p = NULL; //强烈推荐
2.空指针的使用
1)指针初始化为空指针
int *select = NULL;
目的就是,避免访问非法数据。
2)指针不再使用时,可以设置为空指针 int *select = &xiao_long_lv;
//和小龙女约会
select = NULL;
3)表示这个指针还没有具体的指向,使用前进行合法性判断 int *p = NULL;
// 。。。。
if (p) { //p 等同于 p!=NULL
//指针不为空,对指针进行操作
}
3.坏指针
int *select; //没有初始化情形一
printf("选择的房间是: %d\n", *select);
情形二
select = 100;
printf("选择的房间是: %d\n", *select);
5.const运用在指针的区别
//8-7.c #include <stdio.h> #include <stdlib.h> //const 和指针 int main(void){ int wife = 24; int girl = 18; //第一种 渣男型 int * zha_nan = &wife; *zha_nan = 25; zha_nan = &girl; *zha_nan = 19; printf("girl : %d wife: %d\n", girl, wife); //第二种 直男型 //const int * zhi_nan = &wife; //第一种写法 int const * zhi_nan = &wife; // 第二种写法 //*zhi_nan = 26; printf("直男老婆的年龄:%d\n", *zhi_nan); zhi_nan = &girl; printf("直男女朋友的年龄:%d\n", *zhi_nan); //*zhi_nan = 20; //第三种 暖男型 int * const nuan_nan = &wife; *nuan_nan = 26; printf("暖男老婆的年龄:%d\n", wife); //nuan_nan = &girl; //不允许指向别的地址 //第四种超级暖男型的 const int * const super_nuan_nan = &wife; //不允许指向别的地址, 不能修改指向变量的值 //*super_nuan_nan = 28; // //super_nuan_nan = &girl; system("pause"); return 0; } |
总结:看 const 离类型(int)近,还是离指针变量名近,离谁近,就修饰谁,谁就不能变。
6. 指针的算术运算
指针的自增运算
//8.8.c #include <stdio.h> #include <stdlib.h> int main(void){ int ages[]={21,15,18,14,23,28,10}; int len = sizeof(ages)/sizeof(ages[0]); //使用数组的方式来访问数组 for( int i=0; i<len; i++){ printf("第%d个学员的年龄是:%d\n", i+1, ages[i]); } //打印数组的地址和第一个成员的地址 printf("ages 的地址: 0x%p , 第一个元素的地址: 0x%p\n", ages,&ages[0]);
int *p = ages; //访问第一个元素 printf("数组的第一个元素:%d\n", *p); //访问第二个元素 //p++; // p = p+ 1*(sizeof(int)) //printf("数组的第二个元素:%d, 第二个元素的地址:0x%p\n",*p,p); for(int i=0; i<len; i++){ printf("数组的第%d个元素:%d 地址:0x%p\n", i+1, *p, p); p++; } printf("------------------------\n");
char ch[4]={'a','b','c','d'}; char * cp = ch; for(int i=0; i<4; i++){ printf("数组的第%d个元素:%c 地址:0x%p\n", i+1, *cp, cp); cp++; } //总结: p++ 的概念是在p当前地址的基础上 ,自增 p 对应类型的大小 p = p+ 1*(sizeof(类型)) system("pause"); return 0; } |
总结: p++ 的概念是在 p 当前地址的基础上 ,自增 p 对应类型的大小, 也就是说 p = p+ 1*(sizeof(类型))
指针的自减运
//8-9.c #include <stdio.h> #include <stdlib.h> #include <string.h> /**
*/ int main(void){ char input[128]; int len; char tmp;
scanf_s("%s", input, 128); len = strlen(input); //方法 1 交换
/*for( int i=0; i<len/2; i++){ tmp = input[i]; input[i] = input[len-i-1]; input[len-i-1] = tmp; }*/
/*for(int i=0; i<len; i++){ printf("%c", input[i]); }*/ //printf("逆转后:%s\n", input); //第二种方法 /*for(int i=0; i<len; i++){ printf("%c", input[len-i-1]); } printf("\n"); */ //第三种方法 char *p = &input[len-1]; for(int i=0; i<len; i++){ printf("%c", *p--); //p--; } printf("\n"); system("pause"); return 0; } |
指针与整数之间的加减运算
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(void){ int ages[] = {20,18,19,24,23,28,30,38, 35, 32}; int len = sizeof(ages) / sizeof(ages[0]); int *p = ages; printf("第 7 个美女的年龄: %d\n", *(p+6)); printf("*p+6 = : %d\n", *p+6); printf("第 3 个美女的年龄: %d\n", *(p+2)); int *p1 = &ages[4]; printf("相对于第 5 个美女,她的前一位的年龄: %d\n", *(p1 -1)); printf("相对于第 5 个美女,她的前三位的年龄: %d\n", *(p1 -3));
system("pause"); return 0; } |
知识点:
(1)指针与整数的运算,指针加减数字表示的意义是指针在数组中位置的移动;对于整数部分而言,它代表的是一个元素,对于不同的数据类型,其数组的元素占用的字节是不一样的,比如指针 + 1,并不是在指针地址的基础之上加 1 个地址,而是在这个指针地址的基础上加 1 个元素占用的字节数:
n如果指针的类型是 char*,那么这个时候 1 代表 1 个字节地址;
n如果指针的类型是 int*,那么这个时候 1 代表 4 个字节地址;
n如果指针的类型是 float*,那么这个时候 1 代表 4 个字节地址;
n如果指针的类型是 double*,那么这个时候 1 代表 8 个字节地址。
(3)通用公式:数据类型 *p; p + n 实际指向的地址:p 基地址 + n * sizeof(数据类型) p - n 实际指向的地址:p 基地址 - n * sizeof(数据类型) 比如
- 对于 int 类型,比如 p 指向 0x0061FF14,则: p+1 实际指向的是 0x0061FF18,与 p 指向的内存地址相差 4 个字节; p+2 实际指向的是 0x0061FF1C,与 p 指向的内存地址相差 8 个字节
- 对于 char 类型,比如 p 指向 0x0061FF28,则: p+1 实际指向的是 0x0061FF29,与 p 指向的内存地址相差 1 个字节; p+1 实际指向的是 0x0061FF2A,与 p 指向的内存地址相差 2 个字节;
指针与指针之间的加减运算
#include <stdio.h> #include <stdlib.h> /**
int main(void){ int ages[] = {20,18,19,24,23,28,30,38, 35, 32}; int ages1[] = {18, 19, 20, 22}; int len = sizeof(ages) / sizeof(ages[0]); int *martin = ages+6; int *rock = ages+9;
printf("rock - martin = %d\n", rock - martin); printf("martin - rock = %d\n", martin - rock); martin = ages+6; rock = ages1+3; printf("martin: %p rock: %p rock-martin: %d\n", martin, rock ,rock -martin); system("pause"); return 0; } |
|
知识点:
- 指针和指针可以做减法操作,但不适合做加法运算;
- 指针和指针做减法适用的场合:两个指针都指向同一个数组,相减结果为两个指针之间的元素数目,而不是两个指针之间相差的字节数。
比如:int int_array[4] = {12, 34, 56, 78}; int *p_int1 = &int_array[0]; int *p_int2 = &int_array[3];
p_int2 - p_int1 的结果为 3,即是两个之间之间的元素数目为 3 个。
如果两个指针不是指向同一个数组,它们相减就没有意义。
- 不同类型的指针不允许相减,比如char *p1;
int *p2;
p2-p1 是没有意义的。
还有二级指针,剩下的我们下一节再聊。