C语言提高

1.1数据类型本质分析

1.1.1数组形参退化为指针

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
//如果数组作为函数参数,数组形参退化为指针 a后面方括号里面的数字没有意义,但不能是负数也不能是0
//void printf_array(int a[1], int n)
//void printf_array(int a[], int n)
void printf_array(int *a,int n)
{
	
	
	int i = 0;
	printf("数组元素为:");
	for(i = 0;i<n;i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");

	//a当做指针用,指针类型,32位编译器指针长度为4个字节
	int m = sizeof(a) / sizeof(a[0]);//元素个数
	printf("数组退化为指针后m=%d\n", m);
}
int main(void)
{
	
	int a[] = {10,7,1,9,4,6,7,3,2,0 };
	int n = 10;
	int m;

	m = sizeof(a) / sizeof(a[0]);
	printf("原始数组中的元素个数m=%d\n",m);
	printf_array(a, n);
	system("pause");
	return 0;
}

0ba4f7d5c7a14afd95bd6d081a31d497.png

1.1.2数据类型的本质

  1.数据类型可理解为创建变量的模具:是固定内存大小的别名。
  2.数据类型的作用:编译器预算对象(变量)分配的内存空间大小。
  3.注意:数据类型只是模具,编译器并没有分配空间,只有根据类型(模具)创建变量(实物),编译器才会分配空间。

#include <stdio.h>

int main(void)
{
    int a = 10; //告诉编译器,分配4个字节的内存
    int b[10];  //告诉编译器,分配4*10 = 40 个字节的内存

    printf(" b:%d\n b+1:%d\n &b:%d\n &b+1: %d\n", b, b + 1, &b, &b + 1);

    //b+1 和 &b+1的结果不一样 
    //是因为 b 和 &b 所代表的数据类型不一样
    //b  代表数组首元素的地址
    //&b 代表整个数组的地址,整个数组的地址和首元素地址一样

    return 0;
} 

0d5ea55c67e545a0a42008b45acb7249.png

 

1.1.3数据类型的大小

#include <stdio.h>
int main(void)
{
    int a = 10; //告诉编译器,分配4个字节的内存
    int b[10];  //告诉编译器,分配4*10 = 40 个字节的内存

    printf("sizeof(a):%d \n", sizeof(a));
    printf("sizeof(int *):%d \n", sizeof(int *));
    printf("sizeof(b):%d \n", sizeof(b));
    printf("sizeof(b[0]):%d \n", sizeof(b[0]));
    printf("sizeof(*b):%d \n", sizeof(*b));
    printf("*b:%d \n", *b);
    return 0;
}

9fbc66c089b84446bea0c16de06827f7.png

1.1.4数据类型的别名;

#include <stdio.h>

struct People
{
    char name[64];
    int age;
} ;

typedef struct People
{
    char name[64];
    int age;
} people_t;
/* 给结构体类型起别名 */

typedef unsigned int u32;   //给unsigned int类型取别名

int main(void)
{
    struct People p1; 
    people_t p2;
    u32 a;      

    p1.age = 10;    
    p2.age = 11;        

    a = 10;                 

    return 0;                   
}

1.2.1变量的本质

1、程序通过变量来申请和命名内存空间int a=0

2、通过变量名访问内存空间

3、修改变量有几种方法

(1)直接

(2)间接

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

int main(void)
{
	int i = 0;

	// 通过变量直接操作内存
	i = 10;

	int *p = &i;
	printf("&i:%d\n", &i);
	printf("p:%d\n", p); 

	// 通过内存编号间接操作内存
	*p = 100;
	printf("i = %d, *p = %d\n", i, *p);

	system("pause");
 	return 0;
}

1.2程序的内存四区模型

8e2d9aa399f44007951d29be5eeb520c.png

1.2.1 全局区

 

#include <stdio.h>

char * getStr1()
{
    char *p1 = "abcdefg2";//文字常量区
    return p1;
}
char *getStr2()
{
    char *p2 = "abcdefg2";//文字常量区
    return p2;
}
int main(void)
{
    char *p1 = NULL;
    char *p2 = NULL;
    p1 = getStr1();
    p2 = getStr2();

    //打印p1 p2 所指向内存空间的数据
    printf("p1:%s , p2:%s \n", p1, p2);

    //打印p1 p2 的值
    printf("p1:%p , p2:%p \n", p1, p2);

    return 0;
}

因为两个字符串一样,所以编译器只会给它一个内存

8b60eda640c147a5a940922a86ba627f.png

625f4474f8264493b7a883b0e9e317a1.png

1.2.2栈区

        栈空间由系统分配和回收

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

char *get_str()
{
	char str[]="abdefhgj";//栈区 
	return str;
}
int main(void)
{
	char buf[128]={0};
	strcpy(buf,get_str());
	printf("buf=%s\n",buf);//乱码 不确定 
	printf("\n");
	system("pause");
	return 0;
}

d20c08c76ce14d92bd7200c75947c02c.png

       以上代码在此编译器里是先拷贝再释放内存,所以buf输出的是确定值;但是不同编译器不一样,如果编译器是先释放再复制,那buf的内存就是不确定值,也就是乱码。

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


char *get_str()
{
	char str[] = "abdefhgj";//栈区 
	printf("str=%s\n", str);
	return str;
}
int main(void)
{
	char buf[128] = { 0 };
	//strcpy(buf,get_str());
	//printf("buf=%s\n",buf);//乱码 不确定 

	char *p = NULL;
	p = get_str();
	printf("p=%s\n", p);

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

f2932b701dbf4002863d29a298e8cb90.png

cf0d0b61fb124b639cada2b9ca86cfd8.png

        以上代码p指针指向str的地址,但是get_str()函数运行玩后,str空间自动收回(收回的意思应该是该内存可以被其他代码使用,用没用就不知道了),str空间内容未知,有可能还保留着之前的内容,有可能被附上别的内容,即str内存不确定是什么,所以是乱码

1.2.3堆区

        堆空间由程序员分配和回收

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

char *get_str2()
{
	/*在堆上分配了100个字节内存,返回这块内存的首地址,
	把地址强制转换成char *类型后赋给char *类型的指针变量p。
	同时告诉我们这块内存将用来存储char类型的数据。
	也就是说你只能通过指针变量p来操作这块内存。
	这块内存本身并没有名字,对它的访问是匿名访问/
	但是不一定每次都能分配成功 ,函数同样要注意这点:
	如果所申请的内存块大于目前堆上剩余内存块(整块)
	则内存分配会失败,函数返回NULL。注意这里说的
	“堆上剩余内存块”不是所有剩余内存块之和,
	因为malloc函数申请的是连续的一块内存。
	既然malloc函数申请内存有不成功的可能
	那我们在使用指向这块内存的指针时,
	必须用if(NULL!=p)语句来验证内存确实分配成功了。*/
	char *tmp = (char *)malloc(100);
	if (tmp == NULL)
	{
		return NULL;
	}
	strcpy(tmp,"absfsgjghgu");
		return tmp;
}
int main(void)
{
	
	char *p = NULL;
	p = get_str2();
	if (p != NULL)
	{
		printf("p=%s\n", p);
		free(p);
		p = NULL;
	}
	
	system("pause");
	return 0;
}

c06810eb95654952852eebbca8092b74.png

1.3函数调用模型

f7916b766ce040ca984b70220b404489.png

df839926c8684632aa474c4642dec28e.pngadd2b1d2e49b4204810d79d4154d8a75.png

9b366ff33db940ffa565e260761d9835.png

3ae5beddd4714d70a1d26759e3e83cc0.png

1.4作业

        strcpy(char * destination, const char * source )函数的功能是将源头指向的C字符串复制到目标指向的数组中,包括结尾的'/0'字符,并在'\0'字符处停止拷贝。

        第一个参数的类型是char*(字符型指针),它指向拷贝的目的地内存块的起始地址,它的作用是为函数提供目的地的地址,以便函数能够准确地将内容拷贝到目的地的地址空间。

        第二个参数的类型是被const修饰(const修饰的指针,const在*左表示指针指向的内容不可修改,const在*右表示指针的指向不可修改)的char*(字符型指针),它指向拷贝信息的来源内存块的起始地址,它的作用是为函数提供拷贝源头的地址,以便函数能够准确找到拷贝的源头进行拷贝.

//画出下面代码的内存四区图
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

char *get_mem(int size)
{
	char *p2 = NULL;            //分配4个字节的内存 栈区也叫临时区
	p2 = (char *)malloc(size);

	return p2;
}

int main(void)
{
	char buf[100];
	int a = 10;     //分配4个字节的内存 栈区也叫临时区
	int *p;         //分配4个字节的内存
	p = &a;

	*p = 20;

	char *mp = get_mem(100);
	strcpy(mp, "ABCDEFG");//把字符串"ABCDEFG"赋值给指针mp指向的内容,而不是给指针p

	if (mp != NULL)
	{
		printf("before=%d\n", mp);
		printf("before *mp=%s\n", mp);
		free(mp);//告诉系统,mp原来指向的内存可以被别人使用
		printf("after=%d\n", mp);
		//释放完,mp就是野指针,但还是保存之前的地址,所以最好复制为NULL
		mp = NULL;
	}

	return 0;
}

b9b9d306716e4fedb1cb6e6baa88a59c.png

41b3e557b0454ccd8a1e8f7ca25659e9.png  printf("p=%s\n",p);//输出的是p指针指向的内容

  printf("p=%d\n",p);//输出的是p指针指向的地址(十进制)

printf("p=%p\n",p);//输出的是p指针指向的地址(十六进制)

  空指针指向的地址是0

2.1指针的强化

2.1.1指针变量和它所指的内存

2.指针

2.1指针强化

2.1.1指针是一种数据类型


1)指针变量也是一种变量,占有内存空间,用来保存内存地址测试指针变量占有内存空间大小。
2)*p操作内存
在指针声明时,*号表示所声明的变量为指针
在指针使用时,*号表示操作指针所指向内存空间中的的值
*p相当于通过地址(p变量的值)找到一块内存,然后操作内存

