C语言提高

2023-05-16

//头文件函数声明
//防止头文件重复包含(相互包含陷入包含死循环)
#pragma once
//兼容C++编译器
//如果是C++编译器,按c标准编译
#ifdefine __cplusplus  //C语言编译器中已有的宏,不是自定义宏
extern "C"
{
#endif



//接口函数


#ifdefine __cplusplus 
extern "C"
}
#endif

//在a.cpp文件中调用b.c文件中函数,而又不想将b.c文件改成b.cpp文件,则需要在b.h文件加入上述代码,让其按照c标准编译

CS:客户端服务器

BS:浏览器和服务器(可以认为其是一种特殊的CS)

学习标准

第一套api接口

//初始化socket句柄的接口
int  socket_client_init(void **handle);
//发送socket网络报文
int socket_client_send(void *handle, unsigned char *buf, int buf_len);
//接收socket网络报文
int socket_client_recv(void *handle, unsigned char *buf, int *buf_len);
//释放socket句柄资源
int destory_socket(void *handle);

第二套api接口

//初始化socket句柄的接口
int  socket_client_init(void **handle);
//发送socket网络报文
int socket_client_send(void *handle, unsigned char *buf, int buf_len);
//接收socket网络报文
int socket_client_recv(void *handle, unsigned char **buf, int *buf_len);
//
socket_client_free(unsigned char **buf);
//释放socket句柄资源
int destory_socket(void **handle);

void类型

①函数参数为空,定义函数时,可以用void修饰;

②函数没有返回值;

③不能定义void类型的普通变量:void a;//err,无法确定类型,不同类型分配空间不一样(数据类型本质,固定内存块大小别名)

④可以定义void *变量: void * p;//合法,32位机器占4个字节,64位机器占8个字节

⑤void *p 万能指针,函数返回值,函数参数。

内存四区

//"abcdef"保存在字符串常量区
char *get_str1()
{
    char *p ="abcdef";//p指向文字常量区

    return p;
}

char *get_str2()
{
    char str[] ="abcdef";//str空间为栈区,将文字常量区的内容拷贝到str空间,此函数调用完成后str所指向的内存区域被释放

    return str;
}

char *get_str3()
{
    char *tmp = (char *)malloc(100);//堆区,由程序员释放
    if(tmp == NULL)
    {
        return NULL;
    }
    strcpy(tmp,"abcdef");
    return tmp;
}

int main(void)
{
    char *p1 =NULL;
    char *p2 =NULL;
    char *p3 =NULL;

    p1=get_str1();
    printf("%s\n",p1);

    p2=get_str2();
    //get_str2()运行完毕,str空间自动回收,str的空间内容未知,有可能保留之前的内容,也有可能是乱码
    printf("%s\n",p2);

    p3=get_str3();
    if(p3!=NULL)
    {
       printf("%s\n",p3);
       free(p3);//由程序员释放
       p3=NULL;//养成良好习惯
    }
    return 0;
}

函数的调用模型:先进后出

栈的生长方向和内存的存放方向:栈的生长方向为由高到底,而内存以及堆的生长方向由底到高

程序验证

#include<stdio.h> 
#include<stdlib.h> 
#include<string.h> 

char *get_str1()
{
    char *tmp = (char *)malloc(100);//堆区,由程序员释放
    if(tmp == NULL)
    {
        return NULL;
    }
    strcpy(tmp,"abcdef");
    return tmp;
}

char *get_str2()
{
    char *tmp = (char *)malloc(100);//堆区,由程序员释放
    if(tmp == NULL)
    {
        return NULL;
    }
    strcpy(tmp,"taylen");
    return tmp;
}
int main()
{
	//栈的生长方向 
	int a; 
	int b; 
	printf("栈的生长方向\n"); 
	printf("&a: %p\n", &a); 
	printf("&b: %p\n", &b);
	
	//堆的生长方向 
	char * p1=NULL; 
	char * p2=NULL; 
	p1 = get_str1();
	p2 = get_str2();
	printf("堆的生长方向\n");
	printf("p1的地址:%p\n",p1);
	printf("p1+1地址:%p\n",p1+1);
	
	printf("p1的地址:%p\n",p1);
	printf("p2的地址:%p\n",p2);
	free(p1);
	free(p2);
	
	//内存的生长方向
	char buf[4]; 
	printf("内存的生长方向\n");
	printf("buf的地址: %p\n", &buf[0]); 
	printf("buf+1地址: %p\n", &buf[1]); 
	return 0; 
}

输出结果:

①栈区地址生长方向:地址由上往下递减

②堆区地址生长方向:地址由下往上递增

③数组buf,buf+1地址永远递增

指针易错点

①写内存时,一定要内存可写

char *buf1 = "abcdefg";//文字常量区,内存不可改

buf1[2] = '1';//err

char buf2[] = "abcdefg";/

buf2[2] = '1';//ok

②不允许向NULL和未知非法地址拷贝内存

指针强化

如何定义合适类型的指针变量:某个变量地址需要定义一个怎样类型的变量保存;在这个类型的基础上加一个*

int b;
int *q = &b;
int ** t =&q

如果想通过函数形参改变实参的值,必须传地址:

1、值传递,形参的任何修改不会影响到实参

2、地址传递,形参(通过*操作符号)的任何修改会影响到实参

主调函数与被调函数:

a)主调函数可把堆区、栈区、全局数据内存地址传给被调函数

b)被调函数只能返回堆区、全局数据

内存分配:

a)指针做函数参数,是输入与输出特性的(输入:主调函数分配内存;输出:被调函数分配内存)

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

void fun1(char *p /*in*/)
{
    if(p == NULL)
    {
        return;
    }
    //给p指向的内存区域拷贝
    strcpy(p,"PointerAsInput");
}
void fun2(char **p /*out*/,int *len)
{
    if(p == NULL)
    {
        return;
    }
    char *tmp = (char *)malloc(100);
    if(tmp == NULL)
    {
        return;
    }
    strcpy(p,"PointerAsOutput");
    //简洁赋值
    *p =tmp;
    *len =strlen(tmp);

}
int main(void)

{
    //输入,主调函数分配内存
    char buf[100]={0};
    fun1(buf);
    printf("buf = %s\n",buf);

    //输出,被调用函数分配内存,地址传递
    
    char *p = NULL;
    int len = 0;
    fun2(&p);
    if(p!=NULL)
    {
        printf("p = %s,len = %d\n",p,len);
        free(p);
        p=NULL;
    }
    system("pause");
    return 0;
}

字符串

/*C语言没有字符串类型,通过字符数据模拟

C语言字符串,以字符'\0'结尾,数字0*/

//不指定⻓长度,C编译器会⾃自动帮程序员求元素的个数 
//buf1是⼀一个数组  不是⼀一个以0结尾的字符串 
char  buf1[]  =  {'a',  'b',  'c',  'd'};  
printf("buf1 = %s\n",buf1)  //结果会是abcd+乱码

//用字符串初始化字符数组 ,常用
    char  buf2[]  =  "abcd";        //buf2  作为字符数组  有5个字节 
                                //       作为字符串有  4个字节 
    int  len  =  strlen(buf2); 
    printf("buf2字符的⻓长度:%d  \n",  len);     //4 
    //buf3  作为数组  数组是⼀一种数据类型  本质(固定⼩小⼤大内存块的别名) 
    int  size  =  sizeof(buf2);  // 
    printf("buf2数组所占内存空间⼤大⼩小:%d  \n",  size);  //5 

指针和字符串数组名是否等价?

char buf[] = "abcdefg";
char *p =NULL;
p = buf;
p++;//没问题
buf++;//报错
/*buf只是一个常量,不能修改,主要是为了确保最终内存可以回收*/

const的使用

const声明的变量只能被读 ,且必须被初始化。但可以通过指针间接修改。

1)const声明变量为只读

const int a = 10;

a=100;//err

char buf[100]="abcd";
const char *p=buf;
char const *p=buf;//修饰*,指针指向能变,指针指向的内存不能变
//p[0]='1';//err
p = "123456";//ok

