C语言基础-下

目录

指针变量为什么要求类型

封装一个函数,实现两个变量值的交换 

指针指向固定的区域

练习

定义一个指针变量指向数组

指针偏移遍历数组

指针和数组名

练习函数,指针,数组结合

二维数组的地址认知

二维数组的地址写法应用

 数组指针

数组指针和二维数组的配合应用

函数指针

函数指针编程实战-回调函数

指针数组

指针函数

二级指针

二级指针的应用

二级指针不能直接指向二维数组

 指针完结

初识字符串

字符串的存储方式     

sizeof和strlen的区别

 malloc动态开辟内存空间 

字符串常用操作函数

字符串拷贝strcpy( )函数      

字符串拷贝strncpy( )函数 

断言函数assert( )

字符串拼接strcat( )函数

字符串拼接strncat( )函数

字符串比较strcmp( )函数

字符串比较strncmp( )函数

字符填充memset( )函数

结构体引入

定义结构体和使用变量

结构体的应用

结构体和数组的结合     

结构体数组应用

结构体指针变量引入

结构体指针来访问结构体变量

结构体指针的应用

结构体指针数组以及函数综合应用

结构体二级指针     

联合体共用体概念引入   

共用体数据覆盖问题

共用体的应用

枚举类型介绍   

typedef关键字


指针变量为什么要求类型

   间接运算符会 根据指针变量的类型,访问不同大小的空间;

封装一个函数,实现两个变量值的交换 

#include<stdio.h>

void changeData(int *p1,int *p2);

int main(void)
{
   
   int data1 = 10;
   int data2 = 20;   

   printf("交换之前:data1 = %d data2 = %d\n",data1, data2);
   changeData(&data1,&data2);
   printf("交换之后:data1 = %d data2 = %d\n",data1, data2);




   return 0;
}


void changeData(int *p1,int *p2)
{
      int temp;
      temp = *p1;
      *p1 = *p2;
      *p2 = temp; 


 
}

指针指向固定的区域

#include<stdio.h>

int main(void)
{
   int a = 10;
   
   printf("address of a is %p\n",&a);

   int *p =(int *)0x7ffd5fc8dd74;
   printf("p is %p\n",p);

   return 0;
}

练习

输入三个数a,b,c;要求不管怎么输入,在输出的时候,a,b,c就是由大到小的顺序输出,用函数封装实现

#include<stdio.h>

void changeData(int *max, int *b, int *min);
    

int main(void)
{
   int a,b,c;
   printf("请输入a的值:\n");
   scanf("%d",&a);
   printf("请输入b的值:\n");
   scanf("%d",&b);
   printf("请输入c的值:\n");
   scanf("%d",&c); 

   changeData(&a,&b,&c);

   printf("a = %d, b = %d, c = %d\n",a,b,c);

   return 0;
}


void changeData(int *a, int *b, int *c)
{
       
      int temp;
      if(*a < *b)
      {
          temp = *a;
          *a = *b;
          *b = temp;
      }
      if(*a < *c)
      {
          temp = *a;
          *a = *c;
          *c = temp;
      }
      if(*b < *c)
      {
          temp = *b;
          *b = *c;
          *c = temp;
      } 


  
}

定义一个指针变量指向数组

      在C语言中,数组名代表数组中首元素(即序号为0的元素)的地址。因此,下面两个语句等价:

       p = &a[0];  // p的值是a[0]的地址

       p = a;        // p的值是数组a首元素(即a[0])的地址

#include<stdio.h>

    

int main(void)
{
   int arr[3] = {1,2,3};
   int *p;
   int *p1;

   p = &arr[0];
   p1 = arr;

   printf("arr[0]的地址为:%p\n",p);
   printf("arr的首元素的地址:%p\n",p1);


   return 0;
}

指针偏移遍历数组

指针增加和数组得关系:让指针指向数组首元素的地址,指针每次偏移都是偏移一个存储单元的大小,而不是指针在数值上单纯的加1.

#include<stdio.h>
#define NTABLE 

int main(void)
{
   int arr[3] = {1,2,3};
   int *p = arr;
 
   for(int i = 0; i < 3; i++)
   {
       /*(p+i)的意思是:到内存p的位置,移动i个存储单元,检索存储在那里的值*/
       printf("%d ",*(p+i));
   }

   putchar('\n');


   return 0;
}

指针和数组名

  指针当作数组名,下标法访问;数组名当作指针偏移来访问。

#include<stdio.h>

int main(void)
{
   int arr[3] = {1,2,3};
   int *p = arr;
 
   for(int i = 0; i < 3; i++)
   {
       /*(p+i)的意思是:到内存p的位置,移动i个存储单元,检索存储在那里的值*/
       printf("%d ",p[i]);
   }
   putchar('\n');

   for(int i = 0; i < 3; i++)
   {
       printf("%d ",*(arr+i));
   }

   putchar('\n');



   return 0;
}

 不能对数组名进行自增操作,因为数组名是一个常量。(例如:arr[3] = {1,2,3};  arr++不可取)

sizeof以字节的形式给出运算对象所占存储空间的大小;strlen用于计算字符串的字符个数(不包括空字符)

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



int main(void)
{
   int arr[3] = {1,2,3};
   int *p = arr;
   char a[] = "hello";


   printf("sizeof arr is %ld\n",sizeof(arr));   //3x4=12
   printf("sizeof p   is %ld\n",sizeof(p));     // 8 
   printf("sizeof int is %ld\n",sizeof(int));   // 4
   printf("sizeof a is %ld\n",sizeof(a));       // 6
   printf("strlen a is %ld\n",strlen(a));       // 5 计算字符的个数,不包括空字符

   return 0;
}

效率对比:

对于使用指针和数组下标的选择:

系统在使用数组下标对数组成员变量进行访问时,开销比较大,指针的访问效率是远远大于数组名的访问效率的。

但是只有在指针正确访问时,才能比下标法更有效率。

下标法更加容易理解,在可读性方面,也更加的具有优势,具体怎么选择,也没有一定的说法。