*p放在等号的左边赋值(给内存赋值,写内存)

*p放在等号的右边取值(从内存获取值,读内存)
3)指针变量和它指向的内存块是两个不同的概念。

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

int main(void)
{
	char *p = NULL;
	char buf[] = "abcdef";
	printf("p1=%d\n", p);
	// 改变指针变量的值
	p = buf;
	printf("p2=%d\n",p);
	//指针变量,和指针指向的内存是两个不同的概念
	p = p + 1;//改变了指针变量的值,改变了指针的指向
	printf("p3=%d\n",p); 
	printf("buf=%s\n",buf);
	printf("*p=%c\n", *p);//打印p指向的首地址对应的单个字符
	printf("*p=%s\n",p);//打印p指向的首地址对应的字符串

	//改变指针指向的内容,不会影响指针的值
	printf("改变指针指向的内容,不会影响指针的值\n");
	buf[1] = '2';
	printf("p3=%d\n", p);
	printf("buf2=%s\n", buf);

	printf("\n");
	*p = 'm';
	printf("p4=%d\n", p);
	printf("buf3=%s\n", buf);


	system("pause");
	return 0;
}

2af27063040c4975b76016413787e350.png

2.1.1.1写内容时要保证内容可写

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

int main(void)
{
	//写内容时,一定要保存内容可写
	char *buf2 = "abcsdefh";//文字常量区,内存不可写,可读
	//buf2[2] = '3'; //err

	char buf3[] = "sbgdjdgj";
	buf3[1] = '3'; //ok
	printf("buf3=%s\n", buf3);

	system("pause");
	return 0;
}