char *const p1 = buf;//修饰指针变量,指针指向的内存能变,指针指向不能变

//p1="123456";//err

p1[0]='1';//ok


const char* const p2=buf;//p2,只读

 

二级指针

如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针 变量。也称为“二级指针”。

指针数组:

//指针数组,指针的数组,它是一个数组,每个元素都是char*
char *p[] = {"1111","0000","aaaa","bbbb"};
char **q = {"1111","0000","aaaa","bbbb"};//err

//只有在作为函数参数时两者可通用

二级指针做函数参数输出特性

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int getMem(char *p)
{
	p = (char *)malloc(sizeof(char) * 100);
	if (p == NULL)
	{
		return -1;
	}
	//p = "abcdef";
	strcpy(p, "abcdef");
	printf("p = %s\n", p);
	return 0;
}
int main(void)

{
	char *p = NULL;
	int ret = 0;
	ret = getMem(p);
	if (ret != 0)
	{
		printf("getMem err: %d\n", ret);
		return ret;
	}
	printf("p = %s", p);
	printf("\n");
	system("pause");
	return 0;
}
//输出结果
/*
p = abcdef
p = (null)
*/

上面的例子,在执行语句ret = getMem(p)时,只是值传递,拷贝了实参p的值赋值给形参p,即形参p的值一开始即为NULL,随后通过malloc分配了内存,并将该内存中填入“abcd”,但其并未与实参p有实际对应关系。相应的正确代码如下(地址传递,形参修改会影响到实参):

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int getMem2(char **p)
{
	if (p == NULL)
	{
		return -1;
	}
	char * tmp;
	tmp = (char *)malloc(sizeof(char) * 100);
	if (tmp == NULL)
	{
		return -2;
	}
	//p = "abcdef";
	strcpy(tmp, "abcdef");
	
	*p = tmp;
	return 0;
}
int main(void)

{
	char *p = NULL;
	int ret = 0;
	ret = getMem2(&p);
	if (ret != 0)
	{
		printf("getMem err: %d\n", ret);
		return ret;
	}
	printf("p = %s", p);
	if (p != NULL)
	{
		free(p);
		p = NULL;
	}

	printf("\n");
	system("pause");
	return 0;
}

对应的内存四区图如下:

二级指针输入特性:

第一种输入模型

char *myArray[] = {"aaaaaa", "ccccc", "bbbbbb", "111111"}; 
void printMyArray(char **myArray, int num); 
void sortMyArray(char **myArray, int num);

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


void print_array(char **p, int n)
//void print_array(char *p[], int n)与上面相同
{
	int i = 0;
	for (i = 0; i < 4; i++)
	{
		printf("%s, ", p[i]);
	}
	printf("\n");
}

void sort_array(char *p[], int n)
{
	char *tmp = NULL;
	for (int i = 0; i < n - 1; i++)
	{
		for (int j = i + 1; j < n; j++)
		{
			if (strcmp(p[i], p[j]) > 0)
			{
				tmp = p[j];
				p[j] = p[i];
				p[i] = tmp;

			}
		}
	}
}

int main(void)

{	
	//指针数组
	char *p[] = { "111111","000000","bbbbbb","aaaaaa" };
	//char **q = { "111111","000000","bbbbbb","aaaaaa" };err
	int n = sizeof(p) / sizeof(p[0]);
	printf("n = %d\n", n);

	printf("排序前\n");
	print_array(p, n);

	//选择法排序
	sort_array(p, n);

	printf("排序后\n");
	print_array(p, n);
	system("pause");
	return 0;
}

第二种输入模型:

char myArray[10][30] = {"aaaaaa", "ccccc", "bbbbbbb", "1111111111111"}; 
void printMyArray(char myArray[10][30], int num); 
void sortMyArray(char myArray[10][30], int num);

二维数组,数组名代表首行地址。首行地址和首行首元素地址有区别,但值是一样的。

区别:步长不一样

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

//二维数组作为函数参数时,函数参数不能写成如下形式
/*
void print_array(char **a, int n)
{
	printf("a  :%d,a +1 :%d\n", a, a + 1);//二维数组退化为指针,步长已发生改变
	int i = 0;
	for (i = 0; i < n; i++)
	{
		printf("%s, ", a[i]);
	}
	printf("\n");
}
*/
void print_array(char a[][20] ,int n)//20即为步长
{
	printf("a  :%d,a +1 :%d\n", a, a + 1);
	int i = 0;
	for (i = 0; i < n; i++)
	{
		printf("%s, ", a[i]);
	}
	printf("\n");
}

void sort_array(char a[][20], int n)
{
	char tmp[20];
	for (int i = 0; i < n - 1; i++)
	{
		for (int j = i + 1; j < n; j++)
		{
			if (strcmp(a[i], a[j]) > 0)
			{
				strcpy(tmp, a[i]);
				strcpy(a[i], a[j]);
				strcpy(a[j], tmp);

			}
		}
	}
}

int main(void)

{
	char a0[20] = "000000";
	char a1[20] = "111111";
	char a2[20] = "bbbbbb";
	char a3[20] = "cccccc";



	//二维数组第一个[]可以不填,但条件是必须要初始化
	char a[][20] = { "111111","000000","bbbbbb","aaaaaa" };
	int n = sizeof(a) / sizeof(a[0]);
	printf("a0 :%d,a0+1 :%d\n", a0, a0 + 1);
	printf("&a0:%d,&a0+1:%d\n", &a, a + 1);
	printf("a  :%d,a +1 :%d\n", a, a + 1);
	printf("&a:%d,&a+1:%d\n", &a, &a + 1);

	printf("排序前\n");
	print_array(a, n);

	//选择法排序
	sort_array(a, n);

	printf("排序后\n");
	print_array(a, n);
	system("pause");
	return 0;
}
/*
a0 :11532704,a0+1 :11532705
&a0:11532532,&a0+1:11532552
a  :11532532,a +1 :11532552
&a:11532532,&a+1:11532612
排序前
a  :11532532,a +1 :11532552
111111, 000000, bbbbbb, aaaaaa,
排序后
a  :11532532,a +1 :11532552
000000, 111111, aaaaaa, bbbbbb,
*/

第三种输入模型

char **myArray = NULL; 
char **getMem(int num); 
void printMyArray(char **myArray, int num); 
void sortMyArray(char **myArray, int num); 
void arrayFree(char **myArray, int num);

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(void)

{
	char *p0 = NULL;
	p0 = malloc(100);
	strcpy(p0, "abcdefg");

	int i = 0;
	char *p[3] = { 0 };
	for (i = 0; i < 10; i++)
	{
		p[i] = malloc(100);
		strcpy(p[i], "abcdefg");
	}


	int a[3];
	int *q = (int *)malloc(3 * sizeof(int));//相当于int q[10]

	//动态分配一个数组,每个元素都是char*
	//char*buf[3]
	int n = 3;
	char **buf = (char **)malloc(n * sizeof(char *));//相当于char *buf[3],buf指向堆区
	if (buf == NULL)
	{
		return -1;
	}
	//strcpy(buf[0], "abcdefg");
	for (i = 0; i < n; i++)
	{
		buf[i] = (char *)malloc(30 * sizeof(char));
		char str[30];
		sprintf(str, "test%d%d", i, i);
		strcpy(buf[i], str);
	}
	for (i = 0; i < n; i++)
	{
		printf("%s, ", buf[i]);
	}

	printf("\n");
	for (i = 0; i < n; i++)
	{
		free(buf[i]);
		buf[i] = NULL;
	}
	if (buf != NULL)
	{
		free(buf);
	}
	system("pause");
	return 0;
}

内存四区图如下:

封装函数后代码如下:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

char **getMem(int n)
{
	int i = 0;
	char **buf = (char **)malloc(n * sizeof(char *));//相当于char *buf[3],buf指向堆区
	if (buf == NULL)
	{
		return NULL;
	}
	//strcpy(buf[0], "abcdefg");
	for (i = 0; i < n; i++)
	{
		buf[i] = (char *)malloc(30 * sizeof(char));
		char str[30];
		sprintf(str, "test%d%d", i, i);
		strcpy(buf[i], str);
	}
	return buf;
}

