共用体(联合体)、枚举、动态内存申请、字符串处理函数

一、共用体(联合体)

        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个字节,成员变量的地址都是相同的,证明了,共用体各个成员是占用同一块内存

共用体的特点:

  1. 同一段内存可以存放几种不同类型的数据,但是每一个数据只在一瞬间起作用
  2. 共用体变量中起作用的成员数据是最后一次存放的成员数据,在存入一个新成员数据后,原有的成员数据会被覆盖
  3. 共用体变量的地址和它各个成员的地址都是统一地址
  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;   

}

二、枚举

   将变量的值一一列举出来,变量的值只限于列举出来的值的范围内

枚举类型也是一个构造类型,即先定义枚举类型,再定义枚举变量

  1. 枚举类型的定义方法

   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;   

}

三、动态内存申请

  1. 动态分配内存的概述

   数组的长度一般是预先定义好的,在整个程序中固定不变,在实际编译中,所需的内存空间取决于实际输入的数据,而无法事先预定。为解决这一问题,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的区别:

  1. 函数的名字不一样
  2. 参数个数不一样
  3. malloc申请的内存,内存存放的数据是随机的,不确定的,calloc函数申请的内存中内容为0

         例子: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个字节

            共用体(联合体)、枚举、动态内存申请、字符串处理函数_第1张图片

         注意: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);//不要忘记释放        

}

 总结:申请的内存,一定不要把首地址丢了,不用的时候一定要释放内存

四、字符串处理函数

  1. 测字符串长度函数

  头文件:#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型数值对应