练习函数,指针,数组结合

练习1:函数封装数组初始化和数组遍历

#include<stdio.h>

void initArray(int *parr,int size);//数组初始化
void printArray(const int *parr,int size);//数组遍历


int main(void)
{
   int array[3];
   int size = sizeof(array)/sizeof(array[0]);

   initArray(array,size);
   printArray(array,size);

   return 0;
}

void initArray(int *parr,int size)
{
    for(int i = 0; i < size; i++)
    {
          printf("请输入%d个元素的值:\n",i+1);
          scanf("%d",parr+i); 
    }


}

void printArray(const int *parr,int size)
{


    printf("数组元素的值为:\n");
    for(int i = 0; i < size; i++)
    {
         printf("%d ",*(parr+i));

    }
    putchar('\n');

}

练习2:将数组中的n个元素按逆序存放 ---- 函数封装

思路:

        根据数组的大小算出遍历到中间的值,即size/2

        第一个元素a[i]跟最后一个元素a[j]的下标关系为:i+j = size -1;

        根据下标的关系进行值的交换

#include<stdio.h>

void initArray(int *parr,int size); // 数组的初始化
void printArray(const int *parr,int size); // 数组的遍历
void reverseArray(int *parr, int size);    //逆序存放数组的值


int main(void)
{
   int array[7];
   int size = sizeof(array)/sizeof(array[0]);

   initArray(array,size);
   printArray(array,size);
   reverseArray(array,size);
   printArray(array,size);

   return 0;
}

void initArray(int *parr,int size)
{
    for(int i = 0; i < size; i++)
    {
          printf("请输入%d个元素的值:\n",i+1);
          scanf("%d",parr+i); 
    }


}

void printArray(const int *parr,int size)
{


    printf("数组元素的值为:\n");
    for(int i = 0; i < size; i++)
    {
         printf("%d ",*(parr+i));

    }
    putchar('\n');

}



void reverseArray(int *parr, int size)
{
    int temp;
    for(int i = 0;i < size/2; i++)
    {
          int j = size -1 - i;
          temp = *(parr+i);
          *(parr+i) = *(parr+j);
          *(parr+j) = temp; 
    }

}




二维数组的地址认知

1.多维数组元素的地址

      为了说清楚指向多维数组元素的指针,先回顾一下多维数组的性质,以二维数组为例。设有一个二维数组a,它有3行4列。

      它的定义为:

      int a[3][4] = {{1,3,5,7},{9,11,13,15},{17,19,21,23}};

      a是二维数组名。a数组包含3行,即3个行元素;a[0],a[1],a[2]。而每一个行元素又是一个一维数组,它包含4个元素(即4个列元素)。例如,a[0]所代表的一维数组又包含4个元素:a[0][0],a[0][1],a[0][2],a[0][3]。可以认为二维数组是“数组的数组”,即二维数组a是由3个一维数组所组成的。

 

     a[0],a[1],a[2]既然是一维数组名,而C语言又规定了数组名代表数组首元素地址,因此a[0]代表一维数组a[0]中第0列元素的地址,即&a[0][0]。也就是说,a[1]的值&a[1][0],a[2]的值是&a[2][0]。

问题:a是谁的地址,a[0]又是什么,那*a或者*(a+0)呢

答:a表示二维数组的地址,a[0],*a表示的是一维数组的地址

#include<stdio.h>

    

int main(void)
{
   int arr[3][4] = {{11,22,33,44},{12,13,15,16},{22,66,77,88}};

   printf("二维数组的首地址:%p,偏移1后的地址是:%p\n",arr,arr+1);
   printf("一维数组的首地址:%p,偏移1后的地址是:%p\n",*(arr+0),*(arr+0)+1);
   printf("一维数组的首地址:%p,偏移1后的地址是:%p\n",arr[0],arr[0]+1);

   return 0;
}

二维数组的地址写法应用

使用地址的写法遍历二维数组

#include<stdio.h>

int main(void)
{
   int arr[3][4] = {{11,22,33,44},{12,13,15,16},{22,66,77,88}};
   int i,j;
   
   for(i = 0; i < 3; i++)
   {
          for(j = 0; j < 4; j++)
   	  {
 		printf("address:%p,data:%d\n",*(arr+i)+j,*(*(arr+i)+j));
          }

   }
   printf("===========================================\n");
   for(i = 0; i < 3; i++)
   {
          for(j = 0; j < 4; j++)
   	  {
 		printf("address:%p,data:%d\n",arr[i]+j,*(arr[i]+j));
          }

   }

   return 0;
}

 数组指针

“[ ]”运算符优先级是大于“*”的运算符;因此要想表示数组指针,需要将*和标识符括起来;

例如:int (*p)[4];

#include<stdio.h>

int main(void)
{
   int arr[3][4] = {{1,2,3,},{4,5,6},{7,8,9}};
   int i,j;
   int (*p)[4];
   p = arr;

   printf("p存放的值为:%p,p偏移1的值为:%p\n",p,p+1);

   for(i=0; i<3; i++)
   {
       for(j=0; j<4; j++)
       {
             printf("address:%p data:%d\n",*(arr+i)+j,*(*(arr+i)+j));

       }
   }


   return 0;
}

数组指针和二维数组的配合应用

 练习:获取用户输入的二维数组下标,输出二维数组任意行列的数;

#include<stdio.h>

void get_arr_data(int (*p)[4])
{
   int i,j;
   printf("输入行和列的值:\n");
   scanf("%d%d",&i,&j);

   printf("第%d行 第%d列的值为:%d\n",i,j,*(*(p+i)+j));

}

