IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 超详解,让你C语言成功入门(四)——数组 -> 正文阅读

[C++知识库]超详解,让你C语言成功入门(四)——数组

数组

数组是一组具有相同类型元素组成的集合。

1.一维数组

1.1一维数组的创建和初始化

数组的创建方式:

type_t   arr_name   [const_n];
//type_t 是指数组的元素类型
//const_n 是一个常量表达式,用来指定数组的大小
// [] - 下标操作符, 用于访问数组的操作符。

例:
int arr[10]
char ch[12]
float date[30]

注: 在C99标准之前,数组的大小必须是常量或者常量表达式;在C99标准之后,数组大小可以是变量,是为了支持变长数组。( char ch[n] 这种数组是不能初始化的)

1.2数组的初始化

数组的初始化,指在创建数组时给数组的内容一些合理的值。
例:

int arr[10] = {1, 2, 3};//不完全初始化,剩余元素默认初始化为 0
int arr2[] = {1, 2, 3, 4, 5, 6};
char ch[4] = {'a', 'b', 'c', 'd'};
char ch2[] = {'a', 99, 'd'};
char ch3[] = "abcdef";

数组在创建的时候如果想不指明数组的确定大小就得初始化。数组的元素个数根据初始化的内容来确定。

但是对于下面的代码要区分,内存中如何分配。

char ch1[] = "abc";
// a b c \0
char ch2[] = {'a','b','c'};
// a b c

在这里插入图片描述

int arr[10] = {1,2,3,4,5,6,7,8,9,10};

在内存中栈区找了一块连续的空间来存放1,2,3,4,5,6,7,8,9,10。
我们给这块空间取名为arr。
就像在酒店排了十个连续的房间,而每个房间都有一个编号,在C语言中,这个编号从0开始,这里的编号就是数组的下标。
在这里插入图片描述
通过数组的下标访问数组的元素,每个下标对应一个元素。

1.3一维数组的使用

#include <stdio.h>
int main()
{
    int arr[10] = {0};//数组的不完全初始化
    //计算数组的元素个数
    int sz = sizeof(arr)/sizeof(arr[0]);
    //对数组内容赋值,数组是使用下标来访问的,下标从0开始。所以:
    int i = 0;//做下标
    for(i=0; i<10; i++)
    {
         arr[i] = i;
    } 
    //输出数组的内容
    for(i=0; i<10; ++i)
    {
         printf("%d ", arr[i]);
    }
    return 0; 
 }

sizeof(arr),计算的是整个数组的大小;sizeof(arr[0]),计算的是一个元素所占字节数。

例1.3.1:用数组实现Fibonacci数列问题。

#include<stdio.h>
int main()
{
    int i = 0;
    int f[20] = { 0 };
    //对前两个元素赋值为1
    f[0] = f[1] = 1;
    //依次求出f[2]~f[19]的值
    for (i = 2; i < 20; i++) {
        f[i] = f[i - 2] + f[i - 1];
    }
    for (i = 0; i < 20; i++) {
        printf("%d ", f[i]);
    }
    printf("\n");
    return 0;
}

在这里插入图片描述

此题还可拓展到求第n个元素的值。

#include<stdio.h>
int main()
{
    int n = 0;
    int i = 0;
    scanf("%d", &n);
    int f[n] = { 0 };
    f[0] = f[1] = 1;
    for (i = 2; i < n; i++) {
        f[i] = f[i - 2] + f[i - 1];
    }
    for (i = 0; i < n; i++) {
        printf("%d ", f[i]);
    }
    printf("\n");
    return 0;
}

例1.3.2:对数组元素进行排序。

#include<stdio.h>
int main(){
    int arr[10] = {0};
    int i = 0, j = 0;
    printf("输入:");
    //对数组元素进行赋值
    for(i = 0; i < 10; i++){
        scanf("%d",&arr[i]);
    }
    for(j = 0; j < 9; j++){
        for(i = 0; i < 9 - j; i++){
            //比较大小
            if(arr[i] > arr[i+1]){
                int tmp = arr[i];
                arr[i] = arr[i+1];
                arr[i+1] = tmp;
            }
        }
    }
    printf("输出:");
    for(i = 0; i < 10; i++){
        printf("%d ",arr[i]);
    }
    printf("\n");
    return 0;
}

