指针
9.为什么需要用指针?
①若要通过函数调用来改变调用者里的局部变量,就要传地址,可以通过指针的方式来修改main函数的数据
如实现两数交换:
void changeData(int* data1, int* data2)
{
int tmp;
tmp = *data1;
*data1 = *data2;
*data2 = tmp;
}
②指针能指向固定区域
volatile unsigned int* p = (volatile unsigned int*)0x0000FE1526;
printf("固定地址为:%p\n", p);
10.通过指针引用数组
指针可以当作数组名,并用下标法访问,数组名可以拿来加,但是a++是不可以的,因为数组名为常变量指针
将数组中的n个元素按逆序存放
//初始化数组
void initArr(int* arr, int len)
{
for (int i = 0; i < len; i++)
{
scanf("%d", arr++);
}
}
//遍历数组并输出
void printArr(int* arr, int len)
{
for (int i = 0; i < len; i++)
{
printf("%d ", *arr++);
}
putchar('\n');
}
//将数组里的值逆序存
void revangArr(int* arr, int len)
{
int i;
int j;
int tmp;
for (i = 0; i < len / 2; i++)
{
j = len - i - 1;
tmp = *(arr+i);
*(arr + i) = *(arr + j);
*(arr + j) = tmp;
}
}
int main()
{
int array[6];
initArr(array, 6);
printArr(array, 6);
revangArr(array, 6);
printArr(array, 6);
}
11.指针和二维数组
int arr[3][4]= { {11,22,33,44},{1,2,3,4},{12,4} };
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("a[%d][%d]的地址是:%p,值为:%d\n", i,j,&arr[i][j], arr[i][j]);
printf("a[%d][%d]的地址是:%p,值为:%d\n", i, j, arr[i]+j,*(arr[i]+j));
printf("a[%d][%d]的地址是:%p,值为:%d\n", i, j, *(arr+i)+j,*(*(arr+i)+j));
//以上三个输出结果是一样的
}
运行结果:
12.数组指针
int (*p)[4]
int arr[3][4] = { {11,22,33,44},{1,2,3,4},{12,4} };
int i, j;
int* p = arr;
//定义一个指针,让指针偏移的时候,也偏移对应大小的数组
//数组指针,定义一个指针,指向一个数组;
//数组指针才是真正等同于二维数组名
int(*p2)[4] = arr;
printf("p2的地址是%p\n", p2);
printf("++p=%p,++p2=%p\n", p + 1, p2 + 1);//p+1偏移4个字节,p2 + 1偏 //移4*4=16个字节
for (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++) {
printf("%d\n", *(*(p2 + i) + j));
}
}
13.函数指针
13.1函数名就是地址
用函数指针来进行函数调用
void printWelcome()
{
puts("欢迎光临\n");
}
int getData(int data)
{
return ++data;
}
int main()
{ void (*p)();
int (*p2)(int data)
p = printWelcome;
p2 = getData;
(*p)();//函数调用
printf("%d",(*p2)(10));
return 0;
}
13.2函数指针能根据程序运行过程的不同情况,调用不同的函数
例:有两个整数a和b,由用户输入1,2或3。如输入1,程序就给出a和b中大
者,输入2,就给出a和b中小者,输入3,则求a与b之和。
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 dataHandle(int a, int b, int(*p)(int, int))
{
return (*p)(a, b);
}
int main()
{
int data1;
int data2;
int cmd;
int (*p)(int, int);
int ret;
printf("请输入两个整数:\n");
scanf("%d%d", &data1, &data2);
printf("请输入指令:");
scanf("%d", &cmd);
switch (cmd)
{
case 1:
p = getMax;
break;
case 2:
p = getMin;
break;
case 3:
p = getSum;
break;
default:
printf("指令错误");
exit(-1);
}
ret = dataHandle(data1, data2, p);
printf("%d", ret);
return 0;
}
14.指针数组
数组的每一项都是一个指针变量
int a = 1;
int b = 2;
int c = 3;
int* p[3] = { &a,&b,&c };
for (int i = 0; i < 3; i++){
printf("%d ", *p[i]);
}
函数指针数组,对于13.2的例子可将程序改写成:
int (*p[3])(int, int);
p[0] = getMax;
p[1] = getMin;
p[2] = getSum;
printf("大值:%d,小值:%d,和:%d\n", (*p[0])(1,2), (*p[1])(1,2), (*p[2])(1,2));
15.指针函数
函数的返回值为指针
有3个学生,每个学生有4门课程的成绩。要求在用户输入学生序号以后,
能输出该学生的全部成绩。用指针函数来实现。
int* getPos(int pos ,int (*ppos)[4])
//int (*ppos)[4]为数组指针,即该指针指向一个数组
{
int* p;
p = (int *)(ppos + pos);
return p;
}
int main()
{
int score[3][4] = { {88,55,99,66},{75,98,64,85},{66,44,88,76} };
int pos;
int* ret;
printf("请输入学生序号\n");
scanf("%d", &pos);
ret = getPos(pos, score);
printf("学生%d的成绩为:\n", pos);
for (int i = 0; i < 4; i++)
{
printf("%d ", *(ret+i));
}
return 0;
}
对上例中的学生,找出其中有不及格的课程的学生及其学生号。
int* research(int(*ppos)[4])
{
int* q=NULL;//防止野指针
for (int i = 0; i < 4; i++)
{
if (*(*ppos + i) <= 60) {
q = ppos;
}
}
return q;
}
//
int main()
{
int score[6][4] = { {88,59,99,66},{75,98,64,85},{66,44,88,56} };
int* num;
for (int i = 0; i < 3; i++) {
num = research(score + i);
if (num == (score + i))
{
printf("不及格的学生序号为:%d\n", i+1);
}
}
return 0;
}
字符串拼接:
char* myStrcat(char* des, char* src)
{
assert(des != NULL || src != NULL);
char* data = des;
while (*des) {
des++;
}
while (*src!='\0')
{
*des++ = *src++;
}
*des = '\0';
return data;
}
int main()
{
char cdata1[10] = "chen";
char* p = "xiao";
char* p2;
p2 = (char*)malloc(1);
*p2 = 'c';
myStrcat(cdata1, p);
printf("%s\n", cdata1);
printf("%c\n", *p2);
}
字符串
16.字符串常用的两种定义格式:
char str[ ] = "hello";
char *p =“hello”
两者区别前者是是字符串变量,后者是字符串常量,不允许被修改
以上定义的字符串多了结束标志‘\0’
ps:C语言只有在定义字符数组的时候才能用“=”来初始化变量,其它情况下是不能直接用“=”来为字符数组赋值的,之所以不能赋值成功,是因为数组名是一个指针常量,指向固定地址,再对其赋值即改变其指向的地址,作为常量自然不同意。
要为字符数组赋值可以用string.h头文件中的strcpy函数来完成。
例如:
char a[10] = "123"; /*正确,在定义的时候初始化*/
char a[10];
a = "123"; /*错误,不能用“=”直接为字符数组赋值*/
strcpy(a, "123"); /*正确,使用strcpy函数复制字符串*/
17.sizeof和strlen的区别
- 动态开辟字符串
函数:
- 常用函数
拷贝:char *strcpy(char* dest, const char *src);
char *strncpy(char *dest, const char *src, int n)
断言:assert
char* myStrcpy1(char* des, char* src)
{
char* data = des;
//assert 的作用是现计算表达式expression ,如果其值为假(即为0)
//那么它先向stderr打印一条出错信息,然后通过调用abort来终止程序运行
assert(src == NULL || des == NULL);//断言
//if (src == NULL || des == NULL) {
return NULL;
}
while (*src != '\0') {
*des = *src;
des++;
src++;
}
*des = '\0';
return data;
}
拼接:char *strcat(char *dest, const char *src);
将src拼接到dest后面
比较:
结构体
20.结构体数组,指针,函数应用
结构体指针注意:1.把以前的普通变量名,或者下标访问的.运算符,改成结构体指针的->
2.指针++,每次遍历会到数组尾巴,下次遍历之前记得回来(重新指数组头)!
做一个选票系统(要避免野指针!)
struct xuanMin* initXm(struct xuanMin* p, int* pn)
{
if (p == NULL) {
printf("有几个候选人\n");
scanf("%d", pn);
p = (struct xuanMin*)malloc(*pn * sizeof(struct xuanMin));
}
for (int i = 0; i < *pn; i++) {
p->ticket = 0;
printf("请输入选民的候选人名字:\n");
scanf("%s", p->name);
p++;
}
return p - *pn;
}
void printXm(struct xuanMin* p, int len)
{
int i;
for (i = 0; i < len; i++) {
printf("%s的票数为:%d\n",p->name, p->ticket);
p++;
}
}
int dovot(struct xuanMin* p, int len)
{
int feipiao = 0;
int flag;
char tmpname[32];
struct xuanMin* pbak = p;
for (int i = 0; i < 5; i++) {
flag = 0;
p = pbak;
printf("请输入你要投票的候选人名字:\n");
memset(tmpname, '\0', sizeof(tmpname));
scanf("%s", tmpname);
for (int j = 0; j < len; j++) {
if (strcmp(tmpname, p->name) == 0) {
p->ticket++;
flag = 1;
}
p++;
}
if (flag == 0) {
printf("无此候选人,弃票\n");
feipiao++;
}
}
return feipiao;
}
struct xuanMin getMax(struct xuanMin* p, int len)
{
struct xuanMin max = *p;
for (int i = 0; i < len; i++) {
if (max.ticket < p->ticket) {
max = *p;
}
p++;
}
return max;
}
int main()
{
struct xuanMin* xm = NULL;
int total=0;
xm = initXm(xm, &total);
int feipiao = dovot(xm, total);
struct xuanMin max = getMax(xm, total);
printXm(xm, total);
printf("%s当选,票数为:%d,废票数为:%d", max.name, max.ticket, feipiao);
return 0;
}
共用体/联合体
- 结构体元素有各自单独空间 共用体元素共享空间,空间大小有最大类型确定
- 结构体元素互不影响 共用体赋值会导致覆盖
ps:在用scanf或getchar连续输入字符时,需要主要清空缓冲区!
有若干个人员的数据,其中有学生和教师。学生的数据中包括:姓名、号码、性别、职业、班级。教师的数据包括:姓名、号码、性别、职业、职务。要求用同一个表格来处理。
struct person
{
char people;
char name[32];
char zhiye;
union
{
int class;
char zhiwu;
}mes;
};
int main()
{
struct person u[3];
for (int i = 0; i < 3; i++) {
printf("请输入是学生还是老师,学生输入s,老师输入t:\n");
scanf("%c", &(u[i].people));
if(u[i].people == 's') {
printf("请输入学生名字\n");
scanf("%s", u[i].name);
printf("请输入信息,学生输入班级(整形数字),\n");
scanf("%d", &(u[i].mes.class));
}
else if (u[i].people == 't') {
printf("请输入老师名字\n");
scanf("%s", u[i].name);
printf("请输入信息,老师输入职务(字符),\n");
getchar();
scanf("%c", &(u[i].mes.zhiwu));
}
int c;
// 未读到文件结尾前提下,清空换行符前的所有字符
while ((c=getchar()) != EOF && c != '\n') {}
}
for (int i = 0; i < 3; i++) {
if (u[i].people == 's') {
printf("%c\n名字:%s,信息:%d\n", u[i].people, u[i].name, u[i].mes.class);
}
else if (u[i].people == 't') {
printf("%c\n名字:%s,信息:%c\n", u[i].people, u[i].name, u[i].mes.zhiwu);
}
}
return 0;
}
枚举类型
typedef关键字
给已经有的变量类型起名字,一般配合结构体用