目录
前言
一、函数介绍
1.1 strlen函数
1.2strcpy函数
1.3strcat 函数
1.4 strcmp函数
1.5 memcpy函数
1.6memmove 函数
二、自定义类型--结构体
2.1结构体声明
2.2结构体变量的定义和初始化
2.3结构体传参
三、位段
3.1认识位段
3.2 位段的内存分配
四、枚举
4.2为什么要用枚举?
4.3枚举的使用
总结
前言
上篇我们学习了指针进阶,今天我们来学习字符串、结构体、位段、枚举等。
一、函数介绍
1.1 strlen函数
先看如下实例代码:
int my_strlen(const char* str){assert(str!=NULL);if (*str != '\0'){return 1 + my_strlen(str + 1);}elsereturn 0;}int main(){char a[] = "abcde";int len = my_strlen(a);printf("%d", len);return 0;}
strlen是求字符串长度,统计\0之前出现的字符个数
1.2strcpy函数
char* strcpy (char *destination,const char *source)
strcpy 函数将source值 拷贝给 desination,拷贝的前提是目标空间足够大,以确保能放下。长度不受限制。
1.3strcat 函数
char* strcat (char *destination,const char *source)
strcat函数j将右边的值追加到左边值的尾部,长度不受限制。
1.4 strcmp函数
int strcmp(char* str1,char* str2)
strcmp函数,比较俩字符串大小,长度不受限制。以上四组就是我们常见的、常用的一些字符串函数下面简单介绍些其他的
int strncmp(const char*str1,const char * str2, size_t num);
比较出现另个字符不一样或者一个字符结束或者num个字符全部比较完
同理,有strncmp函数,也有strncat、strncpy。
字符分类函数:
函数 | 如果他的参数符合下列条件就返回真 |
iscntrl | 任何控制字符 |
isspace | 空白字符:空格' ',换页’\f',换行‘\n’,回车‘\r’,制表符'\t'或者垂直制表符'\v' |
isdigit | 十进制数字0~9 |
isxdigit | 十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F |
islower | 判断小写字母 |
isupper | 判断大写字母 |
isaipha | 字母a~z或A~Z |
isalnum | 字母或者数字标点符号a~z,A~Z,0~9 |
ispunct | 标点符号,任何不属于数字或者字母的图形字符(可打印) |
isgraph | 任何图形字符 |
isprint | 任何可打印字符,包括图形字符或空白字符 |
1.5 memcpy函数
void* memcpy(void* destination,const void* source,size_t num)
memcpy函数,从源头指向的内存块拷贝固定字节的数据到目标指向的内存块,模拟代码实现如下:
void* my_memcpy(void* dest, void* src,size_t num){void* ret = dest;assert(dest && src);while (num--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}return ret;}int main(){int arr[] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[10] = { 0 };my_memcpy(arr2, arr+1, 20);for (int i = 0; i < 10; i++){printf("%d\n", arr2[i]);}return 0;}
使用了模拟memcpy函数my_memcpy将arr数组下标为1的前20字节(即前5个整形)拷进arr2数组中
1.6memmove 函数
void* memmove(void* destination,const void* source,size_t num)
memmove函数功能是:从源头指向的内存块拷贝固定字节的数据到目标指向的内存块,并且源头的内存块与目标内存块可以重叠(最后一部分memmove与memcpy最大的区别)
具体实例如下:
#include<stdio.h>#include<string.h>int main(){int arr[] = { 1,2,3,4,5,6,7,8,9,10 };memmove(arr+2, arr, 16);for (int i = 0; i < 10; i++){printf("%d\n", arr[i]);}return 0;}
memmove函数将arr前四个元素1,2,3,4拷贝给arr下标为3的四个数(即3,4,5,6)。
二、自定义类型--结构体
2.1结构体声明
结构是一些值的集合,这些值是成员变量,结构的每个成员可以是不同类型的变量
struct stu {
char name[20];
int age;
}s1,s2; //分号不能少
s1,s2 是两个结构体变量,全局变量,
匿名结构体类型
struct {int a;char b;float c;}x;struct {int a;char b;float c;}a[20],*p;
上面两个结构体声明过程中少了个结构体标签,只有个单一的struct。其实这俩个声明是完全不一样的两个类型。
2.2结构体变量的定义和初始化
有了结构体,定义变量及初始化如下;
struct point { //类型的声明int x;int y;}p1 = { 10,20 };//声明类型的同时定义变量p1及初始化//初始化:定义变量的同时赋值初始化struct point s2 = { 1,0 };
上面这是结构体最简单的初始化,我们再看一看结构体嵌套下的初始化(即就是一个结构体里包含着另一个结构体,示例如下:
struct S {int num;char ch;struct point p;float d;}n1={1,'w',{2,3},3.4f}; //结构体嵌套初始化struct S s = { 100,'ww',{3,4},3.45f }; //结构体嵌套初始化
嵌套结构体使用时要用到{ }符号。
2.3结构体传参
示例代码如下:
struct S {int data[20];int num;};struct S s = { {1,2,3,4},1999 };void print1(struct S s) {printf("%d\n", s.num);}void print2(struct S* ps){printf("%d\n", pe->num);}int main(){printf1(s); //传结构体print2(&s); //传地址}
print1和print2都是传参,哪个更好些?答案是:首选print函数,
函数传参的时候,参数需要压栈,会有时间空间上的系统开销。
如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降。
三、位段
3.1认识位段
先看一段代码:
struct A {int _a : 2; //二进制位int _b : 4;int _c : 10;};
在上述代码中,A表示一个位段类型,_a 表示2个二进制位,尾端跟结构体类似,功能跟结构体一样,但比结构体更节省空间
3.2 位段的内存分配
位段的成员可以是 int unsigned int signed int 或者char(属于整形家族)类型
位段的空间上是按照需要以四个字节(Int)或者1个字节(char)方式开辟的。
位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段
四、枚举
4.1枚举类型的定义,直接上代码:
enum Day {mon,Tues,wed,Thur,Fri,sat,sun,};
枚举其实就是列举,上述代码中,定义的enum Day就是枚举类型,{ }中的内容是枚举类型的可能取值,也叫枚举常量。
这些取值都是有值的,默认从0开始,依次递增1,当然在定义的是时候,也可以赋值初值,如:
enum SEX {MALE = 2,DASE=4,SECRE=5,};
4.2为什么要用枚举?
我们可以用#define 定义常量,为什么非要用枚举?
主要有以下几点:
增加代码的可读性和可维护性
和#define 定义的标识符比较,枚举有类型检查,更加严谨。
防止命名污染(封装)
便于调试
使用方便,一次可以定义多个常量
4.3枚举的使用
直接上代码演示:
enum color { //颜色RED=1,GREEN=2,BLUE=4};enum color clr = GREEN; //只能枚举常量给枚举变量赋值,才不会出现差异
上述代码中最后一行执行了使用枚举类型,
总结
提上就是本片所有内容,希望这篇内容对大家有所帮助。
欢迎大家评论点赞,大家的支持是我前进的动力