在这里插入图片描述

1.4 数组名

数组名确实能表示首元素的地址
但是,有两个例外:

  • sizeof(数组名),这里的数组名表示的是整个数组,计算的是整个数组的大小,单位是字节
  • &数组名,这里的数组名表示整个数组,取出的是整个数组的地址

1.5一位数组在内存中的存储

#include<stdio.h>
int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int i = 0;
    int sz = sizeof(arr) / sizeof(arr[0]);
    for (i = 0; i < 10; i++)
    {
        //打印数组元素的地址
        printf("&arr[%d] = %p\n", i, &arr[i]);
    }
    return 0;
}

在这里插入图片描述

地址是用十六进制表示的,从结果中看出,每个整型数组元素占了四个字节。且随着数组下标的增长,元素的地址,也在有规律的递增。
由此可以得出:数组在内存中是连续存放的。
在这里插入图片描述

2.二维数组

二维数组通常叫矩阵。把二维数组写成行(row)和列(column)的排列形式。

2.1二维数组的创建

把多组数据都存储起来,用二维数组。

//1 2 3 4
//2 3 4 5
//3 4 5 6

int arr[3][4];//三行四列的数组
char arr[2][5];
float arr[4][3];
double arr[3][3];

在这里插入图片描述

2.2二维数组的初始化

int arr1[3][4] = {1,2,3,4,2,3,4,5,3,4,5,6};
int arr2[3][4] = {1,2,3,4,5};
int arr3[3][4] = {{1,2},{3,4},{5}};//1,2放第一行,3,4放第二行,5放第三行 
int arr4[][4] = {{1,2},{3}};//二维数组如果有初始化,行可以省略,列不能省略

在这里插入图片描述
当数据不够时,自动补零。

2.3二维数组的使用

二维数组的使用也是通过下标的方式。

#include <stdio.h>
int main()
{
    int arr[3][4] = {0};
    int i = 0;
    for(i=0; i<3; i++)
    {
        int j = 0;
        for(j=0; j<4; j++)
        {
            scanf("%d",&arr[i][j]);
        }
    }
    for(i=0; i<3; i++)
    {
        int j = 0;
        for(j=0; j<4; j++)
        {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
    return 0;
}

可以将二维数组看做是一维数组,即可理解为一维数组的数组
在这里插入图片描述

2.4二维数组在内存中的存储

二维数组在内存中也是连续存储的。

在这里插入图片描述

#include <stdio.h>
int main()
{
    int arr[3][4];
    int i = 0;
    for(i=0; i<3; i++)
    {
        int j = 0;
        for(j=0; j<4; j++)
        {
            printf("&arr[%d][%d] = %p\n", i, j,&arr[i][j]);
        }
    }
    return 0;
}

在这里插入图片描述

2.5二维数组的数组名的理解

int main()
{
    int arr[3][4];
    printf("%p\n",arr);//二维数组的数组名也表示数组首元素的地址
    return 0;
}

在这里插入图片描述
求数组一行或一列的大小

printf("%d\n",sizeof(arr)/sizeof(arr[0]));//一行
printf("%d\n",sizeof(arr[0])/sizeof(arr[0][0]));//一列

3.数组越界

数组的下标是有范围限制的。
数组的下标规定从0开始,如果有n个元素,那数组的最后一个元素的下标就是n-1。
故,数组的下标小于零,或大于n-1,就是数组越界访问,超出了数组合法空间的访问。
C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的,所以程序员写代码时,最好自己做越界的检查

#include<stdio.h>
int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int i = 0;
    for (i = 0; i <= 13; i++)
    {
        printf("%d\n", arr[i]);
    }
    return 0;
}

在这里插入图片描述

二维数组的行和列也可能存在越界。

4.字符数组和字符串

4.1字符数组的创建

第一个入门篇中介绍了,字符型数据是以ASCII码存储在内存单元中的,一个数组元素占一个字节单位。
一维字符数组:char ch[10];
二维字符数组:char ch[5][5];

4.2字符数字的初始化及使用

4.2.1一维字符数组

char ch1[10] = { 'H','e','l','l','o','w','o','r','l','d'};
    char ch2[] = { 'H','e','l','l','o','w','o','r','d' };
    char ch3[15] = { 'H','e','l','l','o','w','o','r','l','d' };//未初始化的字符数组元素,自动补‘\0’
    char ch4[] = "Helloworld";

在这里插入图片描述

4.2.2二维字符数组

char arr[5][5] = { {' ',' ','*'},{' ','*','*','*'},{'*','*','*','*','*'},{' ','*','*','*'},{' ',' ','*'} };
#include<stdio.h>
int main()
{
    char arr[5][5] = { {' ',' ','*'},{' ','*','*','*'},{'*','*','*','*','*'},{' ','*','*','*'},{' ',' ','*'} };
    //做出来一个菱形。
    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 5; j++) {
            printf("%c", arr[i][j]);
        }
        printf("\n");
    }
    return 0;
}