4)指针是一种数据类型,是指它指向的内存空间的数据类型。
指针步长(p++),根据所致内存空间的数据类型来确定。
p++等价于(unsigned char )p+sizeof(a);
5)当我们不断的给指针变量赋值,就是不断的改变指针变量(和所指向内存空间没有任何关系)。指针指向谁,就把谁的地址赋值给指针。

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

char buf[100]="absfdghh";
int i;
for(i=0;i<strlen(buf);i++)
{
p=&buf[i];//或者p=buf+1;这两句话是等价的
printf("p=%d,%c\n",p,*p);
}

2.1.2通过形参改变实参的值

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

int get_a()
{
	int a = 10;
	return a;
}

void get_a2(int a)
{
	a = 22;
}
void get_a3(int *p)
{
	*p = 33 ; 
}
void get_a4(int *a1, int *a2, int *a3, int *a4)
{
	*a1 = 1;
	*a2 = 2;
	*a3 = 3;
	*a4 = 4;
}


int main(void)
{
	int a = get_a();
	printf("a=%d\n", a);
	get_a2(a);
	printf("a2=%d\n", a);
	//如果想通过形参改变实参内容的值,必须地址传递
	get_a3(&a);
	printf("a3=%d\n", a);

	int a1, a2, a3, a4;
	get_a4(&a1, &a2, &a3, &a4);
	printf("a1=%d,a2=%d,a3=%d,a4=%d\n", a1, a2, a3, a4);
    system("pause");
	return 0;
	

}

a8cb48d8f6314e64aa6f29e09fdc906f.png

2.1.3二级指针间接赋值

int *p=0x1122;

int **q=&p;//q是二级指针,指向一级指针p的地址

*q是取p的值

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

void fun(int *p)
{

	p = (int*)0xaabb;
	printf("fun:p=%p\n", p);
}

void fun2(int **p)
{

	*p = (int*)0xeeff;
	printf("fun2:p=%p\n", *p);
}


int main(void)
{

	int *p = (int*)0x1122;//指针变量的值为0x1122,指针变量指向的内存未知,指针变量的地址是确定的
	printf("p=%p\n", p);

	fun(p);//值传递
	printf("fun:p1=%p\n", p);

	fun2(&p);//地址传递
	printf("fun2:p2=%p\n", p);



	system("pause");
	return 0;

}

 26b6f13436e54213b0e6fb96d2553f9d.png

2.1.3.1如何定义合适类型的指针变量:

某个变量的地址需要定义一个怎么样类型的变量保存:  在这个类型的基础上加一个*

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

2.1.4对二级指针的理解

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


int main(void)
{

	int a = 10;
	int *p = &a;
	
	printf("十六进制p=%p\n", p);//打印十六进制指针的值
	printf("十六进制&a=%p\n", &a);

	printf("十进制p=%d\n", p);//打印十进制指针的值
	printf("十进制&a=%d\n", p);

	printf("&p=%d\n", &p);//打印指针p的地址

	printf("*p=%d\n", *p);//打印指针p指向的内存
	printf("a=%d\n", a);


	printf("\n");

	int *q = (int*)0x1122;
	printf("十进制q=%d\n", q);
	printf("&q=%d\n", &q);
	//printf("*q=%d\n", *q);//打印不出来,指针所指内容不确定

	printf("\n");

	int **q1 = &p;
	printf("q1=%d\n",q1);//打印指针q1的值,也就是p的地址
	printf("*q1=%d\n",*q1);//打印指针q1指向的内存,也就是指针p的值

	system("pause");
	return 0;

}

04bc57d04a1145478740bc0c6f725083.png

2.1.5指针作参数输入输出特性

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

void fun(char *p /* in */)
{
	//给指针p指向的内存区域拷贝
	strcpy(p, "abcdefg");
}

void fun2(char *p)
{
	if (p == NULL)
	{
		return;
	}
	//给指针p指向的内存区域拷贝
	strcpy(p, "abcdefg");
}

void fun3(char **p /*  out */, int *len)
{
	if (p== NULL)
	{
		return;
	}
	char *tmp = (char*)malloc(100);
	if (tmp == NULL)
	{

	}

	//给指针p指向的内存区域拷贝
	strcpy(tmp, "abcdefg");
	*p = tmp;
	*len = strlen(tmp);

}

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

	printf("\n");
	char *str = NULL;
	fun2(str);//不能给空指针,或未知的内存地址拷贝
	printf("str=%d\n",str);

	//输出,被调用函数分配内存,地址传递
	printf("\n");

	char *p=NULL;
	int len = 0;
	fun3(&p,&len);
	if (p != NULL)
	{
		printf("p=%s, len=%d\n", p,len);
	}

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

 

ce61c0f3a6d14171b9098248a444d515.png

6357b77239fe4909952ee8640aa41e1a.png

//1 C语言的字符串 以零'\0'结尾的字符串

//2 在C语言中没有字符串类型  通过字符数组来模拟字符串 

(数字 0 和字符‘\0’等价)

1.sizeof为一个操作符,执行sizeof的结果,在编译期间就已经确定;

      strlen是一个函数,是在程序执行的时候才确定结果。

2. sizeof和strlen对于求字符串来讲,sizeof() 求字符串类型的大小,包括’\0’;strlen() 求字符串的长度不包括‘\0’(数字 0 和字符‘\0’等价)。

3字符串

3.1字符串基础操作

3.1.1字符串初始化

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


