指针函数
1. 概念
本质是函数,函数的返回值为指针。类比着指针数组。
指针数组:本质是数组,数组中存放指针。
数据类型 *数组名[元素个数];
int a[2][3];
int *arr[2] ={a[0],a[1]};
//*(*(arr+i)+j) *(arr[i]+j) arr[i][j]
2. 定义格式
格式:
数据类型 *函数名(参数列表)
{
函数体;
return 地址; //当失败时一般返回NULL
}
3. 函数内开辟空间
案例一:
#include <stdio.h>
char *fun()
{
char buf[32] = "hello";
return buf;
}
int main(int argc, char const *argv[])
{
char *p = fun();
printf("%s\n", p);
return 0;
}
结果未知,因为返回局部变量地址,函数调用结束后没有权限使用栈区的空间了所以结果是不可控的。如果被其他进程占用,可能会报段错误或者打印奇怪的东西。
修改:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *fun()
{
char *p = (char *)malloc(32);
if (NULL == p)
{
printf("malloc lost\n");
return NULL;
}
// p="hello"; //让p又指向了常量区的hello而不指向堆区了
strcpy(p, "hello");
return p;
}
int main(int argc, char const *argv[])
{
char *p = fun();
if (NULL == p)
{
printf("fun err\n"); //如果函数返回值为NULL可以打印一句错误提示
return -1; //如果错误让主函数结束,程序不再往后运行了
}
printf("%s\n", p);
free(p);
p = NULL;
return 0;
}
案例二:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void fun(char *p) //p=NULL
{
p = (char *)malloc(32);
if (NULL == p)
{
printf("malloc lost\n");
}
strcpy(p, "hello");
}
int main(int argc, char const *argv[])
{
char *p = NULL;
fun(p);
printf("%s\n", p); //函数调用结束后,主函数中的p还是指向了NULL,打印所以段错误
free(p);
p = NULL;
return 0;
}
报端错误:因为主函数中p指向了NULL,函数内改变形参p不会影响到主函数中的变量p, 所以函数调用完后主函数中的p还是指向了NULL。
修改:传递二级指针
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void fun(char **p) //p = &q;
{
*p = (char *)malloc(32); //*p = *&q = q
if (NULL == *p)
{
printf("malloc lost\n");
}
strcpy(*p, "hello"); //*p = *&q = q
}
int main(int argc, char const *argv[])
{
char *q = NULL;
fun(&q); //函数调用结束后,q指向了堆区空间
printf("%s\n", q);
free(q);
q = NULL;
return 0;
}
案例三:数组传递
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void fun(int arr[5]) //arr类型为int *, 本质还是传递指针
{
arr[0] = 100;
printf("%ld\n", sizeof(arr));
}
int main(int argc, char const *argv[])
{
int a[5] = {1, 2, 3, 4, 5};
fun(a);
printf("%d\n", a[0]);
return 0;
}
传递数组的形式是为了让用户知道要传递的是一个数组,但本质还是传递数组的首地址,也就是传递指针。
函数指针
1. 概念
本质时指针,指向了函数。类比着数组指针。
数组指针:本质是指针,指向了数组的指针。
数组指针定义格式:数据类型 (*指针名)[列数];
int a[2][3];
int (*p)[3] = a;
//p[i][j] *(p[i]+j) *(*(p+i)+j)
2. 定义格式
2.1
数据类型 (*指针名)(参数列表);
函数名:函数地址
2.2 基本用法
#include <stdio.h>
int add(int a, int b) //add: 函数名,也就是函数的地址
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int main(int argc, char const *argv[])
{
int (*p)(int, int); //定义函数指针, 类型int (*)(int,int)
p = add;
printf("%d\n", p(1, 2));
printf("%d\n", add(1, 2));
p = sub;
printf("%d\n", p(1, 2));
printf("%d\n", sub(1, 2));
return 0;
}
把函数指针当成参数传递给函数:
一种接口,多种方法:
#include <stdio.h>
int add(int a, int b) //add: 函数名,也就是函数的地址
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int test(int (*p)(int, int), int a, int b) //p=add, a=5, b=6
{
return p(a, b); //add(5,6);
}
int main(int argc, char const *argv[])
{
printf("%d\n", test(add, 5, 6)); //11
printf("%d\n", test(sub, 5, 6)); //-1
return 0;
}
结构体内成员是函数指针(不常用但是看内核原码可能会见到)
#include <stdio.h>
struct test
{
int (*p)(int,int);
};
int add(int a,int b)
{
return a+b;
}
int main(int argc, char const *argv[])
{
struct test x;
x.p = add;
printf("%d\n", x.p(1,2));
return 0;
}
3. 函数指针数组
3.1 概念
本质是数组,数组中元素是函数指针
3.2 定义格式
数据类型 (*数组名[元素个数])(形参列表);
数据类型:和指向的函数返回值类型一致
形参列表:和指向的函数参数一致
3.3 赋值
int (*arr[3])(int,int) = {函数名};
#include <stdio.h>
int add(int a, int b) //add: 函数名,也就是函数的地址
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int main(int argc, char const *argv[])
{
int (*arr[2])(int, int) = {add, sub}; //函数指针数组
printf("%d %d\n", arr[0](1, 2), arr[1](3, 4));
return 0;
}
练习:
a) 一个整型数
int a;
b) 一个指向整型的指针
int *p=&a;
c)一个指向指针的指针,它指向的指针是一个指向一个整型数
int **q=&p;
d)一个有10个整型数的数组
int arr[10];
e)一个有10个指针的数组,该指针是指向一个整型数的
int *a[10];
f)一个指向有10个整型数数组的指针
int a[1][10]
int (*p)[10]=a;
int b[10]; //b=&b[0] &b表示整个数组的地址了也就是行地址
p = &b;
g)一个指向函数的指针, 该函数有一个整型参数并返回一个整型数
int (*p)(int);
h)一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数
int (*a[10])(int);
条件编译
按照条件是否满足决定代码是否被编译,是预处理指令。
简单来说就是 后面的条件语句(condition)如果执行结果不为0,则该#if语句块内的代码会被编译,否则就不会被编译。
1. 根据宏是否定义
#define 宏名
#ifdef 宏名
/*code1*/
#else
/*code2*/
#endif
执行顺序:宏名如果定义则编译code1,否则编译code2
例子:
2. 根据宏值
#define 宏名 值
#if 宏名
/*code1*/
#else
/*code2*/
#endif
执行顺序:宏的值为非0则编译code1,为0则编译code2
例子:
#include <stdio.h>
#define DEF 0
int main(int argc, char const *argv[])
{
#if DEF //如果DEF为真则编译下面代码,否则编译#else下面代码
printf("hello\n");
#else
printf("world\n");
#endif
return 0;
}
3. 防止头文件重复包含
放在头文件中:
#ifndef 宏名
#define 宏名
/*code*/
#endif
报错原因:多次引用了包含struct test结构体定义的头文件,所以造成结构体重复定义
C高级到今天算是告一段落,从周四开始给大家更新数据结构笔记。大家可以运用目前所学知识进一步完善学生成绩管理系统了。