回顾: c的数据类型: 基本数据类型 整型 char short int long 浮点 float double 扩展数据类型 数组 指针 自定义数据类型 结构体(构造体) 枚举 联合 void 空类型 结构体的作用 将其他多个类型,组合起来构造一个新的类型 结构体的定义 struct student { //定义结构体类型 student char name[8]; int score; ...... }; typedef struct student s; //定义了一个结构体变量 s.name 通过.(分量运算) 访问结构体里地 name成员 ..... 结构体指针 struct student *ps; ps = &s; ps->name 通过->(指向运算) 访问结构体成员 结构体的使用 结构体作为函数的参数 跟基本数据类型变量一样,传递的是结构体的值,而不是引用(地址) ,跟数组(指针)不一样 结构体作为函数的返回值 作为返回值,跟基本类型变量一样,返回的是结构体整体的值,而不是指针 结构体的对齐和补齐 结构体的数组
--------------------
#include <stdio.h> int main(){ typedef struct { char name[8]; int score; int age; }Student; Student s; Student as[20]; printf("sizeof(s) = %d ",sizeof(s)); //16 printf("&as[4] = %p ",&as[4]); printf("&as[5] = %p ",&as[5]); return 0; }
//16
//16 每个元素占用16个字节
//16
#include <stdio.h> int main(){ struct Score{ int stdc; int unixc; int cpp; char qt; }; typedef struct { char name[8]; struct Score score;//定义一个结构体类型 int age; }Student; Student s; s.score.stdc = 90; s.score.unixc = 92; s.score.cpp = 93; printf("stdc = %d ",s.score.stdc); printf("unixc = %d ",s.score.unixc); printf("cpp = %d ",s.score.cpp); getchar(); }
typedef struct student{ char name[8]; struct Score score; //struct student s; //结构体里不能放该结构体的变量, struct student* s; //可以是该结构体类型的指针 int age; }Student;
自定义数据类型 --枚举
char c; c的取值集合(-128 ~ 127)
unsigned char uc ;uc的取值集合(0 ~ 255)
通过 enum 可以设定一个 取值集合范围 的类型,枚举
SEASON{Spring,Summer,Autumn,Winter} //SEASON只能是中括号里面的值
#include <stdio.h> int main(){ enum SEASON{Spring,Summer,Autumn,Winter}; enum SEASON s; //定义一个枚举变量s s = Summer; if(Summer == s) { printf("现在是夏天 "); } getchar(); } //现在是夏天
#include <stdio.h> int main(){ enum SEASON{Spring,Summer,Autumn,Winter}; enum SEASON s; //定义一个枚举变量s printf("Spring = %d ",Spring); printf("Summer = %d ",Summer); printf("Autumn = %d ",Autumn); printf("Winter = %d ",Winter); getchar(); } Spring = 0 Summer = 1 Autumn = 2 Winter = 3
定下标
#include <stdio.h> int main(){ enum SEASON{Spring =6,Summer,Autumn=10,Winter}; enum SEASON s; //定义一个枚举变量s printf("Spring = %d ",Spring); printf("Summer = %d ",Summer); printf("Autumn = %d ",Autumn); printf("Winter = %d ",Winter); getchar(); } Spring = 6 Summer = 7 Autumn = 10 Winter = 11
/*
* 输入数字 1 - 7
* 输出 对应的星期几调休
*/
#include <stdio.h> enum WEEK {MON = 1,TUE,WED,THU,FRI,SAT,SUN}; int main(){ //int num = 0; enum WEEK num; //输入数字 printf("输入调休的数字:"); scanf("%d",(int *)&num); switch(num) { case MON: printf("周一休息 "); break; case TUE: printf("周二休息 "); break; case WED: printf("周三休息 "); break; case THU: printf("周四休息 "); break; case FRI: printf("周五休息 "); break; case SAT: printf("周六休息 "); break; case SUN: printf("周日休息 "); break; default: printf("你输入的数字不对 "); break; } getchar(); getchar(); return 0; }
联合体(共用体) union
typedef union Score { //定义了一个联合类型 Score char level; int i_score; double d_score; //8 }; Score s; printf("sizeof(s) = %d ",sizeof(s)); //8 即double类型的大小 getchar();
联合体会找到所有成员中最大的那一个类型做为当前联合体体的占用空间
#include <stdio.h> int main(){ typedef union Score { //定义了一个联合类型 Score char level; int i_score; double d_score; }; Score s; s.level = 'A'; // 或者 printf("当前成绩是%c ",s.level); printf("&s.level = %p ",&s.level); s.i_score = 80; printf("当前成绩是%d ",s.i_score); printf("&s.i_score = %p ",&s.i_score); s.d_score = 80.8; printf("当前成绩是%lf ",s.d_score); printf("&s.d_score = %p ",&s.d_score); printf("当前成绩是%c ",s.level);//之前的值被覆盖掉了 printf("当前成绩是%d ",s.i_score);//之前的值被覆盖掉了 getchar(); }
当前成绩是A
&s.level = 00000000010FFEC0
当前成绩是80
&s.i_score = 00000000010FFEC0
当前成绩是80.800000
&s.d_score = 00000000010FFEC0
当前成绩是3 //之前的值被覆盖掉了
当前成绩是858993459 //之前的值被覆盖掉了
C的数组成分: 19
c的数据类型:
基本数据类型
整型 unsigned signed
char short int long
浮点
float double
扩展数据类型
数组
指针
自定义数据类型
结构体(构造体) struct
枚举 enum
联合 union
void 空类型 void
数据的存储特性:
auto static const regiter volatile
用于计算数据字节数 sizeof
类型的别名 typedef
流程控制语句:
分支语句 if else
多路分支 switch case default
循环 for while do
其他语句 break continue return goto
extern 符号的外部链接特性
C99 扩充了5个
_Bool 布尔类型
_Complex 复数类型
_Imaginary 二进制类型
inline 内联
restrict 存储类型,限定指针的访问
指针高级
回顾指针
指针 --> 内存的地址
指针变量 --> 用来保存地址的内存
sizeof(指针变量) = 机器字
指针运算
+ - ++ --
#include <stdio.h> int main(){ int arr[5]; char *pc = (char *)arr; int *pa = arr; printf("arr 地址 = %p ",arr); printf("pc 保存的地址 = %p ",pc); printf("pa 保存的地址 = %p ",pa); getchar(); return 0; }
arr 地址 = 0000000000EFFEC0
pc 保存的地址 = 0000000000EFFEC0
pa 保存的地址 = 0000000000EFFEC0
指针类型++
#include <stdio.h> int main(){ int arr[5]; char *pc = (char *)arr; int *pa = arr; printf("arr 地址 = %p ",arr); printf("pc 保存的地址 = %p ",pc); printf("pc++ 保存的地址 = %p ",++pc); printf("pa 保存的地址 = %p ",pa); printf("pa++ 保存的地址 = %p ",++pa); getchar(); return 0; } arr 地址 = 00000000010FFD70 pc 保存的地址 = 00000000010FFD70 pc++ 保存的地址 = 00000000010FFD71 pa 保存的地址 = 00000000010FFD70 pa++ 保存的地址 = 00000000010FFD74
指针的作用
通过指针变量间接访问其所指向的变量
作为函数的参数,可以通过指针变量修改函数外部的局部变量
通过指针传入 大块的数据
指针常量
作为函数的参数,通过指针不可以修改 传入的指向区域的内容
指针常见问题
1. 野指针 int *p; p == 2. 空指针 int i = 0; int *p = NULL; //p -> 指向 内存0位置 *p = 20; //往0地址赋值为20; //系统报段错误,终止程序 空指针 比野指针好 空指针更安全 避免野指针 在声明指针变量时 对其初始化为NULL 3. 悬空指针 函数返回的指针 指向当前函数的局部变量 当函数结束,局部变量生命结束,内存被收回,指针指向函数局部变量无效
/* * 悬空指针 */ #include <stdio.h> int * func() { static int b = 20; int *pb = &b; return pb; //pb 是悬空的指针,所指向的b 在函数结束后,已经被释放了,b的存储空间 可以被其他函数使用 //pb 之所以悬空 因为 b 是auto 变量 // 可以返回 字符串常量 的地址 } int main(){ int a = 10; int *pa = NULL; pa = func(); printf("a = %d ",a); //该函数调用 使用了原来b的位置 printf("*pa = %d ",*pa); return 0; }
int * p,q;
p 是指针变量
q 是整型变量
------------
二级指针
#include <stdio.h> int main(){ int a = 20; int *pa = &a; //pa 指向 a变量 pa 保存a 变量的地址 int **ppa = &pa; //ppa 是二级指针,包保存指针变量pa的地址 printf("*pa = %d ",*pa); //通过pa 间接访问 a的值 printf("**ppa = %d ",*(*ppa)); //通过ppa 间接访问 a的值 getchar(); return 0; } *pa = 20 **ppa = 20
二级指针变量 保存指针变量的地址
二级指针的作用
#include <stdio.h> void func(int* i) { *i = 30; } int main(){ int a = 20; func(&a);//修改main函数的a变量 printf("i=%d ",a);//i=30 getchar(); return 0; }
/* * 指向指针的指针 * 二级指针 */ #include <stdio.h> void func2(int** pi) { *pi = NULL; } int main(){ int i = 20; int *pi = &i; printf("pi = %p ",pi); func2(&pi); //func2 功能将pi 指向NULL //如果要修改传入变量的内容,必须是该变量的指针 printf("pi = %p ",pi); getchar(); return 0; } pi = 000000000098F950 pi = 0000000000000000
1. 通过函数参数 修改指针变量的内容
#include <stdio.h> void func2(int *a,int *b, int** pi) { if (*a>*b) { *pi = a; }else{ *pi = b; } } int main(){ int a=0,b=b,*pmax=NULL; printf("a的地址= %p ",&a); printf("b的地址= %p ",&b); printf("请输入两个整数 "); scanf("%d%d",&a,&b); printf("改之前pamx = %p ",pmax); func2(&a,&b,&pmax); printf("改之后pamx = %p ",pmax); getchar(); getchar(); return 0; } a的地址= 0000000000F3F7A0 b的地址= 0000000000F3F7A8 请输入两个整数 34 32 改之前pamx = 0000000000000000 改之后pamx = 0000000000F3F7A0
2. 字符串数组的使用
#include <stdio.h> int main(){ char *str[5] = {"巴西","葡萄牙","比利时","法国","德国"}; char **pstr = str; for(int i = 0;i<5;i++) { printf("%s ",*(pstr+i)); } getchar(); return 0; } 巴西 葡萄牙 比利时 法国 德国
/* * 二级指针使用案例 * 操作字符串数组 * * 写一个程序,预测一下世界杯冠军 */ #include <stdio.h> #include <stdlib.h> #include <time.h> int main(){ int i = 0; char *str[5] = {"巴西","葡萄牙","比利时","法国","德国"}; char **pstr = str; for(i = 0;i<5;i++) { printf("%s ",*(pstr+i)); } srand(time(0)); i = rand()%5; printf("我预测的世界杯冠军是 %s ", *(pstr+i)); getchar(); return 0; } 巴西 葡萄牙 比利时 法国 德国 我预测的世界杯冠军是 法国