1 指针编程练习:
1.1 字符串左旋
实现一个函数,可以左旋字符串中的n个字符。
例如:
ABCD左旋一个字符得到BCDA
ABCD左旋两个字符得到CDAB
法一(移首补尾法):
#include<stdio.h>
#include<string.h>
#include<assert.h>
void string_left_rotate(char* str, int n)
{
assert(str != NULL);
int sum = strlen(str);
for (int i = 0;i < n;i++)//旋转几个字符就循环几次
{
char tmp = *str;//1.把第一个字符存起来
for (int j = 0;j < sum - 1;j++)
{
*(str + j) = *(str + j + 1);//2.把第一个后面的字符依次往前挪动一位
}
*(str + sum - 1) = tmp;//3.把第一个字符放到最后
}
}
int main()
{
char arr[20] = "ABCDEF";
printf("%s\n", arr);
int n = 0;
scanf("%d", &n);
string_left_rotate(arr, n);
printf("%s\n", arr);
return 0;
}
法二(三步翻转法):
#include<stdio.h>
#include<string.h>
#include<assert.h>
void reverse(char* left, char* right)//字符串逆置(翻转)函数
{
assert(left != NULL);
assert(right != NULL);
while (left < right)
{
char tmp = *left;
*left = *right;
*right = tmp;
left++;
right--;
}
}
void string_left_rotate(char* str, int n)
{
int sum = strlen(str);
assert(str!=NULL);
//如ABCDEF n=2
reverse(str, str+n-1);//1.翻转要左旋的n个字符 BA CDEF
reverse(str+n, str + sum - 1);//2.翻转要剩余的个字符 BA FEDC
reverse(str, str + sum - 1);//3.整体翻转 CDEF AB
}
int main()
{
char arr[20] = "ABCEDF";
printf("%s\n", arr);
int n = 0;
scanf("%d", &n);
string_left_rotate(arr, n);
printf("%s\n", arr);
return 0;
}
1.2 字符串旋转结果
字符串旋转
写一个函数,判断一个字符串是否为另外一个字符串旋转之后的字符串。
例如:给定arr1 = AABCD和arr2 = BCDAA,返回1
给定arr1 = abcd和arr2 = ACBD,返回0.
AABCD左旋一个字符得到ABCDA
AABCD左旋两个字符得到BCDAA
AABCD右旋一个字符得到DAABC
法一(暴力穷举法):
//法一(暴力穷举法):
#include<stdio.h>
#include<string.h>
#include<assert.h>
int is_string_rotate(char* str1, char* str2)
{
//穷举str1所有左旋的结果,然后和str2比较(右旋是左旋的特殊形式)
assert(str1 != NULL);
assert(str2 != NULL);
int sum = strlen(str1);
for (int i = 0;i < sum;i++)//旋转str1所有的结果
{
char tmp = *str1;//1.把第一个字符存起来
for (int j = 0;j < sum - 1;j++)
{
*(str1 + j) = *(str1 + j + 1);//2.把第一个后面的字符依次往前挪动一位
}
*(str1 + sum - 1) = tmp;//3.把第一个字符放到最后
//旋转一个字符比较一次
if (strcmp(str1, str2) == 0)
{
return 1;
}
}
return 0;//所有结果比较完,没有就返回0
}
int main()
{
char arr1[20] = "ABCDEF";
char arr2[20] = "CDEFAB";
if (is_string_rotate(arr1, arr2))//是就返回1
{
printf("yes\n");
}
else
{
printf("no\n");
}
return 0;
}
法二:
法二用到很多字符串函数,笔试时可以更好展现自己,而且效率更高了
这些字符串函数的详细介绍都放在下面文章了
//法二:
#include<stdio.h>
#include<string.h>
#include<assert.h>
int is_string_rotate(char* str1, char* str2)
{
assert(str1 != NULL);
assert(str2 != NULL);
if (strlen(str1) != strlen(str2))
{
return 0; //长度不相等返回0
}
//1.给str1追加一个str1
//如ABCDEF变成ABCDEFABCDEF 这样新字符串的其中6个就包含了原字符串旋转的所有可能
strncat(str1, str1, strlen(str1));
//字符串追加函数,在第一个参数后面追加第三个参数个第二个参数的字符串
//(strcat不能给自己追加,strncat多最后一个参数)
//2.比较str2是不是str1新字符串的子字符串(是的话就是str1原字符串旋转得来)
char* ret = strstr(str1, str2);
//判断第二个参数是否是第一个参数的子字符串
//是的话返回第一个参数中子字符串的首地址,不是的话返回空指针
//注意到如果长度不一样也是子字符串,不合题意,所以一开始判断长度
return ret != NULL; //是空指针返回0,不是返回1
}
int main()
{
char arr1[20] = "ABCDEF";
char arr2[20] = "CDEFAB";
if (is_string_rotate(arr1, arr2))//是就返回1
{
printf("yes\n");
}
else
{
printf("no\n");
}
return 0;
}
2 字符函数和字符串函数
下面讲的函数的头文件: string.h
重点介绍处理字符和字符串的库函数的使用和注意事项
求字符串长度
strlen
长度不受限制的字符串函数介绍
strcpy
strcat
strcmp
长度受限制的字符串函数介绍
strncpy
strncat
strncmp
字符串查找
strstr
strtok
错误信息报告
strerror
字符串操作:C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,
字符串通常放在常量字符串 中或者 字符数组 中。
字符串常量 适用于那些对它不做修改的字符串函数.
2.1 strlen三种实现
文档:size_t strlen ( const char * str );
1.字符串以 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数
(不包含 '\0')。
2.参数指向的字符串必须要以 '\0' 结束。
3.注意函数的返回值为size_t,是无符号的( 易错 )
代码演示:
#include <stdio.h>
#include <string.h>
int main()
{
int len = strlen("abcdef");
printf("%d\n", len);
return 0;
}
要注意的地方:
#include<stdio.h>
#include<string.h>
int main()
{
const char* str1 = "abcd";
const char* str2 = "abcdef";
if (strlen(str1) - strlen(str2) > 0)
{
printf("str1>str2\n");
}
else
{
printf("srt1<=str2\n");
}
return 0;
}
//输出str1>str2 因为无符号减无符号还是无符号 大于0
//用自己模拟的strlen就不会出现这种问题
strlen函数的模拟实现:(三种方法)
#include<stdio.h>
#include<string.h>
#include<assert.h>
int my_strlen1(const char* str)//计数器版本
{
assert(str != NULL);
int count = 0;
while (*str != '\0')
{
count++;
str++;
}
return count;
}
int my_strlen2(const char* str)//递归版本
{
assert(str != NULL);
if (*str != '\0')
return 1 + my_strlen2(str + 1);
else
return 0;
}
//左下右上转圈地看
//my(hello)
//my(hello) 1+my(ello) =5
//my(ello) 1+my(llo) =4
//my(llo) 1+my(lo) =3
//my(lo) 1+my(o) =2
//my(o) return 1+my(\0) =1
//my(\0):return 0;
int my_strlen3(const char* str)//指针减指针版本
{
assert(str != NULL);
char* ret = str;
while (*ret != '\0')
{
ret++;
}
return ret - str;
}
int main()
{
char arr[] = "hello";
printf("%d\n", strlen(arr));
printf("%d\n", my_strlen1(arr));
printf("%d\n", my_strlen2(arr));
printf("%d\n", my_strlen3(arr));
return 0;
}
2.2 strcpy模拟实现
文档:char* strcpy(char * destination, const char * source );
Copies the C string pointed by source into the array pointed by destination, including the
terminating null character (and stopping at that point).
源字符串必须以 '\0' 结束。
会将源字符串中的 '\0' 拷贝到目标空间。
目标空间必须足够大,以确保能存放源字符串。
目标空间必须可变。
代码演示:
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "abcdefghi";
char arr2[] = "123";
printf("拷贝前:%s\n", arr1);//拷贝前:abcdefghi
strcpy(arr1, arr2); // 字符串拷贝(目标空间,源字符串)
printf("拷贝后:%s\n", arr1);//拷贝后:123
return 0;
}
strcpy函数的模拟实现
#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{
assert(dest != NULL);
assert(src != NULL);
char* ret = dest;//让ret指向dest的起始地址
while (*dest++ = *src++)//'\0'的ASCII码为0拷贝后跳出循环
{
;
}
return ret;
}
int main()
{
char arr1[50] = "#####################";
char arr2[] = "hello world";
//printf("%s\n", strcpy(arr1, arr2));
printf("%s\n", my_strcpy(arr1, arr2));
return 0;
}
2.3 strcat模拟实现
文档:char * strcat ( char * destination, const char * source );
Appends a copy of the source string to the destination string. The terminating null character in destination is overwritten by the first character of source, and a null-character is included at the end of the new string formed by the concatenation of both in destination.
源字符串必须以 '\0' 结束。
目标空间必须有足够的大,能容纳下源字符串的内容。
目标空间必须可修改。
字符串自己给自己追加,要用到strncat,用strcat的话 \0被覆盖了
代码演示:
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[30] = "hello";
char arr2[] = "world";
strcat(arr1, arr2);
printf("%s\n", arr1);//helloworld
return 0;
}
strcat函数模拟实现:
#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strcat(char* dest, const char* src)
{
assert(dest != NULL);
assert(src != NULL);
char* ret = dest;//让ret指向dest的起始地址
while (*dest)//找到'\0' '\0'的ASCII码为0跳出循环
{
dest++;
}
while (*dest++ = *src++)//'\0'的ASCII码为0拷贝后跳出循环
{
;
}
return ret;
}
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
//printf("%s\n", strcat(arr1, arr2));
printf("%s\n", my_strcat(arr1, arr2));
return 0;
}
2.4strcmp模拟实现
文档:int strcmp ( const char * str1, const char * str2 );
This function starts comparing the first character of each string. If they are equal to each
other, it continues with the following pairs until the characters differ or until a terminating
null-character is reached.
标准规定:
第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字
代码演示:
#include <stdio.h>
#include <string.h>
int main()
{
char* p1 = "abcdef";
char* p2 = "aqwer";
int ret = strcmp(p1, p2); // p1和p2比
// a==a, 对比下一对,b<q,所以p2大
printf("%d\n", ret);//-1
return 0;
}
strcmp函数模拟实现:
自己想的笨方法:
#include<stdio.h>
#include<string.h>
#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 != NULL);
assert(str2 != NULL);
while (*str1 && *str2)
{
if (*str1 > *str2)
{
return 1;
}
else if (*str1 < *str2)
{
return -1;
}
else
{
str1++;
str2++;
}
}
if (*str1 == '\0' &&*str2 == '\0')//str1 str2都等于\0
{
return 0;
}
else if (*str1)//str2等于\0
{
return 1;
}
else//str1等于\0
{
return -1;
}
}
int main()
{
char arr1[] = "abcd";
char arr2[] = "abcd";
/*if (strcmp(arr1, arr2) > 0)
{
printf("arr1>arr2\n");
}
else if (strcmp(arr1, arr2) < 0)
{
printf("arr1<arr2\n");
}
else
{
printf("arr1=arr2\n");
}*/
if (my_strcmp(arr1, arr2) > 0)
{
printf("arr1>arr2\n");
}
else if (my_strcmp(arr1, arr2) < 0)
{
printf("arr1<arr2\n");
}
else
{
printf("arr1=arr2\n");
}
return 0;
}
看书后优化:
#include<stdio.h>
#include<string.h>
#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 != NULL);
assert(str2 != NULL);
while (*str1 == *str2)
{
if (*str1)//如果两个相等且其中一个为\0
{
return 0;
}
else
{
str1++;
str2++;
}
}
return *str1 - *str2;//返回>0或<0才是标准,而且可以看相差的ASCII码
}
int main()
{
char arr1[] = "abcd";
char arr2[] = "abcd";
/*if (strcmp(arr1, arr2) > 0)
{
printf("arr1>arr2\n");
}
else if (strcmp(arr1, arr2) < 0)
{
printf("arr1<arr2\n");
}
else
{
printf("arr1=arr2\n");
}*/
if (my_strcmp(arr1, arr2) > 0)
{
printf("arr1>arr2\n");
}
else if (my_strcmp(arr1, arr2) < 0)
{
printf("arr1<arr2\n");
}
else
{
printf("arr1=arr2\n");
}
return 0;
}
前面三个都是长度不受限制的字符串函数
有点不安全
以下是三个长度受限制的字符串函数
2.5 strncpy
文档:char * strncpy ( char * destination, const char * source, size_t num );
从源字符串拷贝num个字符到目标空间。
Copies the first num characters of source to destination. If the end of the source C string
(which is signaled by a null-character) is found before num characters have been copied,
destination is padded with zeros until a total of num characters have been written to it.
注意事项:
① 如果源字符串的长度小于 n,则拷贝完源字符串之后,在目标的后面追加 0,填充至 n 个
② dest 和 src 不应该重叠(重叠时可以用更安全的 memmove 替代)
③ 目标空间必须足够大,以确保能够存放源字符串 dest
④ 目标空间必须可变,即目标空间 dest 不可以被 const 声明
代码演示:
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[5] = "abc";
char arr2[] = "hello world";
strncpy(arr1, arr2, 4); // 从arr2中拷贝4个到arr1
printf("%s\n", arr1);// 打印了hell
// 注意这里arr1有5个空间,如果arr1空间不够就会报错或者多打印其它东西
return 0;
}
2.6 strncat
文档:char * strncat ( char * destination, const char * source, size_t num );
追加 num个字符到目标空间
Appends the first num characters of source to destination, plus a terminating null character.
If the length of the C string in source is less than num, only the content up to the terminating null-character is copied
注意事项:如果源字符串的长度小于 num,则只复制 \0 之前的内容。
代码演示:
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[30] = "hello";
char arr2[] = "world";
strncat(arr1, arr2, 3); // 从arr2中取3个追加到arr1中
printf("%s\n", arr1);//打印了hellowor
return 0;
}
2.7 strncmp
文档:int strncmp ( const char * str1, const char * str2, size_t num );
比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。
返回结果和strcmp一样
#include <stdio.h>
#include <string.h>
int main()
{
const char* p1 = "abczdef";
const char* p2 = "abcqwer";
// int ret = strcmp(p1, p2);
int ret1 = strncmp(p1, p2, 1);
int ret2 = strncmp(p1, p2, 4);
printf("%d %d\n", ret1, ret2);//0 1
return 0;
}
2.8 strstr模拟实现
文档:char * strstr ( const char *str1, const char * str2);
在str1字符串中查找是否包含str2字符串,如果存在则返回的是第一次出现位置的字符串,如果不存在,则返回的是null,如果str2传的为空串则返回str1
Returns a pointer to the first occurrence of str2 in str1, or a null pointer if str2 is not part of str1.
代码演示:是子串的话,返回首次出现的地址
#include <stdio.h>
#include <string.h>
int main()
{
char* p1 = "abcdefgh";
char* p2 = "def";
char* ret = strstr(p1, p2); // 判断p2是否是p1的子串
printf("%s\n", ret);//defgh
return 0;
}
思路:(双指针)(注意到是下面匹配上面)
配成功的首地址,这样我们在s1就需要用到双指针,
一个用来记录上面s1第一次匹配的地址
这里以指针p来记录第n次匹配的起始位置;
大致思路是假设指针s1指向目标字符串首地址,指针s2指向源字符串首地址,分别对指针s1和s2解引用并比较,若匹配内容不相同称之为匹配失败,则目标字符串指针s1,向后移动一位,若匹配内容相同称之为第n次匹配,假设第一次匹配成功则将第一次匹配中的指针s1和s2分别后移,若后移后指针s1和s2指向的内容不相同,则第一次匹配失败,s1回到第一次匹配的首位值得下一位,s2回到首地址,若此时指针s1和s2指向的内容相同则,开启第二次匹配,以此类推直到s2指向空,则匹配成功。
#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strstr(const char* str1, const char* str2)
{
assert(str1 != NULL);
if (str2 == NULL)
{
return (char*)str1; //库函数里面的实现
} //强制类型转化为要返回的类型,本来类型是const char*
const char* s1 = str1;
const char* s2 = str2;
const char* p = str1;
while (*p)
{
s1 = p; //一次匹配不成功p++后继续匹配
s2 = str2;
while (*s1 && *s2 && (*s1 == *s2))//其中一个为\0或者不相等就跳出
{
s1++; //可以把两个++放到上面( )
s2++;
}
if (*s2 == '\0') //匹配成功
{
return (char*)p;//强制类型转化为要返回的类型,本来类型是const char*
}
p++; //一次匹配不成功p++后继续匹配
}
return NULL;
}
int main()
{
char arr1[] = "abcdef";
char arr2[] = "cd";
if (strstr(arr1, arr2) == NULL)
{
printf("找不到");
}
else
{
printf("找到了:%s\n", strstr(arr1, arr2));
}
if (my_strstr(arr1, arr2) == NULL)
{
printf("找不到");
}
else
{
printf("找到了:%s\n", my_strstr(arr1, arr2));
}
return 0;
}
2.9 strtok
文档:char * strtok ( char * str, const char * sep );
注意事项:strtok 会破坏原字符串,分割后原字符串保留第一个分割符前的字符
分割邮箱使用演示:
#include<stdio.h>
#include<string.h>
int main()
{
const char* sep = "/.";
char email[] = "https://blog.csdn.net/GRrtx?type=blog";
char cp[40] = { 0 };
strcpy(cp, email);
char* ret = strtok(cp, sep);
if (ret != NULL)
printf("%s\n", ret);
ret = strtok(NULL, sep);
if (ret != NULL)
printf("%s\n", ret);
ret = strtok(NULL, sep);
if (ret != NULL)
printf("%s\n", ret);
ret = strtok(NULL, sep);
if (ret != NULL)
printf("%s\n", ret);
ret = strtok(NULL, sep);
if (ret != NULL)
printf("%s\n", ret);
return 0;
}
运行结果:
上面代码看书后用for优化后我的表情是这样的:
#include<stdio.h>
#include<string.h>
int main()
{
const char* sep = "/.";
char email[] = "https://blog.csdn.net/GRrtx?type=blog";
char cp[40] = { 0 };
strcpy(cp, email);
for (char* ret = strtok(cp, sep); ret != NULL; ret = strtok(NULL, sep))
{
printf("%s\n", ret);
}
return 0;
}
2.10 strerror
文档:char * strerror ( int errnum );
返回错误码,所对应的错误信息。
C语言的库函数,在执行失败的时候,都会设置错误码,设置好的:
使用演示:
#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{
FILE* pf = fopen("test.txt", "r");//打开文件操作,后面会学
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
fclose(pf);
pf = NULL;
return 0;
}
3 字符分类函数和字符转换函数
3.1字符分类函数
下面函数的头文件: #include <ctype.h> (这些函数不常用,刷题想不出来可以自己模拟实现)
代码演示:isupper
#include <stdio.h>
#include <ctype.h>
int main()
{
char ch = 'R';
if(isupper(ch))//是大写字母就返回非0 真
{
printf("是大写字母\n");
}
else
{
printf("不是大写字母\n");
}
return 0;
}
3.2字符转换函数
#include <ctype.h>
int tolower ( int c ); //大写转小写
int toupper ( int c ); //小写转大写
#include<stdio.h>
#include <ctype.h>
int main()
{
int i = 0;
char arr[] = "Test String.\n";
while (arr[i] != '\0')
{
if (isupper(arr[i]))
{
arr[i] = tolower(arr[i]);
}
printf("%c", arr[i]);
i++;
}
//运行结果:test string.
return 0;
}
本篇完
练习就是自己模拟实现上面五种模拟实现的函数,其它的有时间就模拟下