1、共用体和结构体一样,也是一种构造类型的数据结构
既然你是构造类型,先定义类型,再用类型定义变量
定义共用体的方法与结构体差不多,把struct改成union就可以了
在进行某些算法,需要使用不同类型的变量存到同一段内存中,几个变量所使用空间相互重叠
这几种不同的变量共同占用一段内存的结构,在c语言中,被称作“共用体”类型结构
共用体的所有成员占有同一段内存空间
共用体的大小是其占用内存长度最大成员的大小
例1:
typedef struct data{
short int i;//2
char ch;//1
float f;//4
}DATA;
DATA temp;
结构体变量temp最小占7个字节(不考虑字节对齐)
例2:
typedef unio data{
short int i;//2
char ch;//1
float f;//4
}DATA;
DATA temp1;
共用体变量temp1占4个字节,即i,ch,f共用4个字节
例3
typedef struct data1{
int a;
int b;
int c;
}DATA1;
typedef union data2{
int a;
int b;
int c;
}DATA2;
int main(int argc,char *argv[])
{
DATA1 temp1;
DATA2 temp2;
printf("结构体变量temp1:%d\n",sizeof(temp1));//结果为12
printf("共用体变量temp2:%d\n",sizeof(temp2));//结果为4
return 0;
}
例子4
int main(int argc,char *argv[])
{
DATA1 temp1;
DATA2 temp2;
printf("结构体变量temp1:%d\n",sizeof(temp1));//结果为12
printf("共用体变量temp2:%d\n",sizeof(temp2));//结果为4
printf("&(temp2.a)=%p\n",&(temp2.a));
printf("&(temp2.b)=%p\n",&(temp2.b));
printf("&(temp2.c)=%p\n",&(temp2.c));
return 0;
}
分析:共用体temp2的大小为4个字节,成员变量的地址都是相同的,证明了,共用体各个成员是占用同一块内存
共用体的特点:
例子5:
typedef union data{
unsigned char a;
unsigned int b;
}DATA;
int main(int argc,char *argv[])
{
DATA temp;
temp.b=0xffffffff;
printf("temp.b=%x\n",temp.b);
temp.a=0x0d;
printf("temp.a=%x\n",temp.a);
printf("temp.b=%x\n",temp.b);
return 0;
}
将变量的值一一列举出来,变量的值只限于列举出来的值的范围内
枚举类型也是一个构造类型,即先定义枚举类型,再定义枚举变量
enum 枚举类型名{
枚举值列表;
};
在枚举值列表中应该列出所有可用值,称为枚举元素
枚举元素是常量,默认是从0开始编号
注意:枚举变量只能取枚举值列表所列元素
2.枚举变量的定义方法
enum 枚举类型名 枚举变量名;
例子1:
//定义一个枚举类型
enum week{
sun,mon,tue,wed,thu,fri,sat
};
int main(int argc,char *argv[])
{
//定义一个枚举类型变量
enum week workday;
//枚举变量只能取枚举值列表所列元素
workday=fri;
//thu=9;错误的,thu是常量,等号左值是常量,不能赋值
//workday=abc;错误的,枚举变量只能取枚举值列表所列元素
printf("workday=%d\n",fri);//打印结果为5
return 0;
}
总结:(1)枚举值是常量,不能在程序中用赋值语句再对其赋值
(2)枚举元素本身由系统定义一个表示序号的数值
默认从0开始顺序定义为0,1,2.......
enum week{
sun,mon,tue,wed,thu,fri,sat
};
sun=0,mon=1,...
(3)可以改变枚举值的默认值
enum week{
sun=3,mon,tue,wed,thu,fri=4,sat
};
sun=3,mon=4,tue=5,wed=6,thu=7,fri=4,sat=5
注意:在定义枚举类型的时候,枚举元素可以用等号给它赋值,用来表示元素从几开始编号
定义枚举类型之后,在程序中,就不能再次对枚举元素赋值,因为枚举元素是常量
例子:
enum week{
sun=3,mon,tue,wed,thu,fri=4,sat
};
int main(int argc,char *argv[])
{
enum week workday;
printf("sun=%d\n",sun);
printf("mon=%d\n",mon);
printf("tue=%d\n",tue);
printf("wed=%d\n",wed);
printf("thu=%d\n",thu);
printf("fri=%d\n",fri);
printf("sat=%d\n",sat);
return 0;
}
数组的长度一般是预先定义好的,在整个程序中固定不变,在实际编译中,所需的内存空间取决于实际输入的数据,而无法事先预定。为解决这一问题,c语言提供一些内存管理函数,需要动态的分配内存空间,也可将不使用的空间回收再利用
2.静态分配、动态分配
(1)静态分配
1)在程序编译或运行过程中,按照事先大小分配内存空间的分配方式。int a[10]
2)必须事先知道所需空间大小
3)分配在栈区或者全局变量区,一般以数组的形式
(2)动态分配
1)在程序运行过程中,根据需要大小自由分配空间
2)按需分配
3)分配在堆区,一般用特定的函数进行分配
3.动态分配函数
(1)malloc函数
头文件:#include
函数原型: void *malloc(size_t size);//size_t----->unsigned int (无符号整型)
功能说明:
在内存的动态存储区(堆区)中分配一块长度为size字节的连续区域,用来存放类型说明符指定的类型。
函数原型返回为void *指针,使用时必须做出相应的强制类型转换,给你分配的内存空间里面存放的数据不确定,一般就使用memset函数去初始化,类似于清空。
返回值:分配空间的起始地址(分配成功)
NULL (分配失败)
注意:1、调用malloc函数之后,一定用if语句判断一下,通过返回值确定申请内存是否成功
2、多次使用malloc函数申请空间,第一次和第二次申请的内存不一定是连续的
(2)free函数(释放内存的函数)
头文件:#include
函数定义:void free(void *ptr)
函数说明:free函数释放ptr指向的内存
注意:ptr指向的内存必须是malloc、calloc、relloc动态申请的内存
例子:
char *p=(char *)malloc(100);
free(p);
注意:free后,如果p值没有改变,还是指向的原先申请的内存空间,但是内存已经不能再用
p变成了野指针
一块动态申请的内存只能free一次,不能多次free
综合案例:
int main(int argc,char *argv[])
{
int *arry,i,n;
printf("please input the arry number:\n");
scanf("%d",&n);
arry=(int *)malloc(n*sizeof(int));
if(arry==NULL)
{
printf("申请内存失败\n");
return 0;
}
memset(arry,0,n*sizeof(int));//初始化全清0
for(i=0;i
(3)calloc函数
头文件:#include
函数原型:void *calloc(size_t nmemb, size_t size);
size_t是无符号整型,在头文件中,用typedef定义出来的
函数功能:在内存的堆中,申请nmemb块,每块的大小为size个字节的连续空间
函数的返回值:
分配空间的起始地址(分配成功)
NULL (分配失败)
calloc与malloc的区别:
例子:char *p=(char *)calloc(3,100);
//在堆中申请了三块,每块大小为100个字节,即300个字节连续的区域
(4)realloc函数(重新申请内存)
在调用malloc与calloc函数单次申请空间是连续的,再一次申请的两块内存就不一定连续。有时候有这种需求,先用malloc、calloc申请了一块内存,我想原先内存的基础之上挨着申请内存。或者malloc、calloc申请了一块内存,我想释放掉后面一部分内存。于是就发明了realloc函数
头文件:#include
函数原型:void *realloc(void *ptr, size_t size);
函数功能:在原先ptr指向的内存基础之上重新申请内存,新的内存大小为size个字节,如果原先内存后面有足够大的空间,就追加。如果后面内存满足不了,则realloc函数会在堆区找一个size个字节大小的内存申请,将原先内存中的内容拷贝过来,然后释放原先的内存,最后返回新内存地址
如果size比原来的小,则会释放原先内存的后面的存储空间,只留前面size大小的内存
返回值:新申请的内存的首地址
例子:char *p;
p=(char *)malloc(100);
//想追加50个字节
p=(char *)realloc(p,150);//p指向的内存的新的大小为150个字节
注意:malloc、calloc、realloc动态申请的内存,只有在free或者程序结束的时候才释放
(5)内存泄漏
内存泄漏的概念:
申请的内存,首地址找不到,再也没法使用,也没法释放,这块内存就被泄漏了。
内存泄漏例1:
int main()
{
char *p;
p=(char*)malloc(100);
//从此以后,你就没有办法再去使用那100个字节,即动态申请的100个字节就被泄漏了
}
内存泄漏例2:
void fun()
{
char *p;
p=(char*)malloc(100);
p=”hello world”;//p指向了别的地方
...
}
int main()
{
fun();
fun(); //每调用一次,泄漏一次,一次100个字节
}
内存泄漏解决方案1:
void fun()
{
char *p;
p=(char*)malloc(100);
free(p);
}
int main()
{
fun();
fun(); //每调用一次,泄漏一次,一次100个字节
}
内存泄漏解决方案2:
char * fun()
{
char *p;
p=(char*)malloc(100);
return p;
}
int main()
{
char *q;
q=fun();//可以通过q去使用申请的100个字节的内存
free(q);//不要忘记释放
}
总结:申请的内存,一定不要把首地址丢了,不用的时候一定要释放内存
头文件:#include
函数定义: size_t strlen(const char *s);//const char *s,指针指向的内容不可以更改
函数功能:
测字符指针s指向的字符串中字符的个数,不包括’\0’
返回值:字符串中字符的个数
例子:
int main(int argc,char *argv[])
{
char str1[20]="hello";
char *str2="hello";
printf("%d\n",sizeof(str1));//20
printf("%d\n",sizeof(str2));//4
printf("%d\n",strlen(str1));//5
printf("%d\n",strlen(str2));//5
return 0;
}
sizeof测量数据的占用内存空间大小
如果是测量数组的名字,则测的是数组占多少个字节
如果是测量指针变量,则测的是指针变量本身占多少个字节,32位系统中为4,
strlen是个库函数,它测的是字符指针指向的字符串中字符的个数,无论是指针变量还是数组名字
2.字符串拷贝函数
头文件:#include
函数原型: char *strcpy(char *dest, const char *src);
函数说明:
拷贝src指向的字符串到dest指针指向的内存中,’\0’也会拷贝
函数的返回值:
目的内存的地址
注意:在使用此函数的时候,必须保证dest指向的内存空间足够大,否则会造成内存污染
例子:
int main(int argc,char *argv[])
{
char str[100]="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
stpcpy(str,"hello");
printf("str=%s\n",str);//打印hello,说明'\0'也会被拷贝
printf("str+6=%s\n",str+6);//str+6=aaaaaaaaaaaaaaaaaaaaaaaaaaa
return 0;
}
//hello0aaaaaaaaaaaaaaaaaaaaaaaaaaa
char *strncpy(char *dest, const char *src, size_t n);
函数说明:
将src指向的字符串前n个字节,拷贝到dest指向的内存中
返回值:目的内存的地址
注意:1)strncpy不拷贝’\0’;
2)如果说n大于src指向的字符串的字符个数,则在dest后面填充n-strlen(src)个’\0’;
例子:验证了strncpy不拷贝’\0’
int main(int argc,char *argv[])
{
char str[100]="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
strncpy(str,"helloworld",5);
printf("str=%s\n",str);//打印=helloaaaaaaaaaaaaaaaaaaaaaaaaaaaa,说明'\0'不会被拷贝
return 0;
}
例子:验证了n大于src指向的字符串的字符个数,则在dest后面填充n-strlen(src)个’\0’;
int main(int argc,char *argv[])
{
int len,i;
char str[100]="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
len=strlen(str);//算一下数组里面有多少个字符
strncpy(str,"helloworld",15);
printf("str=%s\n",str);//打印str=helloworld,不够的'\0',补15-10=5个'\0'
for(i=0;i
3.字符串追加函数
头文件:#include
函数原型: char *strcat(char *dest, const char *src);
函数的功能:
strcat函数追加src字符串到dest指向的字符串后面,追加的时候会追加’\0’
注意:保证dest指向的内存空间足够大
例子:
int main(int argc,char *argv[])
{
/*char str[100]="aaaaaaaa";
char *src="hello";
strcat(str,src);
printf("str=%s\n",str);//打印str=aaaaaaaahello*/
char str[100]="aa\0aaaaaa";
char *src="hello";
strcat(str,src);
printf("str=%s\n",str);//打印str=aahello,说明是往\0处进行追加,而且追加时自己也追加了\0
return 0;
}
char *strncat(char *dest, const char *src, size_t n);
追加src指向的字符串的前n个字符,到dest指向的字符串的后面
注意:如果n大于src的字符个数,则将src字符串追加到dest指向的字符串的后面,追加时会追加’\0’
例子:
int main(int argc,char *argv[])
{
/*char str[100]="aa\0aaaaaa";
char *src="hello";
strncat(str,src,3);
printf("str=%s\n",str);//打印str=aahel*/
char str[100]="aa\0aaaaaa";
char *src="hello";
strncat(str,src,7);
printf("str=%s\n",str);//打印str=aahello,说明如果大于追加字符数,只需要将字符串追加过去即可
return 0;
}
4.字符串比较函数
头文件:#include
函数原型:int strcmp(const char *s1, const char *s2);
函数说明:
比较s1和s2指向的字符串的大小
比较方法:逐个字符取比较ascll码,一旦比出大小就返回
如果字符都一样,则返回0
返回值:
如果s1指向的字符串大于s2指向的字符串 返回1
如果s1指向的字符串大于s2指向的字符串 返回-1
如果相等,返回0
例子:
int main(int argc,char *argv[])
{
char *str1="hello";
char *str2="helloworld"; //str1的'\0'的ascii值0与str2的'w'的ascii值119
int ret;
ret=strcmp(str1,str2);
if(ret>0)
printf("str1指向的字符串大于str2指向的字符串\n");
else if(ret<0)
printf("str1指向的字符串小于str2指向的字符串\n");
else
printf("两个字符串相等\n");
return 0;
}
int strncmp(const char *s1, const char *s2, size_t n);
函数说明:比较s1与s2指向的字符串的前n个字符
例子:
int main(int argc,char *argv[])
{
char *str1="hellokitty";
char *str2="helloworld";
int ret;
ret=strncmp(str1,str2,5);
if(ret>0)
printf("str1指向的字符串大于str2指向的字符串\n");
else if(ret<0)
printf("str1指向的字符串小于str2指向的字符串\n");
else
printf("两个字符串相等\n");
return 0;
}
5.字符查找函数
头文件:#include
函数原型: char *strchr(const char *s, int c);
函数说明:
在字符指针s指向的字符串中,找ascii值为c的字符
注意:是首次匹配,如果说指针s指向字符串有多个ascii值为c的字符,则找第一个字符
返回值:找到了返回找到的字符的地址
找不到返回NULL
例子:
int main(int argc,char *argv[])
{
char *str="hellokittyhellokitty";
char *p;
p=strchr(str,'k');
if(p==NULL)
{
printf("没有找到该字符\n");
return 0;
}
printf("p-str=%d\n",p-str);//打印p-str=5,说明确实是找到的该字符的首字符
return 0;
}
函数原型:char *strrchr(const char *s, int c);
函数说明:末次匹配
在s指向的字符串中,找最后一次出现ascii值为c的字符
例子:
int main(int argc,char *argv[])
{
char *str="hellokittyhellokitty";
char *p;
p=strrchr(str,'k');
if(p==NULL)
{
printf("没有找到该字符\n");
return 0;
}
printf("p-str=%d\n",p-str);//打印p-str=15,说明确实是末次匹配
return 0;
}
6.字符串匹配函数
头文件:#include
函数原型:
char *strstr(const char *haystack, const char *needle);
函数说明:
在haystack指向的字符串中查找needdle指向的字符串,也是首次匹配
返回值:
找到了,返回字符串的首地址
没找到,返回NULL
例子:
int main(int argc,char *argv[])
{
char *str="agsfasgf$@$sdjusgd$@$";
char str1[20]="$@$";
char *ret=strstr(str,str1);//去str指向空间找str1指向的内容
printf("ret-str=%d\n",ret-str);//打印ret-str=8,说明确实是首次匹配
return 0;
}
7.空间设定函数
头文件:#include
函数原型:void *memset(void *s, int c, size_t n);
函数功能:
memset函数是将s指向的内存空间的n个字节全部赋值为c
参数:s:void*类型的通用指针,指向任意类型的指针,即指向需要修改的内存
c:给s指向的空间赋的值
n:确定将s指向的内存中的n个字节全部用c代替
返回值:
目的内存的首地址,即s的地址
例子:
int main(int argc,char *argv[])
{
char str[100]="helloworld";
printf("str=%s\n",str);//str=helloworld
memset(str,'a',5);
printf("str=%s\n",str);//str=aaaaaworld
memset(str,0,100);
printf("str=%s\n",str);//str=
return 0;
}
8.字符串转换数值
atoi/atol/atof //字符串转换数值
头文件:#include
函数原型: int atoi(const char *nptr);
函数功能:
将nptr指向的字符串转换成整数,返回
返回值:
转换后的整数,此值由输入字符转换而来。如果输入无法转换为该类型的数值,则返回0
例子:
int num;
num=atoi(“999”); //atoi与int整型数值对应
则num的值为999;
long atol(const char *nptr);//atol与long int长整型数值对应
double atof(const char *nptr);atof与double型数值对应