初阶6:函数
- 函数声明
void func();
返回类型 名称 (参数);
- 函数指针:
void (*pfunc)();
类型 (*函数名) (参数)
- 赋值:
pfunc = &func;
或者简写为pfunc=func;
- 函数名是地址,是执行函数代码的起始位置
-
调用1:解引用
(*pfunc)();
//前面必须加括号,因为后面括号优先级高,要把*和pfunc打包成整体才是函数指针解引用
调用2:pfunc();
-
函数指针可以作为函数参数,这叫
回调函数
-
指针函数:
void* func(){};
//返回值是一个指针类型
示例:
#include<stdio.h>
void func();
int main(){
printf("%p %p\n",func,&func);
func();
int n;
int* p;
int** pp;
void(*pfunc)();//函数指针
pfunc = &func;//赋值 pfunc=func
(*pfunc)();//pfunc()
//解引用 解出来也是func的地址
}
void func(){
printf("hello world\n");
}
重要示例!!!
#include<stdio.h>
int add(int a,int b){
return a+b;
}
int subtract(int a,int b){
return a-b;
}
int multipy(int a,int b){
return a*b;
}
int divide(int a,int b){
return a/b;
}
//int(*pfunc)(int,int)函数指针作为参数,便于调用样式统一的add,subtract,multipy,divide作为回调函数
int operation(int arr[],int n,int(*pfunc)(int,int)){
int* p=arr;
int res = *p;
while(++p<arr+n){
res = (*pfunc)(res,*p);//也可以写成res = pfunc(res,*p);
}
return res;
}
int print_array(int arr[],int n){
/* for(int i=0;i<n;i++){
printf("%d ",arr[i]);
}
printf("\n");*/
int* p =arr;
while(p<arr+n){
printf("%d ",*p++);
}
printf("\n");
}
int main(){
int n;
scanf("%d",&n);
int arr[n];
for(int i=0;i<n;i++){
scanf("%d",arr+i);
}
//函数指针数组,把统一样式的函数放到这个数组中,方便遍历调用
int (*opt[])(int,int) = {add,subtract,multipy,divide};
for(int i=0;i<4;i++){
printf("%d\n",operation(arr,n,opt[i]));
}
print_array(arr,n);
}
示例(整型比较):
#include<stdio.h>
#include<stdlib.h>
void print_array(int arr[],int n){
/* for(int i=0;i<n;i++){
printf("%d ",arr[i]);
}
printf("\n");*/
int* p =arr;
while(p<arr+n){
printf("%d ",*p++);
}
printf("\n");
}
int cmp(const void* p,const void* q){
return *(int*)p-*(int*)q;//如果*p>*q为正值,改变顺序,从而是升
序,如果*p<*q返回为负值,是升序
/* int* pn = (int*)p;
int* qm = (int*)q;
int n = *pn;
int m = *qm;
if(m == n){
return 0;
}else if(n<m){
return 1;//正数就行
}else{
return -1;//负数就行
}*/
}
int main(){
int n;
scanf("%d",&n);
int arr[n];
for(int i=0;i<n;i++){
scanf("%d",arr+i);
}
//qsort(void* base,size_t nmemb,size_t size,int (*cmper)(const void*,const void *);
//qsort(数组名,数组大小,数组元素类型大小,一个上面形式的函数指针:可以传这个样式的比较函数,满足条件时返回负值,不满足条件时传正值并改变元素顺序)
qsort(arr,n,sizeof(int),cmp);
print_array(arr,n);
}
示例(浮点型比较):
#include<stdio.h>
#include<stdlib.h>
void print_array(float arr[],int n){
float* p =arr;
while(p<arr+n){
printf("%f ",*p++);
}
printf("\n");
}
int cmp(const void* p,const void* q){
return *(float*)p>*(float*)q?1:-1;//如果*p>*q为正值,改变顺序>,从而是升序,如果*p<*q返回为负值,是升序
}
int main(){
int n;
scanf("%d",&n);
float arr[n];
for(int i=0;i<n;i++){
scanf("%f",arr+i);
}
print_array(arr,n);
//qsort(void* base,size_t nmemb,size_t size,int (*cmper)(const void*,const void *);
//qsort(数组名,数组大小,数组元素类型大小,一个上面形式的函数指针:可以传这个样式的比较函数,满足条件时返回负值,不满足条件时传正值并改变元素顺序)
qsort(arr,n,sizeof(float),cmp);
print_array(arr,n);
}
示例(字符串排序)
#include<stdio.h>//printf()
#include<stdlib.h>//qsort()
#include<string.h>//strcmp()
void print_array(char* arr[],int n){
char** p =arr;
while(p<arr+n){
printf("%s ",*p++);
}
printf("\n");
}
int cmp(const void* p,const void*q){
char* a = *(char**)p;
char* b = *(char**)q;
return strcmp(a,b);
}
int main(int argc,char* argv[]){
print_array(argv,argc);
qsort(argv,argc,sizeof(char*),cmp);
print_array(argv,argc);
}
C初阶7:结构体
struct Point{
int x;
int y;
int z;
};//占3*4=12个字节
- 结构体定义对象
struct Point p ={1,3,6};
- 结构体地址是它第一个成员的地址
- 结构体指针:
struct Point* q =&p;
p->x
等同于(*q).x
注意:->
自带解引用
3.1 访问结构体对象成员用.
如:p.x=100
3.2 访问结构体指针成员用->
如:q->x=1000;
-
c语言中定义结构体对象时
struct
不可省略 ,如struct Point q;
示例:
#include<stdio.h>
struct Point{
int x;
int y;
int z;
};//占3*4=12个字节
void Point_Print(struct Point p){
printf("(%d,%d,%d)\n",p.x,p.y,p.z);
}
void PointPtr_Print(struct Point* p){
printf("(%d,%d,%d)\n",p->x,p->y,p->z);
}
int main(){
struct Point p ={1,3,6};
Point_Print(p);
printf("%d\n",sizeof(p));
struct Point* q=&p;//q是个指针,在64位中占8个字节,32位占4个字
节
printf("%d\n",sizeof(q));
Point_Print(*q);
q->x=100;
q->y=300;
q->z=600;
Point_Print(p);
PointPtr_Print(q);
}
-
结构体数组
struct Point Points[] = {{1,2,3},{3,3,3},{2,3,4},{7,7,7}};
- 结构体嵌套结构体
struct Line{
struct Point p1;
struct Point p2;
}
示例:
#include<stdio.h>
#include<math.h> //sqrt() pow(a,b) 编译时要链接库用-lm
struct Point{
int x;
int y;
int z;
};//占3*4=12个字节
//结构体嵌套结构体
struct Line{
struct Point p1;
struct Point p2;
};
float GetDistance(struct Line line){
return sqrt(
pow(line.p1.x-line.p2.x,2)+
pow(line.p1.y-line.p2.y,2)+
pow(line.p1.z-line.p2.z,2)//pow(a,b)表示a的b>次方
);
}
float GetDistance2(struct Line* line){//参数是结构体指针,访问其对
象line成员时要用-> 调用是传参要传结构体的地址
return sqrt(
pow(line->p1.x-line->p2.x,2)+
pow(line->p1.y-line->p2.y,2)+
pow(line->p1.z-line->p2.z,2)//pow(a,b)表示a的
b次方
);
}
void Point_Print(struct Point p){
printf("(%d,%d,%d)\n",p.x,p.y,p.z);
}
void PointPtr_Print(struct Point* p){
printf("(%d,%d,%d)\n",p->x,p->y,p->z);
}
int main(){
struct Point p ={1,3,6};
Point_Print(p);
printf("%d\n",sizeof(p));
struct Point* q=&p;//q是个指针,在64位中占8个字节,32位占4个字
节
printf("%d\n",sizeof(q));
Point_Print(*q);
q->x=100;
q->y=300;
q->z=600;
Point_Print(p);
PointPtr_Print(q);
//结构体数组,遍历如同数组遍历
struct Point points[] = {{1,2,3},{3,3,3},{2,3,4},{7,7,7}};
for(int i=0;i<4;i++){
Point_Print(points[i]);
PointPtr_Print(points+i);
}
//结构体嵌套结构体
struct Line line = {{0,0,0},{3,4,5}};
Point_Print(line.p1);
Point_Print(line.p2);
printf("%f\n",GetDistance(line));
printf("%f\n",GetDistance2(&line));
}
- 结构体包含结构体数组:
struct Triangle{
struct Point p[3];
};
struct Triangel t = {{{1,2,2},{1,1,1},{8,0,0}}};
- 遍历:
for(int i=0;i<3;i++){
Point_Print(t.p[i]);
}
示例:
#include<stdio.h>
#include<time.h> //struct tm* time() ...
int main(){
time_t now = time(NULL);
struct tm* utc = gmtime(&now);//格林时间
struct tm* local = localtime(&now);//本地时间 比格林世界多8个小时
char temp[20]={'\0'};// YYYY/MM/DD HH:mm:SS
//把struct tm*类型的结构体指针对象的内容,以%F年 %T时间的格式(strftime里面定义的),放入大小为20 的temp数组空间中
strftime(temp,20,"%F %T",local);
printf("%s\n",temp);
struct tm test;
//把时间字符串以%F %T格式放入到 struct tm类型的结构体对象test中
strptime("2020-01-01 21:03:10","%F %T", &test);
printf("%d/%d/%d %d:%d:%d\n",test.tm_year+1900,test.tm_mon+1,test.tm_hour,test.tm_min,test.tm_sec);
}
C初阶8:联合体
示例:
#include<stdio.h>
struct SPoint{
int x;
int y;
int z;
};//占3个*4字节=12字节内存
union UPoint{
int x;
int y;
int z;
};//联合体成员共享内存,所占空间为最大的成员所占空间 这里占4个字节,因为int类型占4字节
union Integer{
int data;
char bytes[4];
};
int main(){
printf("%d\n",sizeof(struct SPoint));
printf("%d\n",sizeof(union UPoint));
union UPoint up;
up.x = 100;//给联合体一个成员赋值,因为成员之间共享一块内存,所以其余成员也被赋同样的值
printf("(%d,%d,%d)\n",up.x,up.y,up.z);
union Integer n;
n.data = 10113234;
printf("%#x\n",n.data);//%#x以十六进制输出
for(int i=0;i<4;++i){
printf("%#hhx\n",n.bytes[i]);//%x默认整型
//单个h是把int(4个字节)扩展成short(2个字节)
//两个h是把int扩展成char(1个字节)
}
C初阶:枚举
- 魔术数字:程序中含义不明的数字。
- 通常用符号来表示,解决方式:
const
和#define
const int SUN = 0;
//SUM是常量1 用等号#define SUN 0;
//把SUN在预处理时替换成0,用空格 -E
可以看预处理 -
const
和#define
(宏定义)区别:
No. | 比较项 | #define | const |
---|---|---|---|
1 | 编译处理 | 预处理阶段 | 编译、运行阶段 |
2 | 工作原理 | 简单的字符串替换 | 有对应的数据类型 |
3 | 存储方式 | 展开,在内存中有若干个备份 | 只读变量在内存中只有一份 |
4 | 类型检查 | 没有类型安全检查 | 在编译阶段进行类型检查 |
5 | 作用域 | 从定义开始,任何位置都可访问 | 只能在变量作用域内 |
#define MON 1;
在编译预处理时把所有的MON替换成1
4. 宏定义无类型
const在编译运行阶段编译处理
-
枚举:
enum 枚举名{名字0,名字1,名字2,...}
5.1. 类型为int 值一次从0到n
5.2. 枚举可看作一堆宏定义放一起(但不是) 就是给一些常量值,规定一个名字
5.3. 枚举量可以直接作为值使用
5.4. 枚举类型可以直接作为类型使用,用类型定义时只能用枚举值里的有限个常量
5.5. 声明时可以指定值 如:
enum R{Bin=2,Oct=8,Dec=10,Hex=16};
如确定一个值后续依次加一 如:
enum M{Jan=1,Feb,Mar,Apr,May,Jun,Jul,Aug,Sept,Oct,Nov,Dec};
即:Feb=2,Mar=3