void print_buf(char **buf,int n)
{
	int i = 0;
	for (i = 0; i < n; i++)
	{
		printf("%s, ", buf[i]);
	}
}
void free_buf(char **buf,int n)
{
	int i = 0;
	for (i = 0; i < n; i++)
	{
		free(buf[i]);
		buf[i] = NULL;
	}
	if (buf != NULL)
	{
		free(buf);
		buf = NULL;
	}
}
int main(void)

{
	char **buf = NULL;
	int n = 3;
	buf = getMem(n);
	if (buf == NULL)
	{
		printf("getMem err\n");
		return -1;
	}
	print_buf(buf,n);
	free_buf(buf, n);
	buf = NULL;
	printf("\n");
	system("pause");
	return 0;
}

内存四区图如下:

数组类型定义

数组类型:由元素个数和元素类型对应,可以通过typedef定义一个数字类型

typedef int A[8];//代表数组类型,它是一个类型,不是变量
A b;//int b[8],去掉typedef,b替换到A的位置

数组指针和指针数组

指针数组:

char *point_array[4]; 
(char *)point_array[4]; 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

//argc:传参的个数(包含可执行程序)
//argv:指针数组,指向输入的参数
int main(int argc,char *argv[])

{
	/*
	指针数组与数组指针
	指针数组:它是数组,每个元素都是指针
	*/
	//指针数组,[]比*的优先级高
	char *a[] = { "aaaaa","bbbbb","ccccc" };
	int i = 0;
	printf("argc=%d\n", argc);
	for (i = 0; i < argc; i++)
	{
		printf("%s\n", argv[i]);
	}

	printf("\n");
	system("pause");
	return 0;
}

数组指针:是一个指针,指向一个数组。(一个指针)

int (*array_point)[4];

1、先定义数组类型,根据类型定义指针变量

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(void)

{
	//数组指针,它是指针,指向一个数组的指针
	//数组指针,指向一维数组的整个数组,而不是首元素地址
	//定义数组指针变量
	int i = 0;
	int a[10] = { 0 };
	//1、先定义数组类型,根据类型定义指针变量
	typedef int A[10];//A数组类型,10代表步长

	A *p = NULL;//p数组指针类型变量
	//p = a;//会有警告
	p = &a;//ok
	printf("p:%d,p+1:%d\n", p, p + 1);
	for (i = 0; i < 10; i++)
	{
		(*p)[i] = i + 1;
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d", (*p)[i]);
	}

	printf("\n");
	system("pause");
	return 0;
}

2、先定义数组指针类型,根据类型定义变量

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(void)

{
	int i = 0;
	int a[10] = { 0 };
	//1、先定义数组指针类型,根据类型定义变量
	//和指针数组写法很类似,多了()
	//()与[]优先级一样,从左往右
	//指向数组的指针,它有typedef,所以它是一个数组指针类型	
	typedef int(*P)[10];

	P q;//数组指针变量
	q = &a;
	for (i = 0; i < 10; i++)
	{
		(*q)[i] = i + 1;
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d", (*q)[i]);
	}
	printf("\n");
	system("pause");
	return 0;
}

    3、直接定义数组指针变量

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(void)

{
	int i = 0;
	int a[10] = { 0 };
	//3、直接定义数组指针变量
	int(*q)[10];//q数组指针变量
	q = &a;
	for (i = 0; i < 10; i++)
	{
		(*q)[i] = i + 1;
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d", (*q)[i]);
	}
	printf("\n");
	system("pause");
	return 0;
}

二维数组的使用

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(void)

{
	int a1[3][4] = { {1,2,3,4},{5,6,7,8},{9,10,11,12} };//不常用

	int a2[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };

	int a3[][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
	int i = 0;
	int j = 0;
	for(i = 0; i < 3; i++)
	{
		for (j = 0; j < 4; j++)
		{
			printf("%d ,", a3[i][j]);
		}
		printf("\n");
	}
	//二维数组数组名代表0行的首地址(区别于第0行首元素地址,虽然值一样)
	//它们步长不一样
	printf("a:%d, a+1:%d\n", a3, a3 + 1);

	printf("%d, %d\n", *(a3 + 0), *(a3 + 1));//第0行与第1首元素地址
	printf("%d, %d\n", *(a3 + 0),*(a3 + 0)+1);//第0行首元素地址,第0行第1列元素地址

	printf("%d, %d\n", a3[0], a3[1]);
	//a3:代表第0行首地址
	//a3+i:->&a[i],代表第i行首地址
	//*(a3+i)->a[i]:代表第0行首元素地址
	//*(a3+i)+j->&a[i][j]:第i行第j列元素地址
	//*(*(a3+i)+j)->a[i][j]=,第i行第j列元素值

	printf("\n");
	system("pause");
	return 0;
}

数组指针与二维数组结合

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(void)

{
	int a[][6] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
	//2个a[4]的一维数组

	//定义数组指针变量
	//指向一维数组的整个数组首地址
	int(*p)[6];
	//p=&a;//整个二维数组首地址p=a;
	p = a;//第0行首地址,p等价子二维数组名
	printf("p:%d,p+1:%d\n", p, p + 1);//6*4=24

	int i = 0; 
	int j = 0;
	int ni = sizeof(a) / sizeof(a[0]);
	int nj = sizeof(a[0]) / sizeof(a[0][0]);
	printf("%d,%d\n", ni, nj);
	
	for (i = 0; i < ni; i++)
	{
		for (j = 0; j < nj; j++)
		{
			printf("%d ", p[i][j]);
			//printf("%d ",*(*(p+i)+j));//可读性不好
		}
		printf("\n");
	}
	


	printf("\n");
	system("pause");
	return 0;
}

二维数组做函数形参

 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef int(*P)[4];

void printArray1(int a[3][4])//第二个[]中的参数一定要填写,决定步长
{
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 4; j++)
		{
			printf("%d ", a[i][j]);
		}
		printf("\n");
	}
}
//步长a+1,跳一行,4*4=16个字节
void printArray2(int a[][4])//第二个[]中的参数一定要填写,决定步长
{
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 4; j++)
		{
			printf("%d ", a[i][j]);
		}
		printf("\n");
	}
}
//步长a+1,跳4个字节
void printArray3(int **a)//err,指针步长不一样
{
	printf("%d,%d", a, a + 1);
	/*
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 4; j++)
		{
			printf("%d ", a[i][j]);
		}
		printf("\n");
	}
	*/
}

void printArray4(int (*a)[4])//err,指针步长不一样
{
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
	for (j = 0; j < 4; j++)
	{
	printf("%d ", a[i][j]);
	}
	printf("\n");
	}
}

void printArray5(P a)//err,指针步长不一样
{
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 4; j++)
		{
			printf("%d ", a[i][j]);
		}
		printf("\n");
	}
}
int main(void)

{
	int a[][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
	//printArray1(a);//ok
	//printArray2(a);//ok
	//printArray3(a);//err,指针步长不一样
	printArray4(a);//ok
	printArray5(a);//ok

	printf("\n");
	system("pause");
	return 0;
}

二维数组做函数形参

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef int(*P)[4];

void printArray1(int a[3][4])//第二个[]中的参数一定要填写,决定步长
{
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 4; j++)
		{
			printf("%d ", a[i][j]);
		}
		printf("\n");
	}
}
//步长a+1,跳一行,4*4=16个字节
void printArray2(int a[][4])//第二个[]中的参数一定要填写,决定步长
{
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 4; j++)
		{
			printf("%d ", a[i][j]);
		}
		printf("\n");
	}
}
//步长a+1,跳4个字节
void printArray3(int **a)//err,指针步长不一样
{
	printf("%d,%d", a, a + 1);
	/*
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 4; j++)
		{
			printf("%d ", a[i][j]);
		}
		printf("\n");
	}
	*/
}