在这里插入图片描述

4.3字符串

在C语言中,将字符串作为字符数组来处理。
字符串的有效长度并不是数组的长度。
字符串是以 ’ \0 ’ 作为结束标志的。字符串遇到 ’ \0 ’ 则结束。
C语言中,在字符数组存储字符串常量时会自动补上 ’ \0 ’ 作为结束符。

4.3.1字符串的创建和初始化

char d[] = { 'H','e','l','l','o',' ','W','o','r','l','d','!','\0' };/这样表示字符串过于繁杂。
char arr[] = {"Hello World!"};
char ch[] = "Hello World!";

4.3.2字符串函数

重点掌握!!!:

  • 求字符串长度
    strlen
  • 长度不受限的字符串长度
    strcpy
    strcat
    strcmp
  • 长度受限的字符串长度
    strncpy
    strncat
    strncmp
  • 字符串查找
    strstr
    strtok
  • 错误信息报告
    strerror

字符串函数的模拟实现如有错误,还望指出。仅做为参考实现代码 ,并不唯一。

1.gets()和puts()——输入和输出字符串

puts()将字符串输出到终端。
用puts()函数输出的字符串中可以包含转义字符。
char str[] = {“Hello\nWorld”};

Hello
World

在用 puts 输出时将字符串结束标志 ’ \0 ’ 转换成 ’ \n ’ ,即输出完字符串后换行。

gets()从终端输入字符串到字符数组。
一般利用 gets 函数的目的是向字符数组输入一个字符串,而不太关心起函数值。

注意:
用 puts 和 gets 函数只能输出或输入一个字符串。

在这里插入图片描述

#include <stdio.h>
int main(int argc, const char * argv[]) {
    char a[100];
    gets(a);
    puts(a);
    return 0;
}

2.strlen——求字符串长度

size_t strlen(const char *str);
  • 字符串以\0作为结束标志,求字符串长度时,只求\0之前的所有字符数,不包含’ \0 ‘。
#include<stdio.h>
#include<string.h>//字符串函数的头文件
int main()
{
    char arr[] = "abcdef";
    int len = strlen(arr);
    printf("len = %d\n", len);
    return 0;
}

//len = 6
  • 参数指向的字符串必须要以 ‘\0’ 结束。
#include<stdio.h>
#include<string.h>//字符串函数的头文件
int main()
{
    char arr[] = {'a','b','c','d','e','f'};
    int len = strlen(arr);
    printf("len = %d\n", len);
    return 0;
}

//len = 19   ? ? ?

这里为什么是输出19,而不是6呢?

在内存中,数组是连续存储的,而字符串以\0为结束标志,内存中不知道哪里放置了\0,故数组会越界查找,直到遇上\0才停止。

正确写法:

char arr[] = {'a','b','c','d','e','f','\0'};
  • 注意函数的返回值为size_t,是无符号的整型unsigned int( 易错 )
#include <stdio.h>
#include<string.h>
int main()
{
    const char*str1 = "abcdef";
    const char*str2 = "bbb";
    if(strlen(str2)-strlen(str1)>0)// 3 - 6 = -3
    {
        printf("str2>str1\n");
    } 
    else
    {
       printf("srt1>str2\n");
    }
    return 0;
}

在这里插入图片描述
运行程序时,发生了错误,无法正常运行。

就是因为strlen返回类型是size_t,是一个无符号的,-3被当做是无符号数来处理时,就是一个非常大的正数。

  • strlen函数的模拟实现:
