目录
指针变量为什么要求类型
间接运算符会 根据指针变量的类型,访问不同大小的空间;
封装一个函数,实现两个变量值的交换
#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;
}