int main(void)
{
   int arr[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
   get_arr_data(arr);

   return 0;
}

函数指针

      如果在程序中定义了一个函数,在编译时,编译系统为函数代码分配一段存储空间,这段存储空间得起始地址(又称入口地址)称为这个函数的指针。 (函数名就是地址)

      函数关心的是返回值的类型; 形参的个数,形参的类型;因此我们在定义函数指针的时候都需要将这样体现出来。例如:  指向函数int getData(int a, int b)的指针是int (*p)(int a, int b);

示例1:

函数指针指向无形参的函数   

#include<stdio.h>


void done();


int main(void)
{
   void (*p)();//定义一个函数指针,该函数无返回类型,无形式参数。
   p = done;
   (*p)();//通过该函数指针调用welcome()函数。


   return 0;
}



void done()
{
   printf("consider it done!\n");
}

示例2:

函数指针指向有返回值有形参的函数  

#include<stdio.h>


int add(const int a,const int b);


int main(void)
{
   int(*p1)(const int,const int);
   p1 = add;

   printf("函数返回值为:%d\n",(*p1)(3,4));


   return 0;
}

int add(const int a,const int b)
{

   return (a+b);

}

函数指针编程实战-回调函数

练习:有两个整数a和b,由用户输入1,2或3.如输入1,程序就给出a和b中大者,输入2,就给出a和b中小者,输入3,则求a与b之和。

#include<stdio.h>

int getInt();       //获取整数
int getChoice();    //获取用户正确的响应
int getMax( int a,  int b);   //获取较大值
int getMin( int a,  int b);   //获取较小值
int add( int a,  int b);      //获取两数之和
void getResult(int a, int b, int(*p)(int, int));  //回调函数

int main(void)
{
     int a;
     int b;
     int choice;
     int (*p)(int ,int );    
       
 
     while( (choice = getChoice()) != 4)
     {
            
	     printf("请输入a的值:\n");
	     a = getInt();
	     printf("请输入b的值:\n");
	     b = getInt();



	     switch(choice){

		     case 1:
			     p = getMax;
			     break;
		     case 2:
			     p = getMin;
			     break;
		     case 3:
			     p = add;
			     break;
		     default:
			     printf("Program error!\n");
			     break;               

	     }

             
             getResult(a,b,p);
     }



     return 0;
}




int getInt()
{
   int a;


   while(scanf("%d",&a) != 1)
   {
        while(getchar() != '\n')  
              continue;
  
        printf("please input int,such as 1,2,3:\n");

   }

   return a;
}

int getChoice()
{
     int input;
     printf("enter number of your choice:\n");
     printf("1.获取a和b中较大者  2.获取a和b中较小者\n");
     printf("3.获取a和b的和      4.退出程序\n");
     input = getInt(); 

     while((input < 1 || input > 3) && input != 4)
     {
              printf("Please respond withh 1,2,3 or 4\n");
              input = getInt();
     } 


     return input;
}

int getMax(int a,int b)
{
    return (a < b)?(b):(a);
}

int getMin(int a,int b)
{
    return (a < b)?(a):(b);
}
int add(int a,int b)
{
    return (a+b);
}



void getResult(int a,int b,int(*p)(int ,int))
{
     printf("结果为:%d\n",(*p)(a,b));
  
}

指针数组

        一个数组,若其元素均为指针类型,称为指针数组,也就是说,指针数组中的每一个元素都存放一个地址,相当于一个指针变量,下面定义一个指针数组:

        int *p[4];

        由于[ ]比*优先级高,因此p先与[4]结合,形成p[4]形式,这显然是数组形式,表示p数组有4个元素。然后再与p前面的“*”结合,“*”表示此数组是指针类型的,每个数组元素(相当于一个指针变量)都可指向一个整型变量。

        注意不要写成

        int (*p)[4];                        //这是指向一维数组的指针变量

        定义一维指针数组的一般形式为

        类型名 *数组名[数组长度]; 

        类型名中应包括“*”,如“int *”表示是指向整型数据的指针类型

/*指针数组:指针数组中的每一个元素都是一个指针变量*/
#include<stdio.h>


int main(void)
{
    int a = 10;
    int b = 20;
    int c = 30;
    int d = 40;

    int *p[4] = {&a, &b, &c, &d};
    
    for(int i = 0; i < 4; i++)
    {
        printf("%d ",*p[i]);
    }
    putchar('\n');

    return 0;
}

 函数指针数组:该数组中的每一个元素都是一个指向函数的指针。例如:

/*函数指针数组*/
#include<stdio.h>

int getMax(int a, int b);
int getMin(int a, int b);
int add(int a, int b);


int main(void)
{
   int(*p[3])(int , int) = {getMax,getMin,add}; //函数指针数组
    
   for(int i = 0; i < 3; i++)
   {
       printf("%d ",(*p[i])(5,10));
   } 

   putchar('\n');

   return 0;
}


int getMax(int a, int b)
{
    return (a < b)?(b):(a);
}
int getMin(int a, int b)
{
    return (a < b)?(a):(b);
}
int add(int a, int b)
{
    return (a+b);
}


指针函数

     一个函数可以返回一个整型值,字符值,实型值等,也可以返回指针型的数据,即地址。其概念与以前类似,只是返回的值的类型是指针类型而已。

     例如"int *a(int x,int y);",a是函数名,调用它以后能得到一个int *型(指向整型数据)的指针,即整型数据的地址。x和y是函数a的形参,为整型。

     请注意在*a两侧没有括号,在a的两侧分别为*运算符和( )运算符。而( )优先级*,因此a先与( )结合,显然这是函数形式。这个函数前面有一个*,表示此函数是指针型函数(函数值是指针)。最前面的int表示返回的指针指向整型变量。

练习:有a个学生,每个学生有b门课程的成绩,要求在用户输入学生序号以后,能输出该学生的全部成绩。用指针函数来实现。

/*返回值是指针的函数*/
#include<stdio.h>

int *getStudent(int num,int(*p)[3]);
int getInt();

int main(void)
{
   int num;
   int *p;
   int student[4][3] ={{78,87,68},{99,88,82},{60,68,69},{79,76,71}};//存放4个学生语数英的成绩

   printf("请输入序号0,1,2,3:\n");
   num = getInt();

   while(num < 0 || num > 3)
   {
      while(getchar() != '\n')
        continue;

      printf("plase response with 0,1,2 or 3:");
      num = getInt();
   }


 
   p = getStudent(num,student);
 
   for(int i = 0; i < 3; i++)
   {
      printf("%d ",*(p+i));
   }   

   putchar('\n'); 

   return 0;
}


int *getStudent(int num,int(*p)[3])
{
   int *ret = *(p + num);

   return ret;
}


int getInt()
{
   int input; 
   while(scanf("%d",&input) != 1)
   {
          while(getchar() != '\n')
               continue; //处理错误输入
          printf("please input a integer:");
   }
   return input;
}

二级指针

     二级指针存放的是指针变量的地址,例如:

#include<stdio.h>


int main(void)
{
    int data = 100;
    int *p = &data;
    int**p1 = &p;

    printf("data的地址是:%p\n",&data);
    printf("p的值是:%p\n",p);
    printf("p的地址是:%p\n",&p);
    printf("p1的值是:%p\n",p1); 

    printf("通过二级指针访问变量data:%d\n",**p1);




    return 0;
}

二级指针的应用

/*返回值是指针的函数*/
#include<stdio.h>

void getStudent(int num,int(*p)[3],int **pp);

int main(void)
{
   int num;
   int *p;
   int student[4][3] ={{78,87,68},{99,88,82},{60,68,69},{79,76,71}};//存放4个学生语数英的成绩

   printf("请输入序号0,1,2,3:\n");
   scanf("%d",&num);

 
   getStudent(num,student,&p);
 
   for(int i = 0; i < 3; i++)
   {
      printf("%d ",*(p+i));
   }   

   putchar('\n'); 

   return 0;
}


void getStudent(int num,int(*p)[3],int **pp)
{
   *pp = *(p + num);

}

二级指针不能直接指向二维数组

      二级指针存放的值是一个指针变量的地址;二维数组名所代表的地址偏移的大小是一维数组的地址大小,而二级指针偏移的大小是指针变量地址大小

/*返回值是指针的函数*/
#include<stdio.h>


int main(void)
{
   int student[4][3] ={{78,87,68},{99,88,82},{60,68,69},{79,76,71}};//存放4个学生语数英的成绩
   int data = 10;
   int *p1 = &data;
   int **p2 = &p1;

   printf("p2=%p p2+1=%p\n",p2,p2+1);   
   printf("student=%p student+1=%p\n",student,student+1);

   return 0;

 指针完结

练习:

定义下面各种指针:

1   一个整型数: int a;

2   一个指向整型数的指针: int *a;

3   一个指向指针的指针,它指向的指针指向一个整型数: int **a;

4   一个有10个整型数的数组:int a[10];

5   一个有10个指针的数组,每个指针指向一个整形数:int *a[10];

6  一个指向有10个整型数的数组的指针:int (*a)[10];

7  一个指向指针的指针,被指向的指针指向一个有10个整型数的数组:  int (**a)[10];

8  一个指向数组的指针,该数组有10个整型指针:int *(*a)[10];

9  一个指向函数的指针,该函数有一个整数参数并返回一个整型数:int (*a)(int);

10 一个有10个指针的数组,每个指针指向一个函数,该函数有一个整型参数并返回一个整型数:

int (*a[10])(int);

11 一个函数的指针,指向的函数的类型是有两个整型参数并且返回一个函数指针的函数,返回的函数指针指向有一个整型参数且返回整型数的函数:     int (*(*a)(int, int))(int);

初识字符串

     使用指针指向字符串常量,使用数组存放的是字符串常量的副本;因此不可以使用指针去修改字符串常量的内容,而字符串数组可以修改。

#include<stdio.h>

int main(void)
{
    char str[] = "hello";   //内容可以进行修改
    char *p = "hello";  //内容不可以进行修改

    printf("%s\n",str);
    printf("%s\n",p);
   
   // *p = 'a';// 执行者这一步会发生段错误


    return 0;
}

字符串的存储方式     

 字符串会在结束的末尾存放一个空字符,作为字符串结束的标志。

#include<stdio.h>

int main(void)
{
    int data[5] = {1,2,3,4,5};
    char data1[5] = {'h','e','l','l','o'};//字符数组
    char data2[] = "hello";//字符串;并且长度比字符数组大1,因此存放了空字符'\0'
  
    printf("data = %ld\n",sizeof(data)/sizeof(data[0]));
    printf("data1 = %ld\n",sizeof(data1)/sizeof(data1[0]));
    printf("data2 = %ld\n",sizeof(data2)/sizeof(data2[0]));





    return 0;
}

sizeof和strlen的区别

    sizeof是c语言的一种运算符,以字节的形式给出操作数的存储空间大小。strlen是一个函数,由c语言标准函数库提供,用来计算字符串的长度。

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

int main(void)
{
    char *p = "hello";
    char arr[100] = "hello";
    printf("sizeof p:     %ld\n",sizeof(p));   //计算指针所占存储空间大小  8
    printf("strlen p:     %ld\n",strlen(p));   //计算指针p所指向的字符串的字符个数 5

    printf("sizeof arr:   %ld\n",sizeof(arr)); //计算数组所占存储空间大小 100
    printf("strlen arr:   %ld\n",strlen(p));   //计算字符串的字符个数     5

    return 0;
}

 malloc动态开辟内存空间 

函数原型:

返回值:返回一个指针,该指针指向被分配内存的首地址

 

 使用示例:

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

int main(void)
{
    char *p; // 野指针
    p = (char *)malloc(1); // p指向了具体的内存

    *p = 'c'; //对该指针进行写操作
 
    putchar(*p);
    putchar('\n');




    return 0;
}

使用strcpy( )函数(结尾的空字符也会进行拷贝)进行字符串的拷贝:

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

//char *strcpy(char *dest, const char *src);

int main(void)
{
    char *p; // 野指针
    p = (char *)malloc(12); // p指向了具体的内存


    strcpy(p,"lizhiwen");
  
    printf("%s",p); 

    putchar('\n');

    return 0;
}

函数原型:  void free(void *ptr);

作用:C库函数释放之前调用calloc,malloc或realloc所分配的内存空间。

          防止内存泄漏

          防止悬挂指针(野指针的一种)

  

函数原型:  void *realloc(void *ptr, size_t size);

作用:尝试重新调整之前调用malloc或calloc所分配的ptr所指向的内存块的大小。

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

char *strcpy(char *dest, const char *src);

int main(void)
{
    char *p; // 野指针
    p = (char *)malloc(12); // p指向了具体的内存
    p = (char *)realloc(p,12); //对p指向的内存进行扩容


    strcpy(p,"123456789123456789123123");
  
    printf("%s",p); 

    putchar('\n');

    return 0;
}

字符串常用操作函数

字符串拷贝strcpy( )函数      

strcpy( )函数原型:char *strcpy(char *dest, const char *src);

#include<stdio.h>


char *strcpy1(char *dest, const char *src);//字符串的拷贝
char *strcpy2(char *dest, const char *src);

int main(void)
{
   char arr[20];
   char *p = "hello world!";
   strcpy2(arr, p);

   puts(arr);



   return 0;
}



char *strcpy1(char *dest, const char *src)
{
   char *temp = dest;
   if(dest == NULL || src == NULL)
   {
       return NULL;
   }
 
   while(*src != '\0')
   {
       *dest++ = *src++;
   } 

   *dest = '\0'; 


   return temp;   
}


char *strcpy2(char *dest, const char *src)
{
   int i;

   for(i = 0; src[i] != '\0'; i++)
   {
       dest[i] = src[i];    
 
   } 

   dest[i] = '\0';


}

字符串拷贝strncpy( )函数 

strncpy( )函数原型:char *strncpy(char *dest, const char *src, size_t n);

#include<stdio.h>


char *strncpy1(char *dest, const char *src, int n);//字符串的拷贝
char *strncpy2(char *dest, const char *src, int n);

int main(void)
{
   char arr1[20];
   char *p1 = "hello world!";
   char arr2[20];
   char *p2 = "lizhiwen";
   
   strncpy1(arr1,p1,4); 
   strncpy2(arr2,p2,4);
   puts(arr1);
   puts(arr2); 


   return 0;
}

char *strncpy1(char *dest, const char *src, int n)
{
   int i;
   /*对参数进行判断*/
   if(dest == NULL || src == NULL)
   {
       return NULL; 
   } 

   for(i = 0; src[i] != '\0' && i < n; i++)
   {
      dest[i] = src[i];     
   } 


   if(i < n)
   {
       for(;i < n; i++)
       {
          dest[i] = '\0';
       }
       return dest;
   }

   
   dest[i] = '\0';

   return dest;

}


char *strncpy2(char *dest, const char *src, int n)
{
   char *temp = dest;
   if(dest == NULL || src == NULL)
      return NULL;
   
   while( *src != '\0' && n > 0)
   {
          *dest++ = *src++;
          n--; 
   }


   if(n > 0)
   {
         while(n > 0)
         {
              *dest = '\0';
              n--; 
         }
         return temp;
   }

   *dest = '\0'; 
   
   return temp;

}


断言函数assert( )

     assert的作用是计算表达式expression,如果其值为 0,那么它先向stderr打印一条出错信息,然后通过调用abort来终止程序运行。

#include<stdio.h>
#include<assert.h>

int main(void)
{
   char *p = NULL;
   assert(p != NULL);


   return 0;
}

字符串拼接strcat( )函数

函数原型:char *strcat(char *dest, const char *src);

函数作用:把src所指的字符串(包括"\0")复制到dest所指向的字符串后面(删除*dest原来末尾的"\0")。要保证*dest足够长,以容纳被复制进来的*src。*src中原有的字符不变。返回指向dest的指针。

#include<stdio.h>


char *strcat1(char *dest, const char *src); //字符串拼接函数
char *strcat2(char *dest, const char *src); 

int main(void)
{
   char arr1[100] = "lizhiwen ";
   char *p1 = "is very nice";
   char arr2[100] = "lizhiwen ";
   char *p2 = "is very nice";

   printf("%s\n",arr1);
   strcat1(arr1,p1);
   printf("%s\n",arr1);

   printf("%s\n",arr2);
   strcat2(arr2,p2);
   printf("%s\n",arr2);

   return 0;
}


char *strcat1(char *dest, const char *src)
{
     char *ret = dest;
     if(dest == NULL || src == NULL)
       return NULL;
     
     while(*dest != '\0')
           dest++;  

     while(*src != '\0')
           *dest++ = *src++;
     
     *dest = '\0';

     return ret;
}

char *strcat2(char *dest, const char *src)
{
     int i;
     int j;
     for(i = 0; dest[i] != '\0'; i++);
     
     for(j = 0; src[j] != '\0'; i++,j++)
     {
             dest[i] = src[j];
     }   

     dest[i] = '\0';
     

     return dest;
}


字符串拼接strncat( )函数

函数原型:char *strncat(char *dest, const char *src,int n);

#include<stdio.h>


char *strncat1(char *dest, const char *src, int n); //字符串拼接函数
char *strncat2(char *dest, const char *src, int n); 

int main(void)
{
   char arr1[100] = "lizhiwen ";
   char *p1 = "is very nice";
   char arr2[100] = "lizhiwen ";
   char *p2 = "is very nice";

   printf("%s\n",arr1);
   strncat1(arr1,p1,3);
   printf("%s\n",arr1);

   printf("%s\n",arr2);
   strncat2(arr2,p2,4);
   printf("%s\n",arr2);

   return 0;
}


char *strncat1(char *dest, const char *src, int n)
{
     char *ret = dest;
     if(dest == NULL || src == NULL)
       return NULL;
     
     while(*dest != '\0')
           dest++;  

     while(*src != '\0' && n > 0)
     {
           *dest++ = *src++;
           n--;
     }
     
     *dest = '\0';
 
     return ret;

}

char *strncat2(char *dest, const char *src, int n)
{
     int i;
     int j;
     for(i = 0; dest[i] != '\0'; i++);
     
     for(j = 0; src[j] != '\0' && j < n; i++,j++)
     {
             dest[i] = src[j];
     }   

     dest[i] = '\0';
     
     return dest;

}


字符串比较strcmp( )函数

函数原型:int strcmp(const char *s1, const char *s2);

函数作用:若s1 = s2,则返回零;若 s1 < s2, 则返回负数;若 s1 > s2, 则返回正数

#include<stdio.h>

int strcmp1(const char *s1, const char *s2);  //字符串比较函数

int main(void)
{

    char *p1 = "12345";
    char *p2 = "1234";    

    printf("%d\n",strcmp1(p1,p2));


    return 0;
}


int strcmp1(const char *s1, const char *s2)
{
   

    while(*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
    {
        s1++;
        s2++;
    }

    if(*s1 < *s2)
      return -1;
    if(*s1 > *s2)
      return 1;


    return 0;
}


字符串比较strncmp( )函数

函数原型:int strcmp(const char *s1, const char *s2,int n);

函数作用:把s1和s2进行比较,最多比较前n个字节,若s1与s2前n个字符相同,则返回0;若s1大于s2,则返回大于0的值;若s1小于s2,则返回小于0的值。

#include<stdio.h>

int strncmp1(const char *s1, const char *s2, int n);  //字符串比较函数

int strncmp2 ( char * s1, char * s2, size_t n)
{

  //在接下来的while函数中

  //第一个循环条件:--n,如果比较到前n个字符则退出循环

  //第二个循环条件:*s1,如果s1指向的字符串末尾退出循环

  //第二个循环条件:*s1 == *s2,如果两字符比较不等则退出循环

  while (--n && *s1 && *s1 == *s2)
  {
  s1++;//S1指针自加1,指向下一个字符
  s2++;//S2指针自加1,指向下一个字符

  }

  return( *s1 - *s2 );//返回比较结

}

int main(void)
{

    char *p1 = "1134";
    char *p2 = "12345";    

    printf("%d\n",strncmp1(p1,p2,2));

    printf("%d\n",strncmp2(p1,p2,2));

    return 0;
}


int strncmp1(const char *s1, const char *s2, int n)
{

    while(n > 1 && *s1 != '\0' && *s2 != '\0' && *s1 == *s2 )
    {
        s1++;
        s2++;
        n--;
    }

    
    if( *s1 != *s2)
      return (*s1 < *s2)?-1:1;


    return 0;
}


字符填充memset( )函数

函数原型:void *memset(void *s, int c, size_t n);

作用:存放n个字符'c'到指针s所指向的内存

返回值:返回指针s

#include<stdio.h>

void *memset1(void *s, int c, int n);

int main(void)
{
    char arr[10];
    memset1(arr,'\0',10);
    memset1(arr,'a',5);
    
    puts(arr);



    return 0;
}


void *memset1(void *s, int c, int n)
{
     const unsigned char ch = c; // //unsigned char占1字节,意味着只截取c的后八位
     unsigned char *p = s;
     while(n--)
     {
         *p++ = c;
     }
 
     return s;
}

结构体引入

      整形数,浮点型数,字符串是分散得数据表示;有时候我们需要用很多类型的数据来表示一个整体,比如学生信息;

      类比与数组:数组是元素类型一样的数据集合;如果是元素类型不同的数据集合,就要用到结构体了

        它算是一个模板,一般不赋予具体的值,每一项在实际应用中并不是都要使用。

定义结构体和使用变量

        使用点运算符访问结构体成员变量

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

struct Student
{
    int num;     
    char name[32];
    char sex;
    int age;
    double score;
    char addr[32];
   
};

int main(void)
{
 
    struct Student stu1; 
    struct Student stu2;

    stu1.num = 1; // 通过点运算符来访问结构体中的成员变量
    stu1.age = 10;
    stu1.score = 98.5;
    strcpy(stu1.name,"lizhiwen");
    strcpy(stu1.addr,"jiangxi");
 

    printf("num = %d\n",stu1.num);
    printf("age = %d\n",stu1.age);
    printf("score = %4.1f\n",stu1.score); 
    printf("name = %s\n",stu1.name);
    printf("addr = %s\n",stu1.addr); 


    return 0;
}

结构体的应用

练习:比较两个学生的成绩,输出成绩高的学生的信息。

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

struct Student
{
    int num;     
    char name[32];
    char sex;
    int age;
    double score;
    char addr[32];
   
};

int main(void)
{
 
    struct Student stu1 = {
                           .num = 1,
			   .name = "张三",
       		           .sex = 'm',
                           .age = 21,
                           .score = 100.5,
                           .addr = "北京"
                          }; 

    struct Student stu2 = {
                           .num = 2,
			   .name = "李四",
       		           .sex = 'm',
                           .age = 25,
                           .score = 99.5,
                           .addr = "上海"
                          }; 
    struct Student max = stu1;

    if(stu1.score < stu2.score)
         max = stu2;


    printf("学号:%d 姓名:%s 性别:%c 年龄:%d 分数:%.2f 地址:%s\n",
          max.num,max.name,max.sex,max.age,max.score,max.addr);



    return 0;
}

结构体和数组的结合     

       结构体数组里面的每一个元素都是一个结构体变量

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

struct Student
{
    int num;     
    char name[32];
    char sex;
    int age;
    double score;
    char addr[32];
   
};

int main(void)
{
        int len; 
	struct Student stu[3] = {

		{1,"张三",'b',21,85.5,"西藏"},
		{2,"李四",'b',24,75.5,"北京"},
		{3,"王五",'b',26,99.5,"上海"}
	};
  
        len = sizeof(stu)/sizeof(stu[0]); 
   
        for(int i = 0; i < len; i++)
              printf("学号:%d 姓名:%s 性别:%d 年龄:%d 分数:%.1f 地址:%s\n",
                      stu[i].num,stu[i].name,stu[i].sex,stu[i].age,stu[i].score,stu[i].addr);      


    return 0;
}

结构体数组应用

  练习:使用结构体创建一个变量,变量的成员有名字和票数;让后进行投票选存主任

/*tickets.c -- 村主任选票程序*/
#include<stdio.h>
#include<string.h>


struct Leader
{
   char name[10];
   int tickets;


};


int main(void)
{
   int i,j; 
   struct Leader arr[3];
   char temp_name[10];
   int len = sizeof(arr)/sizeof(arr[0]);
   int no_this_name_mark;
   struct Leader max;

   //初始化人员信息
   for(i = 0; i < len; i++)
   {
      printf("请输入选举的人员名字:\n");
      scanf("%s",arr[i].name);
      arr[i].tickets = 0;
   } 

   //投票环节
   for(i = 0; i < 5; i++)
   {
      no_this_name_mark = 1;
      memset(temp_name,'\0',10);  //清空上一轮的输入      

      printf("请输入你想投票的人的姓名:\n");
      scanf("%s",temp_name); 


      for(j = 0; j < len; j++)
      {
 
             if( strcmp(arr[j].name,temp_name) == 0)
               {
                 arr[j].tickets++; 
                 no_this_name_mark = 0;
               }
      }

      if(no_this_name_mark == 1)
        printf("姓名为%s的人不存在!\n",temp_name);


   }

   //唱票环节
   for(i = 0; i < len; i++)
      printf("姓名:%s 票数:%d\n",arr[i].name,arr[i].tickets);


   //找出最高票的人
   max = arr[0];
   for(i = 0; i < len; i++)
   {
      if(max.tickets < arr[i].tickets)
            max = arr[i];
   }
   //选出票数最高人
   
   printf("%s以票数%d当选村主任!\n",max.name,max.tickets);


   return 0;
}

结构体指针变量引入

       结构体指针变量存放的是结构体变量的地址;

结构体指针来访问结构体变量

       结构体变量通过点运算符来访问结构体变量成员;

       结构体指针通过 -> 运算符来访问结构体变量成员;

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

struct Test 
{
    int  idata;
    char cdata;  
 
};

int main(void)
{
 
	struct Test t1 = {
                
                  .idata = 3,
  		  .cdata = 'a'
	};   
       struct Test *p = &t1;

       /*点运算符访问结构体成员变量*/ 
       printf("idata = %d cdata = %c\n",t1.idata,t1.cdata); 

       /*结构体指针通过->运算符来访问结构体变量*/ 
       printf("idata = %d cdata = %c\n",p->idata,p->cdata);





    return 0;
}

结构体指针的应用

            将结构体数组名赋给结构体指针变量,然后通过该结构体指针变量访问数组的每一个元素。

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

struct Student
{
    int num;     
    char name[32];
    char sex;
    int age;
    double score;
    char addr[32];
   
};

int main(void)
{
        int len; 
	struct Student stu[3] = {

		{1,"张三",'b',21,85.5,"西藏"},
		{2,"李四",'b',24,75.5,"北京"},
		{3,"王五",'b',26,99.5,"上海"}
	};
        struct Student *p = stu;

  
        len = sizeof(stu)/sizeof(stu[0]); 
   
        for(int i = 0; i < len; i++,p++)
              printf("学号:%d 姓名:%s 性别:%d 年龄:%d 分数:%.1f 地址:%s\n",
                      stu->num,stu->name,stu->sex,stu->age,stu->score,stu->addr);      


    return 0;
}

练习:使用结构体创建一个变量,变量的成员有名字和票数;申请有3个元素的结构体数组,定义一个结构体指针指向该数组,通过该指针让后进行投票选存主任;

/*tickets.c -- 村主任选票程序*/
#include<stdio.h>
#include<string.h>


struct Leader
{
   char name[10];
   int tickets;


};


int main(void)
{
   int i,j; 
   struct Leader arr[3];
   char temp_name[10];
   int len = sizeof(arr)/sizeof(arr[0]);
   int no_this_name_mark;
   struct Leader max;

   struct Leader *p = arr;



   //初始化人员信息
   for(i = 0; i < len; i++,p++)
   {
      printf("请输入选举的人员名字:\n");
      scanf("%s",p->name);
      p->tickets = 0;
   } 
   p = arr;



   //投票环节
   for(i = 0; i < 5; i++)
   {
      no_this_name_mark = 1;
      memset(temp_name,'\0',10);  //清空上一轮的输入      

      printf("请输入你想投票的人的姓名:\n");
      scanf("%s",temp_name); 

      p = arr;
      for(j = 0; j < len; j++,p++)
      {
 
             if( strcmp(p->name,temp_name) == 0)
               {
                 p->tickets++; 
                 no_this_name_mark = 0;
               }
      }

      if(no_this_name_mark == 1)
        printf("姓名为%s的人不存在!\n",temp_name);


   }


   p = arr;
   //唱票环节
   for(i = 0; i < len; i++,p++)
      printf("姓名:%s 票数:%d\n",p->name,p->tickets);


   p = arr;
   //找出最高票的人
   max = arr[0];
   for(i = 0; i < len; i++,p++)
   {
      if(max.tickets < p->tickets)
            max = *p;
   }
   //选出票数最高人
   
   printf("%s以票数%d当选村主任!\n",max.name,max.tickets);


   return 0;
}

结构体指针数组以及函数综合应用

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

struct Leader
{
   char name[10];
   int tickets;

};

struct Leader *initVote(struct Leader *p, int *n); 
void printResult(const struct Leader *p, int n);
void vote(struct Leader *p, int n);
void getMax(const struct Leader *p, int n);


int main(void)
{

   struct Leader *p1 = NULL;
   int num;
   
   p1 = initVote(p1,&num); 
   vote(p1,num); 
   printResult(p1,num); 
   getMax(p1,num); 


   return 0;
}


/*选民初始化*/
struct Leader *initVote(struct Leader *p, int *n)
{
   int i;
   printf("请输入选民的人数:\n");
   scanf("%d",n);
   p = (struct Leader*)malloc( (*n)*sizeof(struct Leader));   

   for(i = 0;i < *n; i++,p++)
   {
        printf("请输入名字:\n");
        scanf("%s",p->name);
        p->tickets = 0;
   }

   return (p-*n); 

}


/*选民结果打印*/
void printResult(const struct Leader *p, int n)
{
   int i;
   for(i = 0; i < n; i++,p++)
   {
      printf("姓名:%s 票数:%d\n",p->name,p->tickets);

   } 


}


/*选举阶段*/
void vote(struct Leader *p, int n)
{
   int round = 5;
   char temp_name[10];
   int i,j;
   bool no_this_name;

   for(i = 0; i < round; i++)
   {
       
       printf("请输入选举人名字:\n");
       memset(temp_name,'\0',sizeof(temp_name));
       scanf("%s",temp_name);
       no_this_name = true; 
       for(j = 0; j < n; j++,p++)
       {
               if( strcmp(temp_name,p->name) == 0)
               {
                   p->tickets++;
                   no_this_name = false;
               }

       }
       p = p - n;//指针偏移到起始位置
       if(no_this_name)
          printf("不存在%s选举人\n",temp_name);

   }


}


void getMax(const struct Leader *p, int n)
{
     const struct Leader *max = p;
     int i;

     for(i = 0; i < n; i++,p++)
     {
         if(max->tickets < p->tickets)
               max = p; 
     }     

    printf("%s以最高票:%d 当选村主任!\n",max->name,max->tickets);
}
 


结构体二级指针     

          使用结构体二级指针进行初始化

//使用结构体二级指针
void init(struct Leader **p, int *n)
{
   int i;
   printf("请输入选民的人数:\n");
   scanf("%d",n);

   *p = (struct Leader*)malloc( (*n)*sizeof(struct Leader));

   for(i = 0;i < *n; i++,(*p)++)
   {
        printf("请输入名字:\n");
        scanf("%s",(*p)->name);
        (*p)->tickets = 0;
   }

   *p = *p - *n;//将指针偏移最开始的位置
  
}

联合体共用体概念引入   

       有时候同一个内存空间存放类型不同,不同类型的变量共享一块内存空间;共用体元素共享空间,空间大小由最大类型确定,共用体赋值会导致覆盖。   

#include<stdio.h>

union Test1
{
    int a;
    char b;
    double c;


};


int main(void)
{
   union Test1 t1;
   printf("union = %ld\n",sizeof(union Test1));
   printf("double = %ld\n",sizeof(double));

   printf("a:%p\n",&t1.a);
   printf("b:%p\n",&t1.b);
   printf("c:%p\n",&t1.c);



 
   return 0;
}

共用体数据覆盖问题

      共用体成员共用一块内存空间,使用相同的起始地址。

#include<stdio.h>

union Test1
{
    int a;
    char b;
    int c;


};


int main(void)
{
   union Test1 t1;

   t1.a = 10;
   t1.c = 20; 
   t1.b = 'a';


   printf("union = %ld\n",sizeof(union Test1));

   printf("a:%p\n",&t1.a);
   printf("b:%p\n",&t1.b);
   printf("c:%p\n",&t1.c);

   printf("a:%d\n",t1.a); // 最后打印的是a的ASCII值97


 
   return 0;
}

共用体的应用

练习:有若干个人员的数据,其中有学生和教师,学生的数据中包括:姓名,职业,班级;教师的数据包括:姓名,,职业,职务。要求用同一个表格来处理。

 

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

struct Person 
{
    char name[32];
    char career;
    union{
            int class;
            char subject[12];
         }mesg;  


 
};

int main(void)
{
    int i;
    struct Person p[2];

    for(i = 0; i < 2; i++)
    {
         printf("请输入职业:(t代表老师 s代表学生):\n");
         scanf("%c",&(p[i].career));
  
         if(p[i].career == 't')
         {
             printf("请输入老师的科目:\n");
             scanf("%s",p[i].mesg.subject);
             printf("请输入老师的名字:\n");
             scanf("%s",p[i].name);

         }        
         else
         {
             printf("请输入学生的班级:\n");
             scanf("%d",&(p[i].mesg.class));
             printf("请输入学生的名字:\n");
    	     scanf("%s",p[i].name);
         }
  
         while(getchar() != '\n')
             continue;

    } 



   for(i = 0; i < 2; i++)
    {

         if(p[i].career == 't')
         {
             printf("老师名字是:%s   科目是:%s\n",p[i].name,p[i].mesg.subject);
               
         }   
         else
         {
            printf("学生的名字是:%s  班级是:%d班\n",p[i].name,p[i].mesg.class);
         }
         

    }




    return 0;
}

枚举类型介绍   

什么是枚举类型?

    如果一个变量只有几种可能的值,比如星期几 son mon tus wd thu fri sat

怎么定义枚举类型? 

    C编译器把它当作常量处理,也称枚举常量

    列表中的名字可以自己定义,无需像变量一样去申请

枚举变量特性

      1.只限列表中所列的几种情况

      2.值默认从0开始

      3.可以指定列表中枚举数的值

      4.可以直接忽略枚举类型名,直接定义枚举变量

#include<stdio.h>

enum weekdays{moday,tuesday,wednesday = 4,thurday,friday,saturday,sunday};

int main(void)
{
   enum weekdays w;
   w = wednesday;
   printf("w = %d\n",w);


   return 0;
}

typedef关键字

      typedef关键字给已有的变量类型起名字;一般配合结构体使用,不用每次都要使用struct开头。

#include<stdio.h>

typedef struct
{
    int num;
    char name[32];  
    char sex;

}Person;


int main(void)
{
	Person arr[2] = {
		{
		       .num=1,
		       .name="张三",
		       .sex='b'
		},
		{
		       .num=2,
		       .name="莉莉",
		       .sex='g'
		} 
	}; 



   printf("学号:%d 姓名:%s 性别:%c\n",arr[0].num,arr[0].name,arr[0].sex);
   printf("学号:%d 姓名:%s 性别:%c\n",arr[1].num,arr[1].name,arr[1].sex);
  

   return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值