#include<stdio.h>
#include<string.h>
#include<assert.h>//断言头文件
size_t my_strlen(const char* str) {
    assert(*str);//断言,用于判断指针是否为空
    int count = 0;
    while (*str != '\0')
    {
        count++;
        str++;
    }
    return count;
}

int main()
{
    char arr[] = "abcdef";
    size_t len = my_strlen(arr);
    printf("%u\n", len);
    return 0;
}

// 6

长度不受限制的字符串函数:

3.strcpy ——字符串拷贝

char* strcpy(char * destination, const char * source );

将字符串 str2 拷贝到 str1 中去。

#include <stdio.h>
#include<string.h>
int main()
{
	char str1[20] = { 0 };
	char str2[] = "zhangsan";
	strcpy(str1, str2);
	printf("%s\n", str1);
	return 0;
}

// zhangsan

注意:
切不可写成 arr1 = "zhangsan"arr1 数组名是地址,地址是一个常量值,不能被赋值。 而 “zhangsan” 要存放到空间里去。

  • 源字符串必须以 ‘\0’ 结束。
#include <stdio.h>
#include<string.h>
int main()
{
	char str1[20] = { 0 };
	char str2[] = {'b','y','t','e'};
	strcpy(str1, str2);
	printf("%s\n", str1);
	return 0;
}

在这里插入图片描述
由于不知何时会遇见\0,所以数组很有可能会进行越界访问。

  • 会将源字符串中的 ‘\0’ 拷贝到目标空间。
#include<stdio.h>
#include<string.h>
int main()
{
    char name[20] = {0};
    strcpy(name,"zhang\0san");
    printf("%s\n",name);
    return 0;
}

在这里插入图片描述

那是否真的将\0拷贝到目标空间内了呢?
测试一下:

#include<stdio.h>
#include<string.h>
int main()
{
    char name[20] = "xxxxxxx";
    strcpy(name,"zhang\0san");
    printf("%s\n",name);
    return 0;
}

拷贝前:

在这里插入图片描述

拷贝后:

在这里插入图片描述

事实证明,确实将\0拷贝到了目标空间内。

  • 目标空间必须足够大,以确保能存放源字符串。
#include <stdio.h>
#include<string.h>
int main()
{
	char str1[2] = { 0 };
	char str2[] = "abcdef";
	strcpy(str1, str2);
	printf("%s\n", str1);
	return 0;
}

在这里插入图片描述
str1 放不下,但非要放进去,就形成了越界访问。

  • 目标空间必须可变。

错误代码:

char *p = "abcdef";//目标区域不可修改
char arr[] = "byte";
strcpy(p,arr);
  • 学会模拟实现。

版本1:

#include <stdio.h>
#include<string.h>
#include<assert.h>
char* my_strcpy(char* dest, char* src) {
	assert(dest);
	assert(src);
	char* ret = dest;
	while (*src)
	{
		*dest++ = *src++;
	}
	*dest = *src;//拷贝 \0
	return ret;
}
int main()
{
	char str1[20] = { 0 };
	char str2[] = "abcdef";
	my_strcpy(str1, str2);
	printf("%s\n", str1);
	return 0;
}

版本2:

#include <stdio.h>
#include<string.h>
#include<assert.h>
char* my_strcpy(char* dest, char* src) {
	assert(dest && src);
	char* ret = dest;
	while (*dest++ = *src++)
	    ;
	return ret;
}
int main()
{
	char str1[20] = { 0 };
	char str2[] = "abcdef";
	my_strcpy(str1, str2);
	printf("%s\n", str1);
	return 0;
}

4.strcat——字符串追加

char * strcat ( char * destination, const char * source );

例:在字符数组 arr1 后追加 “world” 。

#include <stdio.h>
#include<string.h>
int main()
{
	char arr1[20] = "hello ";
	strcat(arr1, "world");
	printf("%s\n", arr1);
	return 0;
}

//hello world

strcat 与strcpy相似,同样有以下要求:

  • 源字符串必须以 ‘\0’ 结束。
  • 目标空间必须有足够的大,能容纳下源字符串的内容。
  • 目标空间必须可修改。
  • 模拟实现 strcat 函数:
  1. 找到目标字符串的结尾\0
  2. 拷贝 – strcpy