int main(void)
{
	//不指定长度,没有0结束符,打印出来是乱码
	char buf[] = { 'a', 'b', 'c' };
	printf("buf=%s\n", buf);
	//指定长度,后面没有赋值的元素自动补0
	char buf1[100] = { 'a', 'b', 'c' };
	printf("buf1=%s\n", buf1);
	//所有元素赋值为0
	char buf2[100] = { 0 };
	printf("buf2=%s\n", buf2);
	char buf3[2] = { '1', '2' };//越界
	printf("buf3=%s\n", buf3);
	char buf4[50] = { '1', 'a', 'b', '0', '7' };
	printf("buf4=%s\n", buf4);
	char buf5[50] = { '1', 'a', 'b', 0,'7' };
	printf("buf5=%s\n", buf5);
	char buf6[50] = { '1', 'a', 'b', '\0', '7' };
	printf("buf6=%s\n", buf6);

	//使用字符串初始化,常用
	char buf7[] = "asbfhjh";
	//strlen:测字符串长度,不包含数字0,字符串'\0'
	//sizeof: 测数组长度,包含数字0,字符'\0'
	printf("strlen=%d, sizeof=%d\n", strlen(buf7), sizeof(buf7));

	char buf8[100] = "asbfhjh";
	printf("strlen=%d, sizeof=%d\n", strlen(buf8), sizeof(buf8));
	
	printf("\n");
	system("pause");
	return 0;
}

41371fb484fb4742ab6a208398ac6c77.png

3.1.2指针法和数组法操作字符串

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