void printArray4(int (*a)[4])
{
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
	for (j = 0; j < 4; j++)
	{
	printf("%d ", a[i][j]);
	}
	printf("\n");
	}
}

void printArray5(P a)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 4; j++)
		{
			printf("%d ", a[i][j]);
		}
		printf("\n");
	}
}
int main(void)

{
	int a[][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
	//printArray1(a);//ok
	//printArray2(a);//ok
	//printArray3(a);//err,指针步长不一样
	printArray4(a);//ok
	printArray5(a);//ok

	printf("\n");
	system("pause");
	return 0;
}

结构体相互赋值

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


//定义结构体类型时不要直接给成员赋值
//结构体只是一个类型,还没有分配空间
//只有根据其类型定义变量时,才分配空间,有空间后才能赋值
typedef struct Teacher
{
	char name[50];
	int age;
}Teacher;

void copyTeacher(Teacher *to, Teacher *from)
{
	*to = *from;

}
int main(void)

{
	Teacher t1 = { "lily",22 };
	//相同类型的两个结构体变量,可以相互赋值
	//把t1成员变量内存的值拷贝给t2成员变量的内存
	//t1与t2没有关系
	Teacher t2 = t1;
	printf("%s ,%d\n", t2.name, t2.age);
	
	Teacher t3;
	memset(&t3, 0, sizeof(t3));
	copyTeacher(&t3, &t1);
	printf("%s ,%d\n", t3.name, t3.age);
	printf("\n");
	system("pause");
	return 0;
}

结构体动态数组

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct Teacher
{
	char name[50];
	int age;
}Teacher;

int main(void)

{
	Teacher a[3] = {
		{"a",18},
		{"b",19},
		{"c",20}
	};

	//静态
	Teacher a2[3] = { "a",18,"b",19,"c",20 };
	int i = 0;
	for (i = 0; i++; i < 3)
	{
		printf("%s ,%d\n", a2[i].name, a2[i].age);
	}

	//动态
	Teacher *p = (Teacher *)malloc(3 * sizeof(Teacher));

	char buf[50];
	for (i = 0; i < 3;i++ )
	{
		sprintf(buf, "name%d%d%d",i,i,i);
		strcpy(p[i].name, buf);
		p[i].age = 20 + i;
	}

	for (i = 0; i < 3; i++)
	{
		printf("%s ,%d\n", p[i].name, p[i].age);
	}
	if(p != NULL)
	{
		free(p);
		p = NULL;
	}

	printf("\n");
	system("pause");
	return 0;
}

结构体嵌套一级指针

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct Teacher
{
	char *name;
	int age;
}Teacher;

int main(void)

{
	/*
	char *name = NULL;
	name = (char *)malloc(30);
	strcpy(name,"lily");
	printf("name = %s\n", name);

	if (name != NULL)
	{
		free(name);
		name = NULL;
	}
	*/
	//1
	Teacher t;
	t.name = (char *)malloc(30);
	strcpy(t.name, "lily");
	t.age = 20;
	printf("name = %s ,age = %d\n", t.name, t.age);
	if (t.name != NULL)
	{
		free(t.name);
		t.name = NULL;
	}

	//2
	Teacher *p = NULL;
	p = (Teacher *)malloc(sizeof(Teacher));
	p->name = (char *)malloc(30);
	strcpy(p->name, "lihua");
	p->age = 22;
	printf("name = %s ,age = %d\n", p->name, p->age);
	if (p->name != NULL)
	{
		free(p->name);
		p->name = NULL;
	}
	if (p != NULL)
	{
		free(p);
		p = NULL;
	}
	//3
	Teacher *q = NULL;
	q= (Teacher *)malloc(sizeof(Teacher) * 3);
	//Teacher q[3]
	int i = 0;
	char buf[30];
	for(i = 0; i < 3; i++)
	{
		q[i].name = (char *)malloc(30);
		sprintf(buf, "name%d%d", i, i);
		strcpy(q[i].name, buf);
		q[i].age = 20 + i;
	}
	for (i = 0; i < 3; i++)
	{
		printf("name = %s ,age = %d\n", q[i].name, q[i].age);
	}
	for (i = 0; i < 3; i++)
	{
		if (q[i].name != NULL)
		{
			free(q[i].name);
			q[i].name = NULL;
		}
	}
	if (q != NULL)
	{
		free(q);
		q = NULL;
	}


	printf("\n");
	system("pause");
	return 0;
}

代码封装

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct Teacher
{
	char *name;
	int age;
}Teacher;

void showTeacher(Teacher *p,int n)
{
	int i = 0;
	for (i = 0; i < n; i++)
	{
		printf("name = %s ,age = %d\n", p[i].name, p[i].age);
	}
}

void freeTeacher(Teacher *p, int n)
{
	int i = 0;
	for (i = 0; i < n; i++)
	{
		if (p[i].name != NULL)
		{
			free(p[i].name);
			p[i].name = NULL;
		}
	}
	if (p != NULL)
	{
		free(p);
		p = NULL;
	}
}

Teacher *getMem(int n)
{
	Teacher *p = (Teacher *)malloc(sizeof(Teacher) * 3);
	//Teacher p[3]
	int i = 0;
	char buf[30];
	for (i = 0; i < n; i++)
	{
		p[i].name = (char *)malloc(30);
		sprintf(buf, "name%d%d", i, i);
		strcpy(p[i].name, buf);
		p[i].age = 20 + i;
	}
	return p;
}

int getMem2(Teacher **tmp, int n)
{
	if (tmp == NULL)
	{
		return - 1;
	}
	Teacher *p = (Teacher *)malloc(sizeof(Teacher) * 3);
	//Teacher p[3]
	int i = 0;
	char buf[30];
	for (i = 0; i < n; i++)
	{
		p[i].name = (char *)malloc(30);
		sprintf(buf, "name%d%d", i, i);
		strcpy(p[i].name, buf);
		p[i].age = 20 + i;
	}

	*tmp = p;
	return 0;
}

int main(void)

{
	/*
	char *name = NULL;
	name = (char *)malloc(30);
	strcpy(name,"lily");
	printf("name = %s\n", name);

	if (name != NULL)
	{
	free(name);
	name = NULL;
	}
	*/
	//1
	Teacher t;
	t.name = (char *)malloc(30);
	strcpy(t.name, "lily");
	t.age = 20;
	printf("name = %s ,age = %d\n", t.name, t.age);
	if (t.name != NULL)
	{
		free(t.name);
		t.name = NULL;
	}

	//2
	Teacher *p = NULL;
	p = (Teacher *)malloc(sizeof(Teacher));
	p->name = (char *)malloc(30);
	strcpy(p->name, "lihua");
	p->age = 22;
	printf("name = %s ,age = %d\n", p->name, p->age);
	if (p->name != NULL)
	{
		free(p->name);
		p->name = NULL;
	}
	if (p != NULL)
	{
		free(p);
		p = NULL;
	}
	//3
	Teacher *q = NULL;
	//q = getMem(3);//实现1

        //实现2
	int ret = 0;
	ret = getMem2(&q,3);
	if (ret != 0)
	{
		return ret;
	}
	showTeacher(q,3);

	freeTeacher(q, 3);

	q = NULL;
	
	printf("\n");
	system("pause");
	return 0;
}

结构体套二级指针

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct Teacher
{
	char **stu;//二维内存

}Teacher;

int main(void)

{
	char **name = NULL;
	//char *name[3];
	int n = 3;
	int i = 0;
	int j = 0;
	name = (char **)malloc(n * sizeof(char *));
	//char buf[30]
	for (i = 0; i < n; i++)
	{
		name[i] = (char *)malloc(30);
		strcpy(name[i], "lily");
	}
	for (i = 0; i < n; i++)
	{
		printf("%s\n", name[i]);
	}
	for (i = 0; i < n; i++)
	{
		if (name[i] != NULL)
		{
			free(name[i]);
			name[i] = NULL;
		}
	}
	if (name != NULL)
	{
		free(name);
		name = NULL;
	}

	//1
	Teacher t;
	//t.stu[3]

	//char *t.stu[3];

	t.stu = (char **)malloc(n * sizeof(char *));
	//char buf[30]
	for (i = 0; i < n; i++)
	{
		t.stu[i] = (char *)malloc(30);
		strcpy(t.stu[i], "lihe");
	}
	for (i = 0; i < n; i++)
	{
		printf("%s\n", t.stu[i]);
	}
	for (i = 0; i < n; i++)
	{
		if (t.stu[i] != NULL)
		{
			free(t.stu[i]);
			t.stu[i] = NULL;
		}
	}
	if (t.stu != NULL)
	{
		free(t.stu);
		t.stu = NULL;
	}

	//2
	Teacher *p = NULL;
	//p->stu[3]
	p = (Teacher *)malloc(sizeof(Teacher));

	//char p->stu[3];
	p->stu = (char **)malloc(n * sizeof(char *));
	//char buf[30]
	for (i = 0; i < n; i++)
	{
		p->stu[i] = (char *)malloc(30);
		strcpy(p->stu[i], "lihua");
	}
	for (i = 0; i < n; i++)
	{
		printf("%s\n", p->stu[i]);
	}
	for (i = 0; i < n; i++)
	{
		if (p->stu[i] != NULL)
		{
			free(p->stu[i]);
			p->stu[i] = NULL;
		}
	}
	if (p->stu != NULL)
	{
		free(p->stu);
		p->stu = NULL;
	}
	if (p != NULL)
	{
		free(p);
		p = NULL;
	}

	//3
	Teacher *q = NULL;
	//Teacher q[3]
	//q[i].stu[3]
	q = (Teacher *)malloc(sizeof(Teacher) * 3);
	for (i = 0; i < n; i++)
	{
		q[i].stu= (char **)malloc(n * sizeof(char *));//char *stu[3]
		for (j = 0; j < 3; j++)
		{
			q[i].stu[j] = (char *)malloc(30);//char buf[30]
			char buf[30];
			sprintf(buf, "name%d%d%d%d", i, i, j, j);
			strcpy(q[i].stu[j], buf);
		}
	}
	for (i = 0; i < 3; i++)
	{
		printf("%s, %s, %s\n", q[i].stu[0], q[i].stu[1], q[i].stu[2]);
	}

	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 3; j++)
		{
			if (q[i].stu[j] != NULL)
			{
				free(q[i].stu[j]);
				q[i].stu[j] = NULL;
			}
		}
		if (q[i].stu != NULL)
		{
			free(q[i].stu);
			q[i].stu = NULL;
		}
	}
	if (q != NULL)
	{
		free(q);
		q = NULL;
	}
	printf("\n");
	system("pause");
	return 0;
}

