指针
认识指针
指针 == 地址
变量访问的两种方式
指针变量 == 存放地址的变量
如何定义一个指针变量
如何使用一个指针变量
变量访问的两种方式
为什么需要指针
练习:指针指向固定的区域(例如:单片机、armbootloader)
作业
输入三个数a,b,c; 要求不管怎么输入,在输出的时候,a,b,c就是由大到小的顺序输出,用函数封装实现
通过指针引用数组
定义一个指针变量指向数组
指针变量保存了数组的首地址
指针增量和数组的关系
通过指针引用数组元素
下标法
指针法
偏移
偏移以后的内存还是指针,地址偏移一个字节数(int or char)
取内容
两种方法效率对比
练习1
练习2
#include <stdio.h>
void Input(int *p,int size)
{
int i;
for(i=0;i<size;i++)
{
printf("请输入第%d个数\n",i+1);
scanf("%d",p++);
}
}
void Output(int *p,int size)
{
int i;
for(i=0;i<size;i++)
{
printf("%d ",*p++);
}
}
void Revers(int *p,int size)
{
int i;
int j;
int cup;
for(i=0;i<size/2;i++)
{
j = size-1-i;
cup = *(p+i);
*(p+i) = *(p+j);
*(p+j) = cup;
}
}
int main()
{
int a[5];
int size = sizeof(a)/sizeof(a[0]);
Input(a,size);
putchar('\n');
Output(&a[0],size);
putchar('\n');
Revers(a,size);
Output(&a[0],size);
return 0;
}
指针和二维数组
二维数组 → 父数组+子数组 → 二维数组也是数组,只不过是数组元素也是数组(子数组)
思考问题1
a是谁的地址,a[0]又是什么,那*a或者*(a+0)呢
思考问题2
a[0]+1又是什么意思呢?
认知
总结
数组指针
函数指针
定义“函数地址”
如何定义一个函数指针变量
使用函数指针
函数调用概念和变量一样
直接访问:变量名(函数名) 间接访问:指针(函数指针)
好用之处
根据程序运行过程的不同情况,调用不同的函数————java接口
练习
#include <stdio.h>
int getmax(int a,int b)
{
return a>b ? a:b;
}
int getmin(int a,int b)
{
return a<b ? a:b;
}
int getsum(int a,int b)
{
return a+b;
}
int dataReceive(int a,int b,int (*p)(int a,int b))
{
int receive;
receive = (*p)(a,b);
return receive;
}
int main()
{
int a=10;
int b=20;
int x;
int result;
int (*p)(int a,int b);
printf("输入1取最大值,输入2取最小值,输入3求和\n");
scanf("%d",&x);
switch(x)
{
case 1: p = getmax; break;
case 2: p = getmin; break;
case 3: p = getsum; break;
default: puts("请输入正确的指令"); exit(-1); break;
}
result = dataReceive(a,b,p);
printf("结果:%d\n",result);
return 0;
}
回调函数的底层逻辑
线程
int pthread_create(pthread_t *id,const pthread_attr_t *attr, void*(*start_rtn)(void*), void *restrict arg);
QT的信号与槽
指针数组
定义:注意和数组指针的区别,面试题会考
他是数组,数组的每一项都是一个指针变量
指针函数(返回指针值的函数)
概念
练习
作业
#include <stdio.h>
int* getposperson(int pos,int (*pstu)[4])
{
int *p;
p = (int *)(pstu + pos);
return p;
}
int main()
{
int scores[3][4] =
{
{53,88,59,92},
{86,85,70,95},
{94,30,66,78},
};
int *ppos;
int pos;
printf("请输入学号:\n");
scanf("%d",&pos);
ppos = getposperson(pos,scores);
int flag;
for(int i=0;i<4;i++)
{
printf("%d ",*ppos++);
if(*ppos < 60)
{
flag = 1;
}
}
putchar('\n');
if(flag == 1)
{
printf("%d号学生有不及格\n",pos);
}
else
{
puts("该学生没有课程不及格");
}
return 0;
}
二级(多级)指针
写法
差别
保存的是指针变量(int *p)的地址,也就是一级指针的地址
通过函数调用来修改调用函数指针指向的时候,就像是通过函数调用修改某变量的值的时候一样
二级指针不能简单粗暴指向二维数组
总结
字符串
定义介绍
换个角度来说,其实就是字符数组
定义的几种格式
和整型数组在储存上的区别
字符数组的存储方式
字符串和字符数组的区别就在于字符串结尾有一个 ’\0‘ 标志
sizeof和strlen的区别
p是一个char *,sizeof计算的时候,得出的是计算机用多少个字节来表示一个地址
动态开辟字符串(按需申请空间)
1、在函数调用结束后栈内数据被清理掉,回收内存;堆只有在函数结束后才释放
2、p = NULL;将指针p设置为NULL,即空指针。在C语言中,NULL是一个宏定义,表示一个空指针常量,通常用来表示指针不指向任何有效的内存地址。将指针设置为NULL可以用来表示指针当前不指向任何有效的对象或内存位置,这在程序中是一种常见的做法,以避免指针悬挂(dangling pointer)或野指针(wild pointer)的问题。
malloc函数 (void *malloc(sizeof_t size))
1、在堆上面进行动态开辟的字符串(普通的数组、变量在栈上开辟空间)
2、一直while循环会耗尽堆上的数据资源,所以不用malloc要释放内存空间
free (void free(void *ptr))
realloc (void *realloc(void *ptr,size_t size))
memset(void *memset (void *str,int c,size_t n))
几种字符串常用的API
输出字符串
获取字符串
scanf("%s",p);
gets
因为本函数可以无限读取,易发生溢出。如果溢出,多出来的字符将被写入到堆栈中,这就覆盖了堆栈原先的内容,破坏一个或多个不相关变量的值
计算长度
拷贝
strcpy
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *mystrcpy(char *des,char *src)
{
if(des == NULL || src == NULL)
{
return NULL;
}
char *back = des;
while(*src != '\0')
{
*des = *src;
des++;
src++;
}
*des = '\0';
return back;
}
char *mystrcpy2(char *des,char *src)
{
if(des == NULL || src == NULL)
{
return NULL;
}
char *back = des;
while(*src != '\0')
{
*des++ = *src++;
}
*des = '\0';
return back;
}
char *mystrcpy3(char *des,char *src)
{
if(des == NULL || src == NULL)
{
return NULL;
}
char *back = des;
while((*des++ = *src++)!= '\0')
*des = '\0';
return back;
}
int main()
{
char str[128] = {'\0'};
char *p = "abcdefg";
strcpy(str,p);
puts(str);
return 0;
}
strncpy
断言
拼接
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
char *mystrcat(char *des,char *src)
{
assert(des != NULL && src != NULL);
char *back = des;
while(*des != '\0')
{
des++;
}
while((*des++ = *src++) != '\0');
*des = '\0';
return back;
}
char *mystrcat2(char *des,char *src)
{
char *back = des;
strcpy(des + strlen(des),src);
return back;
}
int main()
{
char str[128] = "tang shuai ";
char *p = "shi ni ba ba";
char *p2;
p2 = mystrcat2(str,p);
strcat(str,p);
puts(str);
puts(p2);
return 0;
}
strcat (char *strcat(char *dest,const char *src))
把src所指向的字符串(包括“\0”)复制到dest所指向的字符串后面(删除*dest原来末尾的“\0”)。要保证*dest足够长,以容纳被复制进来的*src。*src中原有的字符不变。返回指向dest的指针
比较 (比较字节的大小)
查找子字符
查找字串
strstr(char *strstr(char *str1, const char *str2))
返回值:若str2是str1的子串,则返回str2在str1的首次出现的地址;如果str2不是str1的子串,则返回NULL
字符串分割
特别要注意分割处理后原字符串 str 会变,原字符串的改动是切分符原位置均更改为 '\0'
结构体
初识
为什么要用结构体
定义一个结构体
它算是一个模板,一般不给赋具体的值,每一项在实际应用中并不是都要使用
成员列表(域表)
声明
初始化一个结构体变量并引用
初始化
引用
例题
例题:输入两个学生的名字,学号,成绩,输出成绩高的学生的信息
重点认识
例题:输入两个学生的名字,学号,成绩,输出成绩高的学生的信息
结构体数组
练习:选票系统
#include <stdio.h>
#include <string.h>
struct XuanMin
{
char name[32];
int tickets;
};
int main()
{
struct XuanMin toupiao[3];
struct XuanMin max;
int i;
int length = sizeof(toupiao)/sizeof(toupiao[0]);
int j;
int feipiao = 0;
int mark = 0;
int total = 5;
char tmpName[32];
for(i=0;i<length;i++)
{
toupiao[i].tickets = 0;
printf("请输入第%d个选民的名字:\n",i+1);
scanf("%s",toupiao[i].name);
}
for(i=0;i<total;i++)
{
mark = 0;
printf("你投票给谁:\n");
memset(tmpName,'\0',sizeof(tmpName));
scanf("%s",tmpName);
for(j=0;j<length;j++)
{
if(strcmp(tmpName,toupiao[j].name) == 0)
{
toupiao[j].tickets++;
mark = 1;
}
}
if(mark == 0)
{
printf("弃票\n");
feipiao++;
}
}
for(i=0;i<length;i++)
{
printf("名字:%s,票数:%d\n",toupiao[i].name,toupiao[i].tickets);
}
max = toupiao[0];
for(i=0;i<length;i++)
{
if(max.tickets < toupiao[i].tickets)
{
max = toupiao[i];
}
}
printf("%s以%d票当选!!!\n废票%d张\n",max.name,max.tickets,feipiao);
return 0;
}
结构体指针
概念
通过结构体指针访问结构体
结构体数组,指针,函数应用
#include <stdio.h>
#include <string.h>
struct XuanMin
{
char name[32];
int tickets;
};
int main()
{
struct XuanMin toupiao[3];
struct XuanMin *p = toupiao;
struct XuanMin max;
int i;
int length = sizeof(toupiao)/sizeof(toupiao[0]);
int j;
int feipiao = 0;
int mark = 0;
int total = 5;
char tmpName[32];
for(i=0;i<length;i++)
{
toupiao[i].tickets = 0;
printf("请输入第%d个选民的名字:\n",i+1);
scanf("%s",p->name);
p++;
}
p = toupiao;
for(i=0;i<total;i++)
{
mark = 0;
printf("你投票给谁:\n");
memset(tmpName,'\0',sizeof(tmpName));
scanf("%s",tmpName);
for(j=0;j<length;j++)
{
if(strcmp(tmpName,p->name) == 0)
{
p->tickets++;
mark = 1;
}
p++;
}
if(mark == 0)
{
printf("弃票\n");
feipiao++;
}
p = toupiao;
}
p = toupiao;
for(i=0;i<length;i++)
{
printf("名字:%s,票数:%d\n",p->name,p->tickets);
p++;
}
max = toupiao[0];
p = toupiao;
for(i=0;i<length;i++)
{
if(max.tickets < p->tickets)
{
max = toupiao[i];
}
p++;
}
printf("%s以%d票当选!!!\n废票%d张\n",max.name,max.tickets,feipiao);
return 0;
}
选民系统小综合
共用体/联合体
概念
与结构体的区别
1、结构体元素有各自单独空间 ;共用体元素共享空间,空间大小有最大类型确定
2、结构体元素互不影响 ;共用体赋值会导致覆盖
3、联合体的地址多大取决于联合体中数据的最大空间数
#include <stdio.h>
struct Test1
{
int data1;
char data2;
float data3;
};
union Test2
{
int data1;
char data2;
float data3;
};
int main()
{
struct Test1 t1;
struct Test1 *p0 = &t1;
union Test2 t2;
union Test2 *p1 = &t2;
printf("%d\n",sizeof(t1));
printf("%d\n",sizeof(t2));
printf("%p\n",&p0->data1);
printf("%p\n",&p0->data2);
printf("%p\n",&p0->data3);
printf("%p\n",&p1->data1);
printf("%p\n",&p1->data2);
printf("%p\n",&p1->data3);
putchar('\n');
printf("%p\n",&t1.data1);
printf("%p\n",&t1.data2);
printf("%p\n",&t1.data3);
printf("%p\n",&t2.data1);
printf("%p\n",&t2.data2);
printf("%p\n",&t2.data3);
return 0;
}
注意数据覆盖
应用
#include <stdio.h>
#include <string.h>
struct Person
{
int age;
char name[32];
char occupation;
union
{
int class;
char subject[12];
}mes;
};
int main()
{
int i;
struct Person p[2];
for(i=0;i<2;i++)
{
printf("请输入职业:;'t'代表老师,'s'代表学生\n");
scanf("%c",&(p[i].occupation));
if(p[i].occupation == 't')
{
printf("请输入老师教的科目:\n");
scanf("%s",p[i].mes.subject);
printf("请输入老师的名字:\n");
scanf("%s",p[i].name);
}
else
{
printf("请输入学生的班级:\n");
scanf("%d",&(p[i].mes.class));
printf("请输入学生的名字:\n");
scanf("%s",p[i].name);
}
getchar();
}
for(i=0;i<2;i++)
{
if(p[i].occupation == 't')
{
printf("老师教的科目是:%s\n老师的名字是:%s\n",p[i].mes.subject,p[i].name);
}
else
{
printf("学生的班级是:%d\n学生的名字是:%s\n",p[i].mes.class,p[i].name);
}
}
return 0;
}
枚举类型
什么是枚举类型
如果一个变量只有规定的几种可能的值,比如星期一、二、三 ……
enum Weekday {Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday};
怎么定义枚举类型
枚举变量
只限列表中的集中情况
值默认从0开始,枚举元素不能被赋值,虽然看着像变量
可以指定列表中枚举数的值
可直接忽略枚举类型名,直接定义枚举变量
例子
typedef关键字
给已有的变量类型换名字(OS:换一个简单的名字)
一般配合结构体,简单方便,不要每次都用struct开头
#include <stdio.h>
typedef unsigned int u8;
typedef int u16;
typedef int arr[10];
struct Test
{
int data1;
int data2;
};
typedef struct Test T;
typedef struct
{
int data1;
int data2;
}Demo;
void printInto(T t)
{
printf("%d ",t.data1);
}
int main()
{
arr a;
a[0] = 10;
printf("%d\n",a[0]);
struct Test t1;
t1.data1 = 100;
printf("%d ",t1.data1);
T t2;
t2.data1 = 1000;
printInto(t2);
Demo d;
d.data2 = 999;
printf("%d ",d.data2);
return 0;
}
案例
#include <stdio.h>
typedef struct
{
int num;
char name[32];
char sex;
}Person,*pPerson;
void printInto(Person p)
{
printf("%d号是:%s %c\n",p.num,p.name,p.sex);
}
void printInto2(pPerson pp)
{
printf("%d号是:%s %c\n",pp->num,pp->name,pp->sex);
}
void printInto3(Person *p)
{
printf("%d号是:%s %c\n",p->num,p->name,p->sex);
}
int main()
{
Person p1 = {1,"小美",'g'};
Person p2 = {2,"小帅",'m'};
printInto(p1);
printInto(p2);
pPerson pp1 = &p1;
pPerson pp2 = &p2;
printInto3(pp1);
printInto2(pp2);
Person *pp3 = &p1;
Person *pp4 = &p2;
printInto3(pp3);
printInto3(pp4);
return 0;
}