#include <stdio.h>
#include<string.h>
#include<assert.h>
char* my_strcat(char* dest, const char* src) {
	assert(dest && src);
	char* ret = dest;
	//1.找到目标空间的末尾 \0
	while (*dest != '\0')
	{
		dest++;
	}
	//2.拷贝字符串
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}
int main()
{
	char arr1[20] = "hello ";
	my_strcat(arr1, "world");
	printf("%s\n", arr1);
	return 0;
}

思考:字符串自己给自己追加会如何?

程序崩溃,原因是在找尾时,第一次拷贝字符将\0给覆盖了,然后目标空间没有\0无法停下来,会陷入死循环。

5.strcmp——字符串内容比较

int strcmp ( const char * str1, const char * str2 );

错误代码:

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

int main()
{
	char arr1[20] = "helloworld";
	char arr2[] = "helloworld!!!";
	if (arr1 == arr2)
	{
		printf("==\n");
	}
	else
	{
		printf("!=\n");
	}
	return 0;
}

可能编译器出来的结果是 != ,但这个代码依旧是错的,即使 arr1 和 arr2 的内容一模一样。

因为,arr1 和 arr2 是数组名,数组名是首元素的地址,两个数组名的地址肯定不相等啊,地址是一个常量值。

比较两个字符串相等应该使用strcmp函数。

  • 标准规定:
  1. 第一个字符串大于第二个字符串,则返回大于0的数字
#include <stdio.h>
#include<string.h>

int main()
{
	char arr1[20] = "helloworld!!!";
	char arr2[] = "helloworld";
	int ret = strcmp(arr1, arr2);
	printf("%d\n", ret);
	return 0;
}
// 1
  1. 第一个字符串等于第二个字符串,则返回0
#include <stdio.h>
#include<string.h>

int main()
{
	char arr1[20] = "helloworld";
	char arr2[] = "helloworld";
	int ret = strcmp(arr1, arr2);
	printf("%d\n", ret);
	return 0;
}
// 0
  1. 第一个字符串小于第二个字符串,则返回小于0的数字
#include <stdio.h>
#include<string.h>

int main()
{
	char arr1[20] = "helloworld";
	char arr2[] = "helloworld!!!";
	int ret = strcmp(arr1, arr2);
	printf("%d\n", ret);
	return 0;
}
// -1

注意以下代码:

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

int main()
{
	cha
	r arr1[20] = "abcdef";
	char arr2[] = "abx";
	int ret = strcmp(arr1, arr2);
	if(ret < 0)
	{
	    printf("<\n");
	}
	else if(ret == 0)
	{
	    printf("==\n");
	}
	else
	{
	    printf(">\n");
	}
	return 0;
}

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

int main()
{
	cha
	r arr1[20] = "abxdef";
	char arr2[] = "abc";
	int ret = strcmp(arr1, arr2);
	if(ret < 0)
	{
	    printf("<\n");
	}
	else if(ret == 0)
	{
	    printf("==\n");
	}
	else
	{
	    printf(">\n");
	}
	return 0;
}

// >

两种代码产生差异的原因是:strcmp比较的是字符的ASCII码值。

  • 模拟实现 strcmp 函数:
#include <stdio.h>
#include<string.h>
#include<assert.h>

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
			return 0;//相等
		str1++;
		str2++;
	}
	if (*str1 > *str2)
		return 1;
	else
		return -1;
}

int main()
{
	char arr1[20] = "abcdef";
	char arr2[] = "abc";
	int ret = my_strcmp(arr1, arr2);
	if (ret < 0)
	{
		printf("<\n");
	}
	else if (ret == 0)
	{
		printf("==\n");
	}
	else
	{
		printf(">\n");
	}
	return 0;
}

简化版:

#include <stdio.h>
#include<string.h>
#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
			return 0;//相等
		str1++;
		str2++;
	}
	return (*str1 - *str2);
}
int main()
{
	char arr1[20] = "abcdef";
	char arr2[] = "abc";
	int ret = my_strcmp(arr1, arr2);
	if (ret < 0)
	{
		printf("<\n");
	}
	else if (ret == 0)
	{
		printf("==\n");
	}
	else
	{
		printf(">\n");
	}
	return 0;
}