int main(void)
{
	char buf[] = "gasdfgjkp";
	int i = 0;
	int n = strlen(buf);

	for (i = 0; i <n; i++)
	{
		printf("%c",buf[i]);
	}
	printf("\n");

	//指针方法
	//数字名字,数组元素地址 
	
	char *p = buf;
	for (i = 0; i <n; i++)
	{
		printf("%c", p[i]);
	}
	printf("\n");
    

     //p[i]和*(p+i)等价
	for (i = 0; i <n; i++)
	{
		printf("%c", *(p+i));
	}
	printf("\n");
	
	for (i = 0; i <n; i++)
	{
		printf("%c", *(buf+i));
	}

	//buf和p完全等价吗
	//p++; ok
	//buf++;  err  
	//buf只是一个常量,不能修改

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

3.1.3字符串拷贝


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

void my_strcpy(char *dst, char *str)
{
	int i;
	for (i = 0; str[i] != '\0'; i++)
	{
		dst[i] = str[i];//*(dst+i)=*(str+i);

	}
	//补结束符或者定义的时候dst[100]={0};
	dst[i] = 0;
	//*(dst+i)=0
}

void my_strcpy2(char *dst, char *str)
{
	
	while (*str != 0)
	{
		*dst = *str;
		dst++ ;
		str++;
	}
	*dst = 0;
}

void my_strcpy3(char *dst, char *str)
{

	while (*str != 0)
	{
		*dst++ = *str++;//先用了再加,*dst=*str;dst++;str++;
	}
	*dst = 0;
}
void my_strcpy4(char *dst, char *str)
{

	while ((*dst++ = *str++)!= 0)
	{
		
	} 
}

void my_strcpy5(char *dst, char *str)
{
	//while(a=b)则表示 b的值传给 a,然后以 a 是否为 0 作为循环条件进行判断。
	while (*dst++ = *str++)
	{
		NULL;
	}
}
int main(void)
{
	char str[] = "asdfghjkl";
	char dst[100];


	//my_strcpy(dst, str);
	//printf("%s\n", dst);

	my_strcpy5(dst, str);
	printf("%s\n", dst);

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

}


完善字符串拷贝

如果不用辅助变量代替dst和str的话,while循环结束后dst就指向了字符串的结束标识符'\0',那就打印不出来


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

int my_strcpy6(char *dst, char *str)
{
	//成功为0,失败非0
	//1、判断形参指针是否为NULL
	//2、最好不要直接使用形参
	if (dst == NULL || str == NULL)
	{
		return -1;
	}
	
	//辅助变量代替形参
	char *to = dst;
	char *from = str;
	//while(a=b)则表示 b的值传给 a,然后以 a 是否为 0 作为循环条件进行判断。
	while (*to++ = *from++)
	{
		NULL;
	}

	printf("my_strcpy6:dst=%s\n",dst);
	return 0;
}

int main(void)
{
	char str[] = "asdfghjkl";
	char dst[100] = {0};
	int ret = 0;


	//my_strcpy5(dst, str);
	//printf("%s\n", dst);

	ret=my_strcpy6(dst,str);
	printf("%s\n", dst);

	if (ret != 0)
	{
		printf("my_strcpy6 err:%d\n", ret);
		return ret;
	}


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

}


3.1.4strstr中的while和do-while模型


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


int main01(void)
{
	char *p = "11abcd456abcd7533abcd44abcd56";
	int n = 0;

	do
	{
		p = strstr(p,"abcd");
		if (p!= NULL)
		{
			n++;
			p = p+strlen("abcd");

		}
		else//如果没有找到匹配的字符串
		{
			break;
		}
	} while (*p != 0);

	printf("%d\n", n);

		

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

}

int main02(void)
{
	char *p = "11abcd456abcd7533abcd44abcd56";
	int n = 0;

	while ((p=strstr(p,"abcd"))!=NULL)
	{
		p = p + strlen("abcd");
		n++;
		if (*p == 0)
		{
			break;
		}
	} 

	printf("%d\n", n);

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

}

int my_strstr(char *p, int *n)
{
	//辅助变量
	char *tmp = p;
	int i =0;
	while ((tmp = strstr(tmp, "abcd")) != NULL)
	{
		//能进循环肯定有匹配的子串

		//重新设置起点位置
		tmp = tmp + strlen("abcd");
		i++;
		if (*tmp == 0)
		{
			break;
		}
	}
	//间接赋值
	*n = i;
	return 0;
}

int main(void)
{
	char *p = "11abcd456abcd7533abcd44abcd56";
	int n = 0;
	int ret = 0;

	ret = my_strstr(p, &n);
	if (ret != 0)
	{
		return -1;
	}
	else
	{
		printf("%d\n", n);
	}

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

}



3.1.5两头堵模型

strncpy():将str指向的字符串的前n个字符拷贝至dest指向的内存空间中,函数原型如下:

char *strncpy(char *dest, const char *src, size_t n);

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

int main01(void)
{
	int n = 0;
	char *p = "   abcdefg   ";
	int begin = 0;
	int end = strlen(p)-1;
	while (isspace(p[begin]) && p[begin] != 0)
	{
		begin++;
	}
	while (isspace(p[end]) && p[end] != 0)
	{
		end--;
	}
	n = end - begin + 1;
	printf("%d\n", n);
	

	printf("%s\n",*p);
	system("pause");
	return 0;

}

//封装
//找非空字符串的个数
int fun(char *p, int*n)
{
	if (p == NULL || n == NULL)
	{
		return -1;
	}
	int begin = 0;
	int end = strlen(p) - 1;
	while (isspace(p[begin]) && p[begin] != 0)
	{
		begin++;
	}
	while (isspace(p[end]) && p[end] != 0)
	{
		end--;
	}
	*n = end - begin + 1;
	return 0;
}
//提取非空格字符,中间非空字符需是连续的
int fun2(char*p, char *buf)
{
	if (p == NULL || buf == NULL)
	{
		return -1;
	}


	int begin = 0;
	int end = strlen(p) - 1;
	while (isspace(p[begin]) && p[begin] != 0)
	{
		begin++;
	}
	while (isspace(p[end]) && p[end] != 0)
	{
		end--;
	}
	int n = end - begin + 1;
	strncpy(buf, p + begin, n);
	return 0;
}
int main(void)
{
	int n = 0;
	char *p = "   abcdefg   ";
	int ret = 0;
	ret = fun(p, &n);
	if (ret != 0)
	{
		return -1;
	}
	else
	{
		printf("%d\n", n);
	}


	//提取非空格字符到buf中
	char buf[100] = { 0 };
	int ret1 = 0;
	ret1 = fun2(p, buf);
	if (ret1 != 0)
	{
		return -1;
	}
	else
	{
		printf("%s\n",buf);
	}

	system("pause");
	return 0;

}


3.1.5.1提取奇偶字符串


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


int get_str1str2(char *p, char *buf1, char *buf2)
{
	if (p == NULL || buf1 == NULL || buf2 == NULL)
	{
		return -1;
	}
	int n = strlen(p);
	int i;
	for (i = 0; i < n; i++)
	{
		if (i % 2 == 0)
		{
			*(buf1++) = p[i];//*(p+i);
		}
		else
		{
			*(buf2++) = *(p + i);
		}
	}
	return 0;

}

int main(void)
{
	char *p = "a1b2c3d4";
	char buf1[50] = { 0 };
	char buf2[50] = { 0 };
	int ret = 0;
	ret=get_str1str2(p, buf1, buf2);
	if (ret != 0)
	{
		return -1;
	}
	printf("buf1=%s\n", buf1);
	printf("buf2=%s\n", buf2);


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

}


 3.1.5.2提起key字符串并除去空格

         当时把if括号里面的==写成了=,造成指针指向的地方不对,打印不出结果,


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




//提取非空字符串
int trimspace(char *inbuf, char *outbuf)
{
	char *p = inbuf;
	char *buf = outbuf;
	if (p == NULL || buf == NULL)
	{
		printf("trimspace:err:%d\n");
		return -1;
		
	}


	int begin = 0;
	int end = strlen(p) - 1;
	while (isspace(p[begin]) && p[begin] != 0)
	{
		begin++;
	}
	while (isspace(p[end]) && end>0)
	{
		end--;
	}
	if (end == 0)
	{
		return -2;
	}
	int n = end - begin + 1;
	strncpy(buf, p + begin, n);
	return 0;
}


int get_keyvalue(char*keyvaluebuf, char *keybuf,char *valuebuf,int *valuebuflen)
{

	int  ret = 0;
	if (keyvaluebuf == NULL || keybuf == NULL || valuebuf == NULL || valuebuflen==NULL)
	{
		printf("空指针:err:%d\n");
		return -1;
		
	}

	char *p = NULL;
      p= strstr(keyvaluebuf, keybuf);
	  if (p == NULL)
	  {
		  printf("找不到keybuf\n");
		  return -2;

	  }
	  //如果找到,重新设置起点位置
	  p = p + strlen(keybuf);

	  p = strstr(p,"=");
	  if (p ==NULL)
	  {
		  printf("找不到=\n");
		  return -3;
	  }

	  //如果找到,重新设置起点位置
	  p = p + strlen("=");
	 

	  //提取非空字符
	  ret = trimspace(p, valuebuf);
	  if (ret != 0)
	  {
		  printf("找不到非空字符:err:%d\n");
			  return ret;
	  }

	  //获取长度
	  *valuebuflen = strlen(valuebuf);

	return 0;
}


int main(void)
{
	
	char *p = "key4=    value4";
	char *key = "key4";
	char value[100] = {0};
	int  len=0;

	int ret = 0;

	ret = get_keyvalue(p,key,value,&len);

	if (ret!= 0)
	{
		return -1;
	}
	else
	{
		printf("%s\n", value);
		printf("len=%d\n", len);
	}


	system("pause");
	return 0;

}


d42597c4775b4e6d90f54d45eed78e5b.png 
 

3.1.6const用法

//从左往右看,跳过类型,看修饰哪个字符
//如果是*,说明指针指向的内存不能改变
//如果是指针变量,说明指针的指向不能改变,指针的值不能修改
const char *p = buf;//等价于char const *p1 = buf;
char * const p2 = buf;等价于const char * const p2 = buf;

//p3为只读,指向不能变,指向的内存也不能变
const char * const p3 = buf;


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

typedef struct Mystruct
{
	int a;
	int b;
}Mystruct;

void fun(Mystruct *p)
{
	//指针能变
	//p=NULL
	//指针指向的内容也可以变
	//p->a = 10;ok
}

void fun1(Mystruct const *p)
{
	//p=NULL //ok
	//p->a = 10;//err
}
void fun2(Mystruct * const p)
{
	//p = NULL;//err
	//p->a = 10;//ok
}

void fun3(Mystruct const * const p)
{
	//只读
	//Mystruct tmp;
	//tmp.a = p->a;

}
int main(void)
{
	


	system("pause");
	return 0;

}


 3.1.7指针作函数参数(指针作输出)

值传递:


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

int getMem(char*p)
{
	p = (char*)malloc(sizeof(char) * 100);
	if (p == NULL)
	{
		return -1;
	}
	strcpy(p, "ajlgjdslga");
	printf("%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\n",p);

	system("pause");
	return 0;

}


c0686019188c4aefbd4bed57111245dd.png

f4865b0ffa5e4fb3b3b321490e5d8b4f.png

地址传递(二级指针作输出):

ca3199d415ed4bc29c1c9e033a415967.png

3e355b33778b465cb5c8999100ecd877.png

4二级指针 

4.1二级指针做输入

一维指针数组*p[]形参要用**p(形参的定义和实参一样一定不会错,
例如*p[]的形参也写成*p[],p[]可以用*p代替,于是*p[]就可以写成**p)

二维数组中的一维数组有点类似指针,每行的一维数组类似于一个指针
所以二维数组a[][]应的形参要用**a,或者a[][]

输入:主调函数分配内存       输出:被调用函数分配内存


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

void sort_array( char **p, int n)
{
	char *tmp;
	int i = 0;
	int j = 0;

	for (i = 0; i < n - 1; i++)
	{
		for (j = i+1;j < n; j++)
		{
			if (strcmp(p[i],p[j] )> 0)
			{
				tmp = p[i];
				p[i] = p[j];
				p[j] = tmp;
			}
		}
	}
}

 void print_array(char **p, int n)
{
	int i;
	for (i = 0; i < n; i++)
	{
		printf("%s,", p[i]);
	}
	printf("\n");
}
int main(void)
{
	char *p[] = {"111111111","00000000","bbbbbbbbb","aaaaaaaaa"};
	int n = sizeof(p)/sizeof(p[0]);
	int i = 0;
	int j = 0;
	char *tmp = NULL;

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

	sort_array(p, n);

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

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

} 
 

4.2二维数组

4.2.1二维数组的大小

char [][30];//err,没有指定行数时,必须初始化


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


int main(void)
{
	
	char a[4][30] = { "22222", "11111", "aaaaa", "bbbbb" };
	printf("sizeof(a)=%d\n", sizeof(a));
	printf("sizeof(a[0])=%d\n", sizeof(a[0]));

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

} 
 

91e789cd5d98402487c3b8927a68bac5.png

4.2.2二维数组的本质

35d0f6067a674a52866a18249f6594ef.png

acc010567a8d4a6399e1e6063c9f6157.png

002a6b1fdad444d996fa78630385b19e.png


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


int main(void)
{
	
	char a[4][30] = { "22222", "11111", "aaaaa", "bbbbb" };
	//printf("sizeof(a)=%d\n", sizeof(a));
	//printf("sizeof(a[0])=%d\n", sizeof(a[0]));
	printf("a=%d\n", a);//数组首行地址,首行地址其实也是由首行首元素地址
	printf("&a=%d\n",&a);//整个二维数组地址
	printf("a=%d\n", a[0]);//数组首行首元素地址
	printf("&a[0][0]=%d\n", &a[0][0]);//首行首元素地址


	printf("\n");
	for (int i = 0; i < 4; i++)
	{
		printf("%s\n", a[i]);//a+i,*(a+i)都可以
	}
	printf("\n");
	system("pause");
	return 0;

} 
 

c1ce74a4e9e54ad0a695f7c5a5df66af.png

4.2.3二级指针作输入(第二种模型:二维数组)


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

void sort_array(char a[][30], int n) //如果这里参数写成**a,则函数里面打印a和a+1相差4,因为指针类型占四个字节
{
	int i = 0;
	int j = 0;
	char tmp[30];
	for (i = 0; i < n - 1; i++)
	{
		for (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);
			}
		}
	}
	printf("\n");
}