封装函数后代码如下

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct Teacher
{
	char **stu;//二维内存

}Teacher;

int creatTeacher(Teacher **tmp,int n1,int n2)//n1老师个数,n2老师带学生个数
{
	int i = 0;
	int j = 0;
	if (tmp == NULL)
	{
		return - 1;
	}
	Teacher *q = (Teacher *)malloc(sizeof(Teacher) * n1);
	for (i = 0; i < n1; i++)
	{
		q[i].stu = (char **)malloc(n2 * sizeof(char *));//char *stu[3]
		for (j = 0; j < n2; j++)
		{
			q[i].stu[j] = (char *)malloc(30);//char buf[30]
			char buf[30];
			sprintf(buf, "name%d%d%d%d", i, i, j, j);
			strcpy(q[i].stu[j], buf);
		}
	}
	//间接赋值是指针的最大意义
	*tmp = q;
	return 0;
}

void showTeacher(Teacher *q, int n1, int n2)
{
	int i = 0;
	int j = 0;
	if (q == NULL)
	{
		return;
	}
	for (i = 0; i < n1; i++)
	{
		for (j = 0; j < n2; j++)
		{
			printf("%s,", q[i].stu[j]);
		}
		printf("\n");
	}
}
void freeTeacher(Teacher **tmp, int n1, int n2)
{
	if (tmp == NULL)
	{
		return;
	}
	Teacher *q = *tmp;
	int i = 0;
	int j = 0;
	for (i = 0; i < n1; i++)
	{
		for (j = 0; j < n2; j++)
		{
			if (q[i].stu[j] != NULL)
			{
				free(q[i].stu[j]);
				q[i].stu[j] = NULL;
			}
		}
		if (q[i].stu != NULL)
		{
			free(q[i].stu);
			q[i].stu = NULL;
		}
	}
	if (q != NULL)
	{
		free(q);
		q = NULL;
		*tmp = NULL;
	}
}

int main(void)

{
	Teacher *q = NULL;
	int ret = 0;
	ret = creatTeacher(&q, 3, 3);
	if (ret != 0)
	{
		return ret;
	}
	//Teacher q[3]
	//q[i].stu[3]
	showTeacher(q, 3, 3);
	
	freeTeacher(&q, 3, 3);
	
	printf("\n");
	system("pause");
	return 0;
}

结构体深拷贝与浅拷贝

结构体中嵌套指针,而且动态分配空间,同类型结构体变量赋值,会导致浅拷贝。结构体成员指针变量指向同一块内存

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct Teacher
{
	char *name;
	int age;
}Teacher;
int main(void)

{
	Teacher t1;
	t1.name = (char *)malloc(30);
	strcpy(t1.name, "lily");
	t1.age = 23;

	Teacher t2;
	t2 = t1;//浅拷贝,t1.name与t2.name指向同一块内存区域
	printf("[t2]%s, %d\n", t2.name, t2.age);

	/*
	if (t2.name != NULL)//再释放一次程序就会崩
	{
		free(t1.name);
		t1.name = NULL;
	}
	*/
	//深拷贝,重新拷贝一下
	Teacher t3;
	t3 = t1;
	t3.name = (char *)malloc(30);
	strcpy(t3.name, t1.name);
	//t3.age = t1.age;
	printf("[t3]%s, %d\n", t3.name, t3.age);
	if (t1.name != NULL)//释放一次即可
	{
		free(t1.name);
		t1.name = NULL;
	}

	if (t3.name != NULL)//
	{
		free(t1.name);
		t3.name = NULL;
	}
	printf("\n");
	system("pause");
	return 0;
}

结构体对齐规则

结构体中所有成员的对齐参数N的最大值称为结构体的对齐参数。

如果结构体中嵌套结构体,如结构体A中嵌套结构体B,则先看A中基本成员的的参数最大值,再看B中基本成员参数最大值,以最大的为对齐参数。

struct
{
    int a;
    short b;
}A;//占8个字节