长度受限制的字符串长度:

6.strncpy——拷贝src中num个字符

char * strncpy ( char * destination, const char * source, size_t num );
  • 拷贝num个字符从源字符串到目标空间。
#include <stdio.h>
#include<string.h>

int main()
{
	char arr1[20] = "abcdef";
	char arr2[] = "hello world";
	strncpy(arr1, arr2, 5);
	printf("%s\n", arr1);
	return 0;
}

//hellof

在这里插入图片描述

  • 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。
#include <stdio.h>
#include<string.h>

int main()
{
	char arr1[20] = "abcdef";
	char arr2[] = "xyz";
	strncpy(arr1, arr2, 5);
	printf("%s\n", arr1);
	return 0;
}

//xyz

在这里插入图片描述

  • 模拟实现strncpy函数:
#include <stdio.h>
#include<string.h>
#include<assert.h>
char* my_strncpy(char* dest, const char* src, size_t num)
{
	assert(dest && src);
	char* ret = dest;
	int len = strlen(src);
	if (len >= num)
	{
		int i = 0;
		while (*src != '\0' && i < num)
		{
			*dest++ = *src++;
			i++;
		}
	}
	else
	{
		int j = 0;
		while (*src != '\0')
		{
			*dest++ = *src++;
			j++;
		}
		while (num - j != 0)
		{
			*dest++ = '\0';
			j++;
		}
	}
	
	return ret;
}
int main()
{
	char arr1[20] = "abc";
	char arr2[] = "xyzdef";
	my_strncpy(arr1, arr2, 5);
	printf("%s\n", arr1);
	return 0;
}

7.strncat——追加src中num个字符

char * strncat ( char * destination, const char * source, size_t num );
#include <stdio.h>
#include <string.h>
int main ()
{
 char str1[20] = "hello ";
 char str2[20] = "world";
 strncat (str1, str2, 3);
 puts (str1);
 return 0;
}

//hello wor

在这里插入图片描述
在这里插入图片描述

追加后末尾\0是否补上了呢?

#include <stdio.h>
#include <string.h>
int main()
{
	char str1[20] = "hello \0xxxx";
	char str2[20] = "world";
	strncat(str1, str2, 3);
	puts(str1);
	return 0;
}

在这里插入图片描述

在这里插入图片描述

由此可知,在追加之后,补上了一个\0。

  • 模拟实现 strncat 函数:
#include <stdio.h>
#include <string.h>
#include<assert.h>
char* my_strncat(char* dest, const char* src, size_t num)
{
	assert(dest && src);
	char* ret = dest;
	//1.找到目标空间的末尾 \0
	while (*dest != '\0')
	{
		dest++;
	}
	//2.拷贝字符串
	int i = 0;
	while (*src != '\0' && i < num)
	{
		*dest++ = *src++;
		i++;
	}
	*dest = '\0';
	return ret;
}
int main()
{
	char arr1[20] = "hello ";
	char arr2[20] = "world";
	my_strncat(arr1, arr2, 6);
	puts(arr1);
	return 0;
}

//hello world

8.strncmp——比较num个字符内容

int strncmp ( const char * str1, const char * str2, size_t num );

比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。

#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[] = "helloworld";
	char arr2[] = "hellq";
	int ret = strncmp(arr1, arr2, 5);
	//printf("%d\n",ret);  //1
	if (ret == 0)
		printf("==\n");
	else if (ret < 0)
		printf("<\n");
	else
		printf(">\n");
	return 0;
}

// <
  • 模拟实现 strncmp 函数:
#include <stdio.h>
#include<string.h>
#include<assert.h>
int my_strncmp(const char* str1, const char* str2, size_t num)
{
	assert(str1 && str2);
	int ret = 0;
	while (num != 0)
	{
	    ret = *str1 - *str2;
		if (ret != 0)
			break;//相等
		str1++;
		str2++;
		num--;
	}
	return ret;
}
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abc";
	int ret = my_strncmp(arr1, arr2,3);
	if (ret < 0)
	{
		printf("<\n");
	}
	else if (ret == 0)
	{
		printf("==\n");
	}
	else
	{
		printf(">\n");
	}
	return 0;
}