void print_array(char a[][30], int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%s, ", a[i]);
	}
	printf("\n");
}

int main(void)
{
	
	char a[4][30] = { "22222", "11111", "aaaaa", "bbbbb" };
	int n = sizeof(a) / sizeof(a[0]);
	print_array(a, n);
	sort_array(a, n);
	print_array(a, n);
	printf("\n");
	system("pause");
	return 0;

} 
 

 4.4.3二级指针第三种内存模型(动态生成二维内存)


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



int main(void)
{
	//动态分配一个数组,每个元素都是char*
	//char *ch[3]
	int n = 3;
    //buf指向一个指针数组,里面有3个元素
	char **buf = (char**)malloc(n*sizeof(char *));//等号右边有的类似char *buf[3],但不等价
	if (buf == NULL)
	{
		return -1;
	}
	for (int i = 0; i < n; i++)
	{
        //每个buf[i]都是一个指针
		buf[i] = (char *)malloc(30 * sizeof(char));
		char str[30];
		sprintf(str, "test%d%d", i, i);
		strcpy(buf[i], str);
	}

	for (int i = 0; i < n; i++)
	{
		printf("%s, ", buf[i]);
	}



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

}


 a8116c2f67c14bb5a3b4997d555854e0.png

 p[i]等价于*(p+i)也等价于数组的a[i],也就是取指针指向的数组的对应元素


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