//对齐单位为4字节
/*
偏移量:
a:4*0=0
b:2*2=4

struct
{
    double a;
    int b;
    short c;
}B;//占16 个字节

//对齐单位为8字节
/*
偏移量
a:8*0=0
b:4*2=8
c:2*6=12

struct
{
    int a;
    double b;
    short c;
}B;//占24 个字节

//对齐单位为8字节
/*
偏移量
a:4*0=0
b:8*1=8
c:2*8=16

 

//嵌套结构体
struct A
{
    int a;
    double b;
    float c;
};//占24 个字

struct 
{
    char e[2];
    int f;
    double g;
    short h;
    struct A i;
}B;//占48字节

//对齐单位为8
/*
偏移量
e:2*0=0;
f:4*1=4;
g:8*1=8;
h:2*8=16;
//注意结构体的起点位置,就算h为占满8字节,由于新的结构体成员,所以直接*填满
结构体i起点坐标:
8*3=24
a:24+4*0=24;
b:24+8*1=28;
c:24+4*4=40;

#pragma pack(2)//指定对其单位为2
//若为#pragma pack(8),超过了结构体中最长的成员,则以最长成员对齐即为4
typedef struct
{
    int a;
    char b;
    short c;
    char d;
}A;//占10字节

/*
偏移量
a:2*0=0;
b:1*4=4;
c:2*3=6;
d:1*4=8;

结构体嵌套结构体

1、结构体可以嵌套另外一个结构体的任何类型变量

2、结构体嵌套本结构体变量(不可以),因为本结构体类型大小无法确定;类型本质:固定大小内存别名

3、结构体嵌套本结构体指针变量(可以),指针变量的内存空间可以确定。

typedef struct A
{
    int a;
    int b;
    char *p
}A;

typedef struct B
{
    int c;
    A tmp1;//ok
    A *p1;//ok

    struct B tmp2;//err
    struct B*next;//ok
}B;

链表:每一个节点即一个结构体

数组与链表对比:

数组:一次性分配一块连续地存储区域

优点:随机访问元素效率高

缺点:需要分配一块连续地存储区域(很大区域,有可能分配失败);删除和插入某个元素效率低

链表:

优点:不需要一块连续地存储区域;删除和插入某个元素效率高

缺点:随机访问元素效率低

概念:

节点:数据域,指针域

结尾点:指针域指向NULL

静态链表:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct Stu
{
	int id;//数据域
	char name[100];

	struct Stu *next;//指针域
}Stu;
int main(void)

{
	//初始化三个结构体变量
	Stu s1 = { 1,"tom",NULL };
	Stu s2 = { 2,"tina",NULL };
	Stu s3 = { 3,"lisa",NULL };

	//形成链表
	s1.next = &s2;
	s2.next = &s3;
	s3.next = NULL;//尾结点

	Stu *p = &s1;
	while (p != NULL)
	{
		printf("id = %d,name = %s\n", p->id, p->name);

		//节点往后移动一位
		p = p->next;
	}
	printf("\n");
	system("pause");
	return 0;
}

链表操作:头结点的创建和链表的遍历,节点的插入,清空链表

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct Node
{
	int id;
	struct Node *next;
}Node;
//创建头节点
Node *SListCreat()
{
	Node *head = NULL;
	//头节点作为标志,不存属有效数据
	head = (Node *)malloc(sizeof(Node));
	if(head==NULL)
	{
		return NULL;
	}
	//给head的成员赋值
	head->id = -1;
	head->next = NULL;

	Node *pCur = head;
	Node *pNew = NULL;

	int data;

	while (1)
	{
		printf("请输入数据:");
		scanf("%d", &data);

		if (data == -1)
		{
			break;
		}
		//新节点动态分配空间
		pNew = (Node *)malloc(sizeof(Node));
		if (pNew == NULL)
		{
			continue;
		}
		pNew->id = data;
		pNew->next = NULL;

		//链表建立关系
		pCur->next = pNew;
		//pNew下一个节点指向NULL
		pNew->next = NULL;
		//把pCur移动到pNew
		pCur = pNew;

	}

	return head;
}

//链表的遍历
int SListPrint(Node *head)
{
	if (head == NULL)
	{
		return - 1;
	}
	//取出第一个有效节点(头节点的next)
	Node *pCur = head->next;
	printf("head-> ");
	while (pCur != NULL)
	{
		printf("%d->", pCur->id);
		pCur = pCur->next;
	}
	printf("NULL\n");
	return 0;
}

//插入节点,在值为x的节点前插入值为y的节点;若值为x的节点不存在,则插在表尾
int SListNodeInsret(Node *head,int x,int y)
{
	if (head == NULL)
	{
		return -1;
	}
	Node *pPre = head;
	Node *pCur = head->next;
	while (pCur != NULL)
	{
		if (pCur->id == x)//找到匹配节点
		{
			break;
		}
		pPre = pCur;
		pCur = pCur->next;
	}
	//2种情况
	//1.找到匹配的节点,pCur为匹配节点
	//2.没有找到匹配节点,pCur为空节点,pPre为最后一个节点

	//给新的节点动态分配空间
	Node *pNew = (Node *)malloc(sizeof(Node));
	if (pNew == NULL)
	{
		return -2;
	}
	//给pNew的成员变量赋值
	pNew->id = y;
	pNew->next = NULL;
	pPre->next = pNew;
	pNew->next = pCur;

	return 0;
}
//删除第一个值为x的节点
int SListNodeDel(Node *head, int x)
{
	if (head == NULL)
	{
		return -1;
	}
	Node *pPre = head;
	Node *pCur = head->next;
	int flag = 0;//0代表没有找到
	while (pCur != NULL)
	{
		if (pCur->id == x)//找到匹配节点
		{
			pPre->next = pCur->next;
			free(pCur);
			pCur = NULL;
			flag = 1;
			break;
		}
		pPre = pCur;
		pCur = pCur->next;

	}
	if (0 == flag)
	{
		printf("没有值为%d的节点\n", x);
		return -2;
	}
	return 0;
}

//清空链表,释放所有节点
int SListNodeDestroy(Node *head)
{
	if (head == NULL)
	{
		return -1;
	}
	Node *tmp = NULL;
	int i = 0;
	while (head != NULL)
	{
		tmp = head->next;
		free(head);
		head = NULL;

		head = tmp;
		i++;
	}
	printf("释放次数:%d的\n", i);
	return 0;
}
int main(void)

{
	Node *head = NULL;
	head = SListCreat();//创建头节点
	SListPrint(head);

	//在5的前面插入4
	SListNodeInsret(head, 5, 4);
	SListPrint(head);

	SListNodeDel(head, 5);
	SListPrint(head);

	SListNodeDestroy(head);
	head = NULL;
	printf("\n");
	system("pause");
	return 0;
}

函数指针

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

//指针函数,()的优先级比*高,它是函数,返回值是指针类型
//返回指针类型的函数
int *fun2()
{
	int *p = (int *)malloc(sizeof(int));
	return p;
}

int fun(int a)
{
	printf("a=%d\n", a);
	return 0;
}
int main(void)

{
	//函数指针,它是指针,指向函数的指针
	//定义函数指针有3种形式
	//1、先定义函数类型,根据函数类型定义指针变量(不常用)
	//有typedef是类型,没有是变量
	typedef int FUN(int a);//FUN函数类型
	FUN *p1 = NULL;
	p1 = fun;//p1指向fun函数
	fun(5);//传统调用
	p1(6);//指针变量调用方式

	//2、先定义函数指针类型,根据类型定义指针变量(常用)
	typedef int (*PFUN)(int a);//PFUN,函数指针类类型
	PFUN p2 = fun;//p2指向fun函数
	p2(7);

	//3、直接定义函数指针(常用)
	int(*p3)(int a) = fun;
	p3(8);
	printf("\n");
	system("pause");
	return 0;
}

函数指针应用:函数指针数组

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void add()
{
	printf("addfunction\n");
}
void minus()
{
	printf("minusfunction\n");
}

void multi()
{
	printf("multifunction\n");
}

void divide()
{
	printf("dividefunction\n");
}

void myexit()
{
	exit(0);
}
int main(void)

{
#if 0
	char cmd[100];
	while (1)
	{
		printf("请输入指令: ");
		scanf("%s", cmd);
		if (strcmp(cmd, "add") == 0)
		{
			add();
		}
		else if (strcmp(cmd, "min") == 0)
		{
			minus();
		}
		else if (strcmp(cmd, "mul") == 0)
		{
			multi();
		}
		else if (strcmp(cmd, "div") == 0)
		{
			divide();
		}
		else if (strcmp(cmd, "exit") == 0)
		{
			myexit();
		}
	}
#endif
	void(*fun1)() = add;
	fun1();

	//函数指针数组
	void(*fun[5])() = { add,minus,multi,divide,myexit };
	//指针数组
	char *buf[] = { "add","min","mul","div","exit" };

	int i = 0;
	char cmd[100];
	while (1)
	{
		printf("请输入指令: ");
		scanf("%s", cmd);
		for (i = 0; i < 5; i++)
		{
			if (strcmp(cmd, buf[i]) == 0)
			{
				fun[i]();
				break;//跳出for循环
			}
		}
	}

	printf("\n");
	system("pause");
	return 0;
}

函数指针应用:回调函数(函数指针做函数参数)

你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。回答完毕。

当函数指针 做为函数的参数,传递给一个被调用函数,被调用函数就可以通过这个指针调用外部的函数,这就形成了回调。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void add(int a,int b)
{
	return (a + b);
}
void minus(int a, int b)
{
	return (a - b);
}

void multi(int a, int b)
{
	return (a * b);
}

void divide(int a, int b)
{
	return (a / b);
}

void myexit()
{
	exit(0);
}
//框架,固定不变
//多态,调用同一接口,不一样的结果
void fun(int x, int y,int (*p)(int a,int b))
{
	printf("fun\n");
	int a = p(x, y);//回调函数
	printf("a = %d\n", a);
}

//方法2
typedef int(*Q)(int a, int b);
void fun2(int x, int y, Q p)
{
	printf("fun\n");
	int a = p(x, y);//回调函数
	printf("a = %d\n", a);
}
int main(void)

{
	fun(1, 2, add);

	fun(10, 5, minus);

	fun(10, 5, divide);
	printf("\n");
	system("pause");
	return 0;
}

例子:

#include <iostream>
using namespace std;

typedef void(*PF)();
PF g_pf = NULL;

/******************B模块****************/
// B模块向外提供的回调函数注册接口,也就是说,提供注册服务,想注册的,调用该接口即可