9.strstr——查找子串

char * strstr ( const char *str1, const char * str2);

返回一个指向str1中第一次出现的str2的指针,或者如果str2不是str1的子串,则返回一个空指针。

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = "abcdefghijklmn";
	char arr2[] = "defghi";
	char* ret = strstr(arr1, arr2);
	if (ret == NULL)
	{
		printf("子串不存在\n");
	}
	else
	{
		printf("%s\n", ret);
	}
	return 0;
}

// defghijklmn
  • 模拟实现 strstr 函数:
#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	const char* s1 = str1;
	const char* s2 = str2;
	const char* p = str1;
	while (*p)
	{
		s1 = p;
		s2 = str2;
		while (*s1 != '\0' && *s2 != '\0' && * s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0') 
		{
			return (char*)p;
		}
		p++;
	}
	return NULL;
}
int main()
{
	char arr1[] = "abcdefghijklmn";
	char arr2[] = "defghi";
	char* ret = my_strstr(arr1, arr2);
	if (ret == NULL)
	{
		printf("子串不存在\n");
	}
	else
	{
		printf("%s\n", ret);
	}
	return 0;
}

10.strtok——切割字符串

char * strtok ( char * str, const char * sep );

1234.asdf@csin.com为例:

  • sep参数是个字符串,定义了用作分隔符的字符集合
const char* sep = "@.";
  • 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
const char* sep = "@.";
char arr[] = "1234.asdf@csin.com";
char* ret = strtok(arr, sep);
  • strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)

在这里插入图片描述

const char* sep = "@.";
char arr[] = "1234.asdf@csin.com";
char cp[35] = { 0 };
strcpy(cp, arr);
char* ret = strtok(cp, sep);
  • strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
#include<stdio.h>
#include<string.h>
int main()
{
	const char* sep = "@.";
	char arr[] = "1234.asdf@csin.com";
	char cp[35] = { 0 };
	strcpy(cp, arr);
	char* ret = strtok(cp, sep);
	printf("%s\n", ret);
	return 0;
}

//1234
  • strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。

在这里插入图片描述

#include<stdio.h>
#include<string.h>
int main()
{
	const char* sep = "@.";
	char arr[] = "1234.asdf@csin.com";
	char cp[35] = { 0 }; //"1234.asdf@csin.com"
	strcpy(cp, arr);

	char* ret = strtok(cp, sep);
	printf("%s\n", ret);
	
	ret = strtok(NULL, sep);
	printf("%s\n", ret);
	
	ret = strtok(NULL, sep);
	printf("%s\n", ret);
	
	ret = strtok(NULL, sep);
	printf("%s\n", ret);
	return 0;
}

//1234
//asdf
//csin
//com
  • 如果字符串中不存在更多的标记,则返回 NULL 指针。
#include<stdio.h>
#include<string.h>
int main()
{
	const char* sep = "@.";
	char arr[] = "1234.asdf@csin.com";
	char cp[35] = { 0 }; //"1234.asdf@csin.com"
	strcpy(cp, arr);

	char* ret = strtok(cp, sep);
	printf("%s\n", ret);
	
	ret = strtok(NULL, sep);
	printf("%s\n", ret);
	
	ret = strtok(NULL, sep);
	printf("%s\n", ret);
	
	ret = strtok(NULL, sep);
	printf("%s\n", ret);
	
    ret = strtok(NULL, sep);
	printf("%s\n", ret);
	return 0;
}

//1234
//asdf
//csin
//com
//(null)

以上代码过于繁琐,可以用循环来简化代码:

#include<stdio.h>
#include<string.h>
int main()
{
	const char* sep = "@.";
	char arr[] = "1234.asdf@csin.com";
	char cp[35] = { 0 }; //"1234.asdf@csin.com"
	strcpy(cp, arr);

	char* ret = NULL;
	for (ret = strtok(cp, sep); ret != NULL; ret = strtok(NULL, sep))
	{
		printf("%s\n", ret);
	}
	return 0;
}
  • 模拟实现 strtok 函数:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>