char ** getMem(int n)
{
	char **buf = (char**)malloc(n*sizeof(char *));//char *buf[3]
	if (buf == NULL)
	{
		return NULL;
	}
	for (int 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 )//值传递
{
	for (int i = 0; i < n; i++)
	{
		printf("%s, ", buf[i]);
	}
	printf("\n");
}

void free_buf(char **buf, int n)
{
	for (int i = 0; i < n; i++)
	{
		free(buf[i]);
		buf[i] = NULL;
	}
	if (buf != NULL)
	{
		free(buf);
		buf = NULL;
	}
}
 
int main(void)
{
	char **buf;
	int n = 3;
	buf = getMem(n);
	print_buf(buf,n);//值传递
	free_buf(buf,n);
	buf = NULL;
	
	printf("\n");
	system("pause");
	return 0;
}

c576038001af4d94894ec77b3717e049.png 4.3数组指针

  a610707fd383413083b592a12651cc79.png

这种定义不常用

 b9399a3922a94d77b90ef6d83a4d19c1.png

2e456501be294796b70c85c730c82848.png

 这种定义常用

f52aaf764a2c455e9d5761e749e2ddbb.png

第3种定义 

9aaafe750d0f423e963735fe15662d9e.png

 f51eb50415434719a3688ea8a2376f88.png

二维数组数组名代表第0行的首地址(区别于第0行首元素地址,虽然值一样)
 b07246bfaae7434382e9ee9eae620f88.png

 4.3.1二维数组存储本质也是一维数组


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

void printA(int *a, int n)
{
	int i = 0;
	for (i = 0;i < n; i++)
	{
		printf("%d  ", a[i]);
	}
}

int main(void)
{
	int a[][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,12 };
	printA((int*)a, sizeof(a) / sizeof(a[0][0]));

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

 

c60d60931a4741089f508bef05109ca0.png

4.3.1.1数组指针和二维数组结合

7e362d80b50f466799bd0a344f7c8a07.png9b94ce5fed024484b32d8dc5381403ff.png

5结构体

strcut Teacher
{
char name[50];
int age;
};

strcut Teacher t1;
strcut Teacher *p=&t1;//则t1.name=p->name=(*p).name

5.1结构体嵌套一级指针


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


typedef struct Teacher
{
	//char name [50];//已经分配内存了,可以给name赋值
	char *name;//没有分配内存   不可以给指向结构体的指针p的p->name赋值
	int  age;
}Teacher;



int main(void)
{
		//1
	     Teacher t;
	    /*t.name = (char*)malloc(30);
		strcpy(t.name, "lily");
		t.age = 22;//为什么不用给t.age分配malloc空间呢,因为定义结构体的时候分配了
		printf("name=%s,age=%d\n", t.name, t.age);
		if (t.name != NULL)
		{
			free(t.name);
			t.name = NULL;
		}*/


		 //2
		 //Teacher *p=NULL;
		/* Teacher *p= (Teacher*)malloc(sizeof(Teacher));
		 p->name = (char*)malloc(30);
		 strcpy(p->name, "lily");
		 p->age = 22;//为什么不用给t.age分配malloc空间呢,因为定义结构体的时候分配了
		 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 *p = (Teacher*)malloc(sizeof(Teacher)*3);
		 int i = 0;
		 char buf[30];
		 for (i = 0; i < 3; i++)
		 {
			 p[i].name = (char*)malloc(30);
			 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);

		 }
		 for (i = 0; i < 3; i++)
		 {
			 if (p[i].name!=NULL)
			 {
				 free(p[i].name);
				 p[i].name = NULL;
			 }
		 }

		 if (p != NULL)
		 {
			 free(p);
			 p = NULL;
		 }
		 
	
	printf("\n");
	system("pause");
	return 0;  
}

 

 

//2的内存四区图

41952e4e5a3a42368198acec63dc33bc.png

5.1.2结构体做函数参数

也就是把以上的结构体嵌套一级指针的程序封装成函数


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


typedef struct Teacher
{
	//char name [50];//已经分配内存了,可以给name赋值
	char *name;//没有分配内存   不可以给指向结构体的指针p的p->name直接赋值,需要先分配内存
	int  age;
}Teacher;

void showTeacher(Teacher *p, int n)
{
	int i;
	for (i = 0; i < n; i++)
	{
		printf("%s, %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);
	int i = 0;
	char buf[30];
	for (i = 0; i < n; i++)
	{
		p[i].name = (char*)malloc(30);
		sprintf(buf, "name%d%d%d", i, 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 *q = (Teacher*)malloc(sizeof(Teacher) * 3);
	int i = 0;
	char buf[30];
	for (i = 0; i < n; i++)
	{
		q[i].name = (char*)malloc(30);
		sprintf(buf, "name%d%d%d", i, i, i);
		strcpy(q[i].name, buf);
		q[i].age = 20 + i;
	}
	*tmp = q;
	return 0;
}

int main(void)
{
		
	Teacher *p = NULL;
	int ret = 0;
	ret = getMem2(&p, 3);
	if (ret != 0)
	{
		return ret;
	}
	  
		 int n = 3;
		 showTeacher(p, n);
		 freeTeacher(p, n);
		 p = NULL;

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

 

7bccc749279a4ff786850a559a101ccc.png 5.2结构体嵌套二级指针


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

//1个导师有n个学生
typedef struct Teacher
{
	char **stu;//二维内存
}Teacher;


int main(void)
{
#if 0
	char**name = NULL;

	int n = 3;
	int i = 0;
	/*在堆上分配了n*sizeof(int *)个字节内存,返回这块内存的首地址,
	把地址强制转换成char **类型后赋给char **类型的指针变量name。
	同时告诉我们这块内存将用来存储char*类型的数据。
	也就是说你只能通过指针变量name来操作这块内存。
	这块内存本身并没有名字,对它的访问是匿名访问*/
	name = (char**)malloc(n*sizeof(int *));
	for (i = 0; i < n; i++)
	{
		/*在堆上分配了30字节内存,返回这块内存的首地址,
		把地址强制转换成char *类型后赋给char *类型的指针变量name[i]。
		同时告诉我们这块内存将用来存储char类型的数据。*/
		name[i] = (char*)malloc(30);//name[i]相当于一个一级指针;
		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);
	}


	//1 
	Teacher t;
	//t.stu[3]	一个老师三个学生

	//char *t.stu[3];
	int n = 3;
	int i = 0;
	/*在堆上分配了n*sizeof(int *)个字节内存,返回这块内存的首地址,
	把地址强制转换成char **类型后赋给char **类型的指针变量t.stu。
	同时告诉我们这块内存将用来存储char*类型的数据。
	也就是说你只能通过指针变量t.stu来操作这块内存。
	这块内存本身并没有名字,对它的访问是匿名访问*/
	t.stu = (char**)malloc(n*sizeof(int *));
	for (i = 0; i < n; i++)
	{
		/*在堆上分配了30字节内存,返回这块内存的首地址,
		把地址强制转换成char *类型后赋给char *类型的指针变量t.stu[i]。
		同时告诉我们这块内存将用来存储char类型的数据。*/
		t.stu[i] = (char*)malloc(30);//t.stu[i]相当于一个一级指针;
		strcpy(t.stu[i], "lily");
	}
	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);
	}


	//2
	Teacher *p = NULL;
	//p->stu[3];1个老师三个学生
	p = (Teacher*)malloc(sizeof(Teacher));

	//char *p->stu[3];
	int n = 3;
	int i = 0;
	/*在堆上分配了n*sizeof(int *)个字节内存,返回这块内存的首地址,
	把地址强制转换成char **类型后赋给char **类型的指针变量p->stu。
	同时告诉我们这块内存将用来存储char*类型的数据。
	也就是说你只能通过指针变量p->stu来操作这块内存。
	这块内存本身并没有名字,对它的访问是匿名访问*/
	p->stu = (char**)malloc(n*sizeof(int *));
	for (i = 0; i < n; i++)
	{
		/*在堆上分配了30字节内存,返回这块内存的首地址,
		把地址强制转换成char *类型后赋给char *类型的指针变量p->stu[i] 。
		同时告诉我们这块内存将用来存储char类型的数据。*/
		p->stu[i] = (char*)malloc(30);//p->stu[i]相当于一个一级指针;
		strcpy(p->stu[i], "lily");
	}
	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 = NULL;
	}