void registerFun(PF pf)

{
	g_pf = pf;
}

// B模块的某函数,在一定条件下被触发

void fun()

{
	// 执行别的模块注册过来的回调函数

	(*g_pf)(); // or g_pf();
}

/*****************A模块*****************/
// A模块的回调函数
void callBack()

{

	cout << "call back function" << endl;

}


// A 模块的主函数

int main()

{

	// A模块利用B模块的注册接口, 向B模块注册

	registerFun(callBack);

	// 在一定条件下,B模块的fun函数被触发了

	fun();
	return 0;

}
#define  _CRT_SECURE_NO_WARNINGS 
#include <iostream>

using namespace std;

// 定义一个锦囊的方法 是一个函数指针,类似于c++的纯虚函数
typedef void(TIPS_FUNC)(void);


//写一个锦囊的结构体
struct tips
{
	char from[64]; //这个锦囊是谁写的
	char to[64]; //写给谁的
	TIPS_FUNC *fp; //具体锦囊的内容
};


//写一个供赵云调用的架构函数
void open_tips(struct tips * tp)
{
	cout << "打开了锦囊" << endl;
	cout << "此锦囊是由" << tp->from << ", 写给" << tp->to << endl;
	cout << "内容是" << endl;
	tp->fp();//拆开锦囊,调用具体锦囊的方法
}

//实现一个一个的锦囊  //类似于实现一个纯虚函数
void tips_1(void)
{
	cout << "一到东吴就大张旗鼓找乔国老" << endl;
}

void tips_2(void)
{
	cout << "骗刘备 操作压境" << endl;
}

void tips_3(void)
{
	cout << "找孙尚香求救" << endl;
}

void tips_4(void)
{
	cout << "你们就死在东吴把" << endl;
}

struct tips* create_tips(char *from, char *to, TIPS_FUNC *fp)
{
	struct tips *tp = (struct tips*)malloc(sizeof(struct tips));
	if (tp == NULL) {
		return NULL;
	}
	strcpy(tp->from, from);
	strcpy(tp->to, to);
	//注册回调函数
	tp->fp = fp; //给拥有函数指针的结构体  函数指针变量 赋值 就是 注册回调函数

	return tp;
}

void destory_tips(struct tips * tp)
{
	if (tp != NULL) {
		free(tp);
	}
}

int main(void)
{
	//诸葛亮去写锦囊
	struct tips * tp1 = create_tips("孔明", "赵云", tips_1);
	struct tips * tp2 = create_tips("孔明", "赵云", tips_2);
	struct tips * tp3 = create_tips("孔明", "赵云", tips_3);
	struct tips * tp4 = create_tips("庞统", "赵云", tips_4);


	// 赵云去拆机囊
	cout << "刚来到 东吴境内 ,打开了第一个锦囊" << endl;
	open_tips(tp1);

	cout << "刘备乐不思蜀 ,打开第二个锦囊 " << endl;

	open_tips(tp2);

	cout << "孙权追杀刘备, 打开第三个锦囊" << endl;
	open_tips(tp3);
	cout << "赵云发现  抵挡不住 军队,想到了庞统的最后一个锦囊 打开了" << endl;

	open_tips(tp4);


	destory_tips(tp1);
	destory_tips(tp2);
	destory_tips(tp3);
	destory_tips(tp4);

	return 0;
}

回调函数的优点:

①函数的调用和函数的实现 有效地分离

②类似C++中的多态,可扩展

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言提高 的相关文章

  • OpenCV中Mat总结

    一 数字图像存储概述 数字图像存储时 xff0c 我们存储的是图像每个像素点的数值 xff0c 对应的是一个数字矩阵 二 Mat的存储 1 OpenCV1基于C接口定义的图像存储格式IplImage xff0c 直接暴露内存 xff0c 如
  • opencv----(1) mat最好用,和IplImage,cvmat 比较

    学习了几天 xff0c 发现mat比IplImage cvmat 好用太多了 不知道确切的原文出处 xff0c 我是转自新浪的一篇博文 xff1a http blog sina com cn s blog 534497fd01015k7z
  • OpenCV学习笔记之 ( 三 ) MFC显示Mat图片

    以下步骤参考以下链接 http blog csdn net dcrmg article details 51913160 原理及详解见上链接 下面只讲步骤 一 打开VS2010 xff0c 建立对话框的项目 二 建立 PictureCont
  • 彻底解决显示Opencv中Mat图像到Mfc窗口问题

    第一步 xff0c 转换格式前预先获得待显示控件的大小 xff0c 若相等则不做处理 xff0c 若不等则首先改变Mat图像大小 xff0c 再进行转换 CRect rect GetDlgItem IDC STATIC SRC gt Get
  • 在MFC中显示cv::Mat

    BOOL DrawMat CDC pDC cv Mat amp img CRect amp rect if img empty img depth 61 CV 8U img channels 61 3 return FALSE BITMAP
  • 在MFC图像控件上显示opencv Mat格式图片

    显示opencv图片格式Mat到图像控件vIDC上 vMat xff1a 图像Mat vIDC xff1a 图像控件ID void CDLFaceDlg showMat Mat vMat int vIDC if vMat empty ret
  • 在OpenCV中将cv::Mat绘制到MFC的视图中

    毕设时遇到了一个问题 xff0c 不能在MFC中显示由GrabCut抠出来的图 经一番折腾发现 xff1a 在OpenCV中如果图像最初是Mat类型的话 xff0c 就不能通过转换为IplImage xff0c 再转换为CvvImage来显
  • 【AutoLisp】AutoLisp中的DCL界面应用基础

    目录 01 DCL的作用 01 01 DCL介绍 01 02 DCL对话框成员 01 标准DCL对象家族 02 装饰DCL对象家族 03 集群DCL对象家族 04 整合DCL对象家族 02 DCL的加载 03 DCL的规则 04 DCL对话
  • 在MFC的picture控件中如何显示Mat图

    首先 xff0c 要进行输入检查 xff0c 看Mat数据是否是有效的 xff0c 如下图中所示 定义位图数据结构 xff0c 用以方便在图形设备接口 GraphicsDeviceInterface 上显示 xff0c 也就是windows
  • MFC+OPENCV+显示MAT类型图像

    MFC显示图像到界面 xff0c 可以用链接中的DrawMatToPic xff0c 有时会出现IplImage 类型转换问题 xff0c 因为用opencv做后续图像处理 xff0c 所以统一使用Mat类型 xff0c 可以showMat
  • CvMat、Mat、IplImage之间的转换详解及实例

    IplImage xff1a 在OpenCV中IplImage是表示一个图像的结构体 xff0c 也是从OpenCV1 0到目前最为重要的一个结构 xff1b 在之前的图像表示用IplImage xff0c 而且之前的OpenCV是用C语言
  • RGB和RGBQUAD的区别

    RGB和RGBQUAD的区别 昨天的晚上 为一个问题困扰了很长时间 几乎整个晚上都在弄一个小bug 感觉没有问题 但就是效果不理想 几次三番 查来查去 我想实现的功能是 生成一张图 图上有字 这张图是以一张指定的位图为背景的 我使用 COL
  • BITMAPINFO结构

    BITMAPINFO结构 BITMAPINFO结构具有如下形式 xff1a typedef struct tagBITMAPINFO BITMAPINFOHEADER bmiHeader RGBQUAD bmiColors 1 BITMAP
  • OpenCV中的cv::String和CString互相转换

    请注意是cv String xff0c 而不是std string xff0c 第一个字母是大写的 基本上CString转cv String网上都能查到 xff0c 而cv String转CString没有人提到 1 CString gt
  • MFC——文件打开和保存对话框(CFileDialog)

    最近要做一个文件打开和保存的对话框 xff0c 现将相关的代码记录如下 xff0c 用以备忘 xff01 所用控件 xff1a 2个静态标签 Static Text xff1a 用以显示功能标签 xff1b 2个文本框 Edit xff1a
  • OpenCv2 学习笔记(1) Mat创建、复制、释放

    OpenCV和VS2013的安装图文教程网上有很多 xff0c 建议安装好之后 xff0c 用VS2013建立一个空工程 xff0c 用属性管理器分别新建一个对应debug和release工程的props配置文件 xff0c 以后直接根据工
  • MFC C++ Cstring与string互转

    CString 转换成string 我试了很多的方法 xff0c 都不行 xff0c 我用的vs2010 解决方案 unicode CString sz1 61 L 34 abc 34 std string sz2 61 CT2A sz1
  • MFC+opencv 显示mat图像

    VS2015 43 opencv3 0 MFC显示图片中方法三在使用时 xff0c 只能显示彩色图像 xff0c 灰度图像显示有问题 xff0c 经查找 xff0c 是没有设置图像调色板的原因图片控件宽度不为4的倍数 显示错误 xff0c