char* my_strtok(char* str, const char* sep)
{
	assert(sep);
	static char* s1 = NULL;
	char* s2 = sep;
	while (s1 != NULL && *s1 != '\0')
	{
		int cnt = 0;
		char* ret = (char*) sep;
		while (*ret != '\0')
		{
			if (*s1 == *ret)
			{
				cnt = 1;
				s1++;
			}
			ret++;
		}
		if (cnt == 0)
			break;
	}
	if (str == NULL)
	{
		if (s1 == NULL)
		{
			return NULL;
		}
		str = s1;
	}
	char* ans = str;
	while (*ans != '\0')
	{
		char* res = (char*)sep;
		while (*res != '\0')
		{
			if (*ans == *res)
			{
				*ans = '\0';
				s1 = ans + 1;
				return str;
			}
			res++;
		}
		ans++;
	}
	static int num = 0;
	num++;
	if (num == 1)
	{
		return str;
	}
	else
	{
		return NULL;
	}
}
int main()
{
	const char* sep = "@.";
	char arr[] = "1234.asdf@csin.com";
	char cp[35] = { 0 }; //"1234.asdf@csin.com"
	strcpy(cp, arr);

	char* ret = NULL;
	for (ret = my_strtok(cp, sep); ret != NULL; ret = my_strtok(NULL, sep))
	{
		printf("%s\n", ret);
	}
	return 0;
}

//1234
//asdf
//csin
//com

11.strerror——返回错误码所对应的错误信息

C语言的库函数,在执行失败的时候,都会设置错误码,如:0 1 2 3 4 5 6 …
要将这些错误码转换为错误信息,就用到了 strerror 函数。

strerror 函数:返回错误码,所对应的错误信息。

char * strerror ( int errnum );
#include<stdio.h>
#include<string.h>
int main()
{
	printf("%s\n", strerror(0));
	printf("%s\n", strerror(1));
	printf("%s\n", strerror(2));
	printf("%s\n", strerror(3));
	return 0;
}

//No error
//Operation not permitted
//No such file or directory
//No such process

一般用于文件操作中:

#include<stdio.h>
#include<string.h>
#include<errno.h>// errno的头文件
int main()
{
	FILE* pf = fopen("test.txt", "r");//读取这个文件
	if (pf == NULL)//文件不存在
	{
	//errno - C语言设置的一个全局的错误码存放的变量
		printf("%s\n", strerror(errno));//输出错误信息
		return 1;
	}
	else
	{
		//....文件存在,执行其他操作
	}
	return 0;
}

//No such file or directory

如有多个错误,且在继续执行程序,那么记录的始终是最新错误,即新错误将前一个错误所覆盖掉。

文件操作,后期会详说。这里可做了解。

字符转换函数:

12.strlwr、tolower——大写字母转小写字母

char *strlwr(char *str);

#include<stdio.h>
#include<string.h>
#include<ctype.h>
int main()
{
	char arr[] = "ASDFGHJKL";
	char* a = strlwr(arr);
	printf("%s\n", a);
	return 0;
}

//asdfghjkl

int tolower ( int c );

#include<stdio.h>
#include<string.h>
int main()
{
	int a = tolower('W');
	printf("%c\n", a);
	return 0;
}

//w

13.strupr、toupper——小写字母转大写字母

char *strupr(char *str);

#include<stdio.h>
#include<string.h>
int main()
{
	char arr[] = "asdfghjkl";
	char* a = strupr(arr);
	printf("%s\n", a);
	return 0;
}

//ASDFGHJKL

int toupper (int c );

#include<stdio.h>
#include<string.h>
int main()
{
	int a = toupper('b');
	printf("%c\n", a);
	return 0;
}

//B

字符分类函数

函数如果他的参数符合下列条件就返回真
iscntrl任何控制字符
isspace空白字符:空格‘ ’,换页‘\f’,换行’\n’,回车‘\r’,制表符’\t’或者垂直制表符’\v’
isdigit十进制数字 0~9
isxdigit十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A ~ F
islower小写字母a~z
isupper大写字母A~Z
isalpha字母a ~ z或A~Z
isalnum字母或者数字,a ~ z,A ~ Z,0~9
ispunct标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph任何图形字符
isprint任何可打印字符,包括图形字符和空白字符
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-10-22 20:56:54  更:2022-10-22 21:00:03 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 5:59:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码