#endif



	//3
	Teacher *q = NULL;
	//Teacher q[3]
	//q[i].stu[3] 三个老师各自三个学生

	q = (Teacher*)malloc(sizeof(Teacher) * 3);
	//Teacher q[3]
	int i = 0;
	int j = 0;
	

	for (i = 0; i < 3; i++)
	{
		//q[i].stu
		//q[i]->.stu
		q[i].stu = (char**)malloc(3 * sizeof(char*));
		//char *stu[3]
		for (j = 0; j < 3; j++)
		{
			//char buf[30]
			q[i].stu[j] = (char*)malloc(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;
}





 

806a6d8f38ae4f7caf14ea61119039a3.png

 5.2.1结构体嵌套二级指针强化

也就是把以上的结构体嵌套二级指针的程序封装成函数


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

//1个导师有n个学生
typedef struct Teacher
{
	char **stu;//二维内存
}Teacher;

//n1为老师个数,n2为每个老师带的学生个数
int creatTeacher(Teacher **tmp, int n1,int n2)
{
	if (tmp == NULL)
	{
		return -1;
	}
	Teacher*q = (Teacher*)malloc(sizeof(Teacher) * n1 );
	//Teacher q[3]
	int i = 0;
	int j = 0;


	for (i = 0; i < n1; i++)
	{
		//q[i].stu
		//q[i]->.stu
		q[i].stu = (char**)malloc(n2 * sizeof(char*));
		//char *stu[3]
		for (j = 0; j < n2; j++)
		{
			//char buf[30]
			q[i].stu[j] = (char*)malloc(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)
{
	if (q == NULL)
	{
		return;
	}
	int i = 0;
	int j = 0;
	for (i = 0; i < n1; i++)
	{
		for (j = 0; j < n2; j++)
		{
			printf("%s ", q[i].stu[j]);
		}
		printf("\n");
	}
	printf("\n");
}

void freeTeacher(Teacher **tmp, int n1, int n2)
{
	if (tmp == NULL)
	{
		return;
	}
	Teacher *q = *tmp;    //*tmp等价于传进来的&q取内容也就是函数外面的q的值
	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)
	{

		//3
		Teacher *q = NULL;
		//Teacher q[3]
		//q[i].stu[3] 三个老师各自三个学生
		int ret = 0;
		ret = creatTeacher(&q, 3, 3);
		if (ret != 0)
		{
			return ret;
		}

		
		showTeacher(q, 3, 3);
		freeTeacher(&q, 3, 3);

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





5.3结构体的深拷贝和浅拷贝


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.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 = 22;

		Teacher t2;
		//t2 = t1;//浅拷贝

		//深拷贝,人为增加内存,重新拷贝一下
		t2.name = (char*)malloc(30);
		strcpy(t2.name, t1.name);
		t2.age = 22;
		printf("[t2] name:%s, age:%d\n", t2.name, t2.age);

		if (t1.name != NULL)
		{
			free(t1.name);
			t1.name = NULL;
		}

		//浅拷贝不能释放两次
		if (t2.name != NULL)
		{
			free(t2.name);
			t2.name = NULL;
		}
	
		printf("\n");
		system("pause");
		return 0;
	}





501ca40b3e284c5891b53a383f8f5127.png

 

 

  • 26
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值