随机推荐

  • 怎么去掉SVN前面的标签,如感叹号!

    1 问题陈述 xff1a 有时不小心将整个目录都检入 xff0c 导致整个页面的文件与目录都有svn的标签 xff0c 感叹号什么的 2 解决方法 xff1a 打开 所有程序 xff0c 找到TortoiseSVN gt Setting 修
  • 人工智能6.1 -- 机器学习算法篇(一)数据清洗、回归(含实践)

    人工智能 python xff0c 大数据 xff0c 机器学习 xff0c 深度学习 xff0c 计算机视觉 六 机器学习算法篇 xff08 一 xff09 数据清洗 回归 xff08 含实践 xff09 前言 目录算法热身结论 xff1
  • Tesseract-ocr 3.0.2源码 + VS2010项目工程 + 简单测试代码

    编译环境 Visual Studio 2010 所用类库版本 zlib 1 2 7 lpng1514 jpegsr9 tiff 4 0 3 giflib 5 0 4 leptonica 1 69 tesseract ocr3 0 2 下载地
  • Asprise OCR SDK 15.3试用版破解

    1 序言 之前因同事需要 xff0c 破解过Asprise OCR 4 0试用版本 xff0c 对这个库比较有印象 目前最新版本为15 3 xff0c 网上已经能下载到它的试用破解版本 xff0c 但似乎没有看到此版本的破解文章 Aspri
  • 内存中绘图 Memdc

    内存中绘图 Memdc CDC MemDC 首先定义一个显示设备对象 xff0c 所有的绘制首先绘制到这块内存中 CBitmap MemBitmap 定义一个位图对象 随后建立与屏幕显示兼容的内存显示设备 MemDC CreateCompa
  • MFC中char*,string和CString之间的转换

    string是使用STL时必不可少的类型 xff0c 所以是做工程时必须熟练掌握的 xff1b char 是从学习C语言开始就已经和我们形影不离的了 xff0c 有许多API都是以char 作为参数输入的 所以熟练掌握三者之间的转换十分必要
  • C++ 创建文件夹的四种方式

    在开头不得不吐槽一下 xff0c 我要的是简单明了的创建文件夹的方式 xff0c 看得那些文章给的都是复杂吧唧的一大坨代码 xff0c 不仔细看鬼知道写的是啥 因此 xff0c 为了方便以后自己阅读 xff0c 这里自己写一下 C 43 4
  • c++ 多线程:线程句柄可以提前关闭,但是线程并没有关闭

    很多程序在创建线程都这样写的 xff1a ThreadHandle 61 CreateThread NULL 0 CloseHandel ThreadHandle 1 xff0c 线程和线程句柄 xff08 Handle xff09 不是一
  • MFC的GDI绘制坐标问题

    MoveWindow和CDC的位置不一样 xff0c MoveWindow 起点坐标 xff0c 宽 xff0c 高 xff0c CDC xff1a 起点坐标 xff0c 终点坐标
  • C#线程同步(1)- 临界区&Lock

    文章原始出处 http xxinside blogbus com logs 46441956 html 预备知识 xff1a 线程的相关概念和知识 xff0c 有多线程编码的初步经验 一个机会 xff0c 索性把线程同步的问题在C 里面的东
  • 线程锁的概念函数EnterCriticalSection和LeaveCriticalSection的用法

    线程锁的概念函数EnterCriticalSection和LeaveCriticalSection的用法 注 xff1a 使用结构CRITICAL SECTION 需加入头文件 include afxmt h 定义一个全局的锁 CRITIC
  • C# 获取鼠标相对当前窗口坐标的方法

    编写客户端应用程序时 xff0c 经常要用到鼠标当前的位置 在C 的winform开发中 xff0c 可以用Control MousePosition获得当前鼠标的坐标 xff0c 使用PointToClient计算鼠标相对于某个控件的坐标
  • 【Linux】Rocky Linux 9.0 Podman服务无法正常启动

    Rocky Linux 9 0发布后 xff0c 我在本地虚拟机对该版本进行了安装和测试 xff0c 发现Podman服务在某些情况下 xff0c 无法正常启动 当 etc selinux config配置中 xff0c SELINUX 6
  • 如何在C#控件中画点并获得指定点的像素颜色

    画点的方法 方法一 用picGraphics FillRectangle new SolidBrush fillColor p X p Y 1 1 即用一个像素填充方法 方法二 用gdi32 dll库中的SetPixel方法 DllImpo
  • LinearLayout(线性布局)

    本节引言 本节开始讲Android中的布局 xff0c Android中有六大布局 分别是 LinearLayout 线性布局 xff0c RelativeLayout 相对布局 xff0c TableLayout 表格布局 FrameLa
  • 使用DockerFile创建ROS环境(带有xfce和vnc可以访问桌面查看ROS的图形工具)

    基于 consol ubuntu xfce vnc的DockerFile FROM consol ubuntu xfce vnc 切换到root xff0c root才有权限进行安装软件等操作 USER 0 替换桌面背景 xff08 Doc
  • ROS自定义消息类型与使用

    1 创建消息文件 在功能包中创建msg文件夹并在文件夹中创建消息文件exmsage msg Header header int32 demo int float64 demo double 2 修改package xml lt build
  • ROS中的订阅模式、服务模式、action模式

    在ROS的通信方式中存在订阅 发布模式 xff0c 服务模式 xff0c 动作服务模式 1 订阅 发布模式 使用订阅 发布模式进行通信 xff0c 首先要知道主题的存在 发布者向主题发布消息 xff0c 订阅者订阅主题获取消息 其中订阅者不
  • Shell获取标准错误并赋值给变量

    今天在写shell的过程中遇到个问题 xff0c 用 XXX 61 ln s XXX XXX 想在运行软链接时候把错误输出赋值给一个变量 xff0c 却怎么也赋值不了 xff0c 最后发现应该是标准输出和标准错误的问题 一般来说 只会得到命
  • C语言提高

    头文件函数声明 防止头文件重复包含 xff08 相互包含陷入包含死循环 xff09 pragma once 兼容C 43 43 编译器 如果是C 43 43 编译器 xff0c 按c标准编译 ifdefine cplusplus C语言编译