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;
}
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;
}
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;
}
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程序的内存四区模型
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;
}
因为两个字符串一样,所以编译器只会给它一个内存
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;
}
以上代码在此编译器里是先拷贝再释放内存,所以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;
}
以上代码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;
}
1.3函数调用模型
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
//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;
}
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;
}
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;
}
地址传递(二级指针作输出):
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;
}
4.2.2二维数组的本质
#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;
}
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;
}
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;
}
4.3数组指针
这种定义不常用
这种定义常用
第3种定义
二维数组数组名代表第0行的首地址(区别于第0行首元素地址,虽然值一样)
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;
}
4.3.1.1数组指针和二维数组结合
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的内存四区图
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;
}
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;
}
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;
}