复习:
指针:
二级指针:
int a=12;
int *q=&a;
int **p;
p=&q;
*p **p p &p &q
数组指针:指针
指针数组:数组
int (*p)[3]
int *p[3]
局部变量
全局变量
static
局部变量
全局变量
函数
extern
全局变量
函数:
int fun(int a,int b)//a=m b=n
{
return a;
}
int main()
{
int x=fun(m,n);
}
void void*
void * p;
void a;
递归:
栈空间
指针函数
一、函数指针:本质是指针 存函数地址 指向一类函数
函数指针3步:
定义
赋值
调用
1.定义函数指针:根据函数定义函数指针
int max()
{
}
int min()
{
}
int add()
{
}
int (*p)();//定义函数指针p 可以指向返回值类型为int 没有形参的一类函数
2.赋值
p=max;//将函数max的首地址 赋值给函数指针p
3.调用
(*p)();//*p()也可以
无参:
#include<stdio.h>
int max()
{
int a=90;
int b=20;
return a>b?a:b;
}
int main()
{
int (*p)()=NULL;
p = max;
int ret = (*p)();//max();
printf("%d\n",ret);
return 0;
}
有参:
#include<stdio.h>
int max(int a,int b)
{
return a>b?a:b;
}
int main()
{
int c=90;
int d=89;
int (*p)(int,int)=NULL;
p = max;
int ret = (*p)(c,d);//max();
printf("%d\n",ret);
return 0;
}
练习:定义函数指针
定义函数指针指向void *malloc(size_t size);
void*(*pfun)(size_t);
定义函数指针指向void fun()
void (*p)();
练习:定义一函数指针 指向一个函数 函数功能:求两个数的和
#include<stdio.h>
void add(int a,int b)
{
printf("%d\n",a+b);
}
int main()
{
int c=90;
int d=89;
void (*p)(int,int)=NULL;
p = add;
(*p)(c,d);//max();
return 0;
}
例子:
#include<stdio.h>
int max(int a,int b)
{
return a>b?a:b;
}
int min(int a,int b)
{
return a<b?a:b;
}
int main()
{
int c=90;
int d=89;
int ret,choose;
int (*p)(int,int)=NULL;
/*
p = max;
ret = (*p)(c,d);//max();
printf("%d\n",ret);
p=min;
ret = (*p)(c,d);//max();
printf("%d\n",ret);
*/
puts("1.max 2.min");
scanf("%d",&choose);
switch(choose)
{
case 1:
p=max;
break;
case 2:
p=min;
break;
default:
puts("error!");
break;
}
ret = (*p)(c,d);
printf("%d\n",ret);
return 0;
}
练习:函数指针版本-计算器
#include<stdio.h>
#include<stdlib.h>
void add(int a,int b)
{
printf("%d\n",a+b);
}
void sub(int a,int b)
{
printf("%d\n",a-b);
}
void mul(int a,int b)
{
printf("%d\n",a*b);
}
void _div(int a,int b)
{
printf("%d\n",a/b);
}
int main()
{
int choose;
int x,y;
void (*pfun)(int,int)=NULL;
while(1)
{
puts("input op1 op2");
scanf("%d%d",&x,&y);
puts("1.add 2.sub 3.mul 4.div 5.exit");
scanf("%d",&choose);
switch(choose)
{
case 1:
pfun = add;
break;
case 2:
pfun = sub;
break;
case 3:
pfun = mul;
break;
case 4:
pfun = _div;
break;
case 5:
exit(0);//正常退出
default:
puts("choose error!");
continue;
}
(*pfun)(x,y);
sleep(1);
system("clear");
}
return 0;
}
4.函数指针做形参
函数指针定义:函数调用时
函数指针赋值:实参传递给形参时
函数指针调用:函数体内
例子1:
#include<stdio.h>
int max(int a,int b)
{
return a>b?a:b;
}
void fun(int (*p)(int,int),int m,int n)//p=max m=x n=y
{
int ret = (*p)(m,n);//-->max(x,y)
printf("%d\n",ret);
}
int main()
{
int x=90;
int y=89;
fun(max,x,y);
}
例子2:
#include<stdio.h>
int max(int a,int b)
{
return a>b?a:b;
}
int min(int a,int b)
{
return a<b?a:b;
}
int add(int a,int b)
{
return a+b;
}
void fun(int (*p)(int,int),int m,int n)//p=max m=x n=y
{
int ret = (*p)(m,n);//-->max(x,y)
printf("%d\n",ret);
}
int main()
{
int x=90;
int y=89;
fun(max,x,y);
fun(min,x,y);
fun(add,x,y);
}
练习:函数指针形参版本计算器
#include<stdio.h>
#include<stdlib.h>
void add(int a,int b)
{
printf("%d\n",a+b);
}
void sub(int a,int b)
{
printf("%d\n",a-b);
}
void mul(int a,int b)
{
printf("%d\n",a*b);
}
void _div(int a,int b)
{
printf("%d\n",a/b);
}
void fun(void (*p)(int,int),int a,int b)
{
(*p)(a,b);
}
int main()
{
int choose;
int op1,op2;
while(1)
{
puts("input op1 op2");
scanf("%d%d",&op1,&op2);
puts("1.add 2.sub 3.mul 4.div 5.exit");
scanf("%d",&choose);
switch(choose)
{
case 1:
fun(add,op1,op2);
break;
case 2:
fun(sub,op1,op2);
break;
case 3:
fun(mul,op1,op2);
break;
case 4:
fun(_div,op1,op2);
break;
case 5:
exit(0);//正常退出
default:
puts("choose error!");
break;
}
sleep(1);
system("clear");
}
return 0;
}
5.函数指针数组:代替大型switch语句
int (*p[2])(int,int);
int (*p[2])(int,int)={max,min};
p[0]=max;
p[1]=min;
#include<stdio.h>
int max(int a,int b)
{
return a>b?a:b;
}
int min(int a,int b)
{
return a<b?a:b;
}
int main()
{
int choose;
int (*p[2])(int,int)={max,min};//p[0]->max p[1]min *p p[0]
for(;;)
{
puts("1.max 2.min");
scanf("%d",&choose);//1
printf("%d\n",p[choose-1](3,7));
}
}
练习:函数指针数组版本计算器
#include<stdio.h>
#include<stdlib.h>
void add(int a,int b)
{
printf("%d\n",a+b);
}
void sub(int a,int b)
{
printf("%d\n",a-b);
}
void mul(int a,int b)
{
printf("%d\n",a*b);
}
void _div(int a,int b)
{
printf("%d\n",a/b);
}
int main()
{
void (*p[4])(int,int)={add,sub,mul,_div};
int choose;
int op1,op2;
while(1)
{
puts("input op1 op2");
scanf("%d%d",&op1,&op2);
puts("1.add 2.sub 3.mul 4.div ");
scanf("%d",&choose);
p[choose-1](op1,op2);
sleep(1);
system("clear");
}
return 0;
}
二、const:c语言关键字 用来定义一个常量
1.
#include<stdio.h>
int main()
{
int const a=100;//定义一个常量a 其值为100
a = 200;//因为a是常量 不可以被赋值
printf("%d\n",a);
return 0;
}
2.const和指针关系
(1)int const *p;//等价于const int *p
#include<stdio.h>
int main()
{
int a;
a=100;
int const *p=80;
//p=&a;//可以 因为p是变量
*p = 80;//错 因为*p是常量 不能放赋值符号左边
printf("%d\n",*p);
return 0;
}
(2)int *const p;
#include<stdio.h>
int main()
{
int a;
a=100;
int *const p=&a;
//p=&a;//错 因为p是常量 不能放赋值符号左边
*p = 80;//可以
printf("%d\n",*p);
return 0;
}
(3)int const *const p;//p和*p都是常量
三.宏定义:机械替换
(1)宏定义可以嵌套
#define 宏名 宏值
#define N 3
#include<stdio.h>
#define N 2
#define M N+1
int main()
{
int a=M*M;//N+1*N+1=2+1*2+1=5
printf("%d\n",a);//5
return 0;
}
(2)带参宏
例子1:
#include<stdio.h>
#define SQUARE(x) x*x
int main()
{
int a=SQUARE(3);
printf("%d\n",a);//9
return 0;
}
例子2:
#include<stdio.h>
#define SQUARE(x) x*x
int main()
{
int a=SQUARE(2+3);//2+3*2+3=2+6+3=11
printf("%d\n",a);//11
return 0;
}
应该是:
#include<stdio.h>
#define SQUARE(x) (x)*(x)
int main()
{
int a=SQUARE(2+3);//2+3*2+3=2+6+3=11
printf("%d\n",a);//11
return 0;
}
练习:实现一个宏MAX 能求两个数最大值
#define MAX(a,b) (a)>(b)?(a):(b)
练习:定义一个宏 传入一个字符 如果是大写 转为小写 若是小写 则不变
#include<stdio.h>
#define TOS(x) (x)>='A'&&(x)<='Z'?((x)+32):(x)
int main()
{
char ch;
while(1)
{
ch=getchar();
getchar();
ch=TOS(ch);
printf("%c\n",ch);
}
return 0;
}
带参宏与函数调用区别:
带参宏:编译阶段替换 运行效率高 但编译会导致代码量增大
函数调用:运行时调用
代码量小 使用带参宏
代码量大 函数调用
四、条件编译(可以实现让某一部分代码参加编译或不参加编译)
1.#ifdef 标识符
程序段
#endif
#include<stdio.h>
#define A
int main()
{
#ifdef A //如果定义A 则下边的语句参加编译 否则不执行
puts("hahahaha");//输出
#endif
return 0;
}
方法2:
#include<stdio.h>
int main()
{
#ifdef A
puts("hahahaha");//输出
#endif
return 0;
}
gcc -D A y.c //-D 带宏编译
2.#ifdef 标识符 //如果标识符已经定义 则编译程序段1 否则编译程序段2
程序段1
#else
程序段2
#endif
#include<stdio.h>
int main()
{
#ifdef A
puts("hahahaha");
#else
puts("heiheihei~");
#endif
return 0;
}
3。#if 常量/常量表达式 //如果为真 则编译程序段 否则 不编译 主要用于调试程序
程序段
#endif
#include<stdio.h>
int main()
{
#if 1
puts("pleaes input the num of stu:");
puts("hahahaha");
#endif
puts("heiheihei~");
return 0;
}
4.#if 常量/常量表达式 //如果为真则编译程序段1 否则编译程序段2
程序段1
#else
程序段2
#endif
#include<stdio.h>
int main()
{
#if 0
puts("pleaes input the num of stu:");
puts("hahahaha");
#else
puts("heiheihei~");
#endif
return 0;
}
5.自定义头文件
(1) 头文件:
系统头文件<>
自定义头文件""
//y.h
#ifndef Y_H
#define Y_H
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#endif
//y.c
#include"y.h"
int a=90;
int main()
{
if(a>=90)
{
printf(">=\n");
exit(-1);
}
return 0;
}
gcc y.c
./a.out
(2)头文件中条件编译作用:避免头文件重复包含
//a.h
#ifndef A_H
#define A_H
int max=100;
#endif
//b.h
#include"a.h"
//main.c
#include"a.h" //int max=100;
#include"b.h"
int main()
{
}
五、结构体大小-内存对齐
规则:
(1)数据成员对齐:第一个数据存放到相对结构体起始位置偏移为0的地址
(2)以后每个数据成员存储的起始位置从该成员大小的整数倍开始
(3)结构体大小 也就是sizeof的结果 必须是其内部最大成员与机器字长比较
按小的对齐 也就是结果是小的的整数倍 不足补齐
机器字长:CPU一次能处理数据位数
(4)特殊情况 如果结构体作为成员 则存储要从其内部最大元素大小的整数倍开始存储
(5)设置对齐参数
#include<stdio.h>
struct stu
{
char a;
short b;
char c;
};
int main()
{
struct stu s1;
printf("&s1.a:%p &s1.b:%p &s1.c:%p\n",&s1.a,&s1.b,&s1.c);
printf("%d\n",sizeof(s1));//6
return 0;
}
&s1.a:0xbfc88aaa &s1.b:0xbfc88aac &s1.c:0xbfc88aae
0 1 2 3 4 5 6 7 8
a b
#include<stdio.h>
struct stu
{
char a;
short b;
char c;
short d;
};
int main()
{
struct stu s1;
printf("&s1.a:%p &s1.b:%p &s1.c:%p\n",&s1.a,&s1.b,&s1.c);
printf("%d\n",sizeof(s1));//8
return 0;
}
#include<stdio.h>
struct s
{
double f;
char ch;
};
int main()
{
printf("%d\n",sizeof(struct s));//12
}
#include<stdio.h>
#pragma pack(3) //#pragma pack(n) n表示c编译器将按照n个字节对齐 n必须是2的次方
struct s
{
int f;
char ch;
};
int main()
{
printf("%d\n",sizeof(struct s));//6
}
练习:
struct p
{
char a;
int b;
char c
}
struct q
{
char ch;
struct p class;
}
sizeof(q) 12