函数
IO函数
字符串操作函数
字符操作函数
内存操作函数 memset
时间/日期函数
数学函数 sqrt
其他库函数
交换两数
void Swap(int* pa, int* pb)//传地址 形参
{
int tmp = 0;
tmp = *pa;
*pa = *pb;
*pb = tmp;
}
int main()
{
int a = 10;
int b = 20;
Swap(&a,&b);//实参
printf("%d %d\n", a, b);
}
实参:真实传给函数的参数 可以是常量、变量、表达式、函数
形参:是指函数名括号中的变量
传值调用:当实参传给形参的时候,形参实际上实参的临时拷贝,函数的形参和实参分别占着不同内存块,对形参的修改不会影响实参(不对数进行操作,找出最大数)
传址调用:把函数外部创建变量的内存地址传递给函数参数,函数内部可以直接调用函外部的变量(对数进行操作,两数交换)
//每调用一次函数,num+1
//函数的声明 //声明有这个函数,只是在主函数的后面//num可以省略,声明中不会用到
void Add1(int* num);
int main()
{
int num = 0;
Add1(&num);//函数要操作num,传址
printf("%d", num);
}
//函数的定义
void Add1(int* num)
{
(*num)++;//解引用
}
函数的嵌套调用:函数和函数之间可以有机的组合
函数的链式访问:把一个函数的返回值作为另一个函数的参数
printf返回的是整型的字符串个数
int main()
{
printf("%d", printf("%d", printf("%d", 43)));//4321
}
函数的声明放在.h头文件中,函数的实现放在.c文件中,主函数放到test.c
函数声明放在函数使用之前,先声明后使用
函数定义是指函数的具体实现,交待函数的功能实现
递归常见的错误:栈溢出(栈空间被耗尽 )
//将无符号整型 一个个输出
void print(int n)
{
if (n > 9)
{
print(n / 10);
}
printf("%d ", n % 10);
}
int main()
{
unsigned int num = 0;
scanf("%d", &num);
//递归
print(num);
}
(1234)
(123)4
(12)3 4
(1)2 3 4
递归直接或间接调用自身的方法,将一个大问题拆成与原问题相似,但规模较小的问题。
递归的两个必要条件:
1、存在限制条件,满足限制条件,递归不在继续;
2、每次递归越来越接近这个限制条件
斐波那契数列(前两数之和等于第三个数)
int count = 0;
int Fib(int n)
{
if (n == 3)//计算第三个斐波那契数计算次数
{
count++;
}
if (n <= 2)
return 1;
else
return Fib(n - 1)+ Fib(n - 2);//1、递归
}
int Fib(int n)
{
int a = 1;
int b = 1;
int c = 1;
while (n > 2)//2、循环
{
c = a + b;
a = b;
b = c;
n--;
}
return c;
}
int main()
{
int n = 0;
int ret = 0;
scanf("%d", &n);
ret = Fib(n);
printf("%d\n", ret);
}
求字符串长度 strlen
长度不受限制的字符串函数 strcpy strcat strcmp
长度受限制的字符串函数 strncpy strncat strncmp
字符串查找 strstr strtok
错误信息报告 strerror
字符操作
内存操作函数 memcpy memmove memset memcmp
字符串通常放在常量字符串中或者字符数组中
int main()
{
//int len1 = strlen("abcdef");
//printf("%d\n", len1);//6
char arr[] = { 'a','b','c','d','e','f' };
int len2 = strlen(arr);
printf("%d\n", len2);//随机值
}
strlen 字符串以\0作为结束标志,返回的是字符串中\0前面出现的字符大小(不包含\0)
函数的返回值是无符号的
int main()
{
if (strlen("abc") - strlen("abcdef") > 0)
{
printf("hehe");//hehe
}
else
{
printf("haha");
}
}
返回的值是无符号整型 -3
10000000000000000000000000000011 原码
11111111111111111111111111111100 反码
11111111111111111111111111111101补码>0
无符号整型 补码与原码相同
模拟实现strlen()
1、计数器
int my_strlen(const char* str)//地址要放到指针中,str指针变量
{
int count = 0;
assert(str != NULL);//保证指针有效性
while (*str != '\0')//解引用
{
count++;
str++;
}
return count;
}
int main()
{
char arr[] = "abcdef";
int len = my_strlen(arr);//数组传参,传过去的不是整个数组,而是数组首元素的地址
printf("%d\n", len);
}
2、不创建变量实现 递归实现
把大事化小
my_strlen(“bit\0”)
1+my_strlen(“it\0”)
1+1+my_strlen(“t\0”)
1+1+1+my_strlen(“\0”)
1+1+1+0
int my_strlen(const char* str)//str指针变量
{
if (*str != '\0')//*str解引用
{
return 1 + my_strlen(str + 1);
}
else
{
return 0;
}
}
int main()
{
char arr[] = "abcdef";
int len = my_strlen(arr);
printf("%d\n", len);
}
3、指针实现
int my_strlen(const char* str)//地址要放到指针中,str指针变量
{
char* start = str;//start指向数组起始地址
char* end = str;//end指向数组起始地址
while (*end != '\0')//解引用
{
end++;//使end最后一个元素地址 指针+-整数
}
return end - start;//指针-指针 指针指向的位置空间相隔几个元素
}
int main()
{
char arr[] = "abcdef";
int len = my_strlen(arr);//数组传参,传过去的不是整个数组,而是数组首元素的地址
printf("%d\n", len);
}
strcpy 源字符串必须以\0结束(防止越界访问),会将源字符串中的\0拷贝到目标空间,目标空间必须足够大,以确保能存放源字符串
模拟实现strcpy()
char* my_strcpy(char* dest, const char* src)//const不改变源字符 char*是指针,返回地址
{
//指针指向字符串的第一个字符
//while (*src!='\0')
//{
// *dest++ = *src++;
//}
//*dest = *src;//src='\0'
//'\0'的ASCII为0
//if (dest != NULL && src != NULL)//防止字符串为空
//{
// while (*dest++ = *src++)
// {
// ;
// }
//}
char* ret = dest;//将dest的起始地址放到ret中
assert(dest != NULL);//断言 不满足会报错
assert(src != NULL);
while (*dest++ = *src++)//把src指向的字符串拷贝到dest指向的空间,包含'\0'字符
{
;//将\0赋值给*dest=0,结束循环
}
return ret;
}
int main()
{
//strcpy 字符串拷贝
char arr1[] = "#############";//数组
char* arr="abcdef";//常量字符串将a的地址放到arr中
char arr2[] = "bit";
my_strcpy(arr1, arr2);
printf("%s\n", arr1);//bit 打印 b i t \0结束标志,后面的#就不打印
}
strncpy()拷贝n个字符从源字符串到目标空间,如果源字符串的长度小于num,则拷贝完字符串后,在目标的后面追加0,直至num个
int main()
{
char arr1[10] = "abcdef";
char arr2[] = "hello world";
strncpy(arr1, arr2, 4);
}
strcat 追加字符串,目标空间必须足够大,不能追加字符串本身 源字符串必须要有\0
int main()
{
char arr1[30] = "hello";
char arr2[] = "world";
strcat(arr1, arr2);
printf("%s\n", arr1);
}
模拟实现strcat
char* my_strcat(char* dest, char* src)
{
char* ret = dest;//ret中存储的是目的空间的起始地址
assert(dest != NULL);
assert(src);
//1.找到目的字符串的\0
while (*dest!='\0')
{
dest++;
}
//2.追加
while (*dest++ = *src++)//将src中的元素追加到dest ,src=\0,赋值给dest=0,结束循环
{
;
}
return ret;
}
int main()
{
char arr1[30] = "hello";
char arr2[] = "world";
my_strcat(arr1, arr2);
printf("%s\n", arr1);
}
strncat 指定长度的追加,不管追加几个,在结尾处追加\0,若追加的个数大于源字符串的长度,追加完源字符串后就结束。
int main()
{
char arr1[30] = "abcdef\0xxxxxxxxxxxxxxxxxxxxxx";
char arr2[] = "hello world";
strncat(arr1, arr2, 11);
}
strcmp 一对一对字符比较,比较ASCII值
第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串小于第二个字符串,则返回小于0的数字
第一个字符串等于第二个字符串,则返回0
int main()
{
char* p1 = "abc";
char* p2 = "pbc";
//int ret = strcmp(p1, p2);
//printf("%d\n", ret);
if (strcmp(p1, p2) > 0)
printf("p1>p2");
else if (strcmp(p1, p2) == 0)
printf("p1=p2");
else if (strcmp(p1, p2) > 0)
printf("p1<p2");
}
模拟实现strcmp
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;//小于
return (*str1 - *str2);
}
int main()
{
char* p1 = "abc";
char* p2 = "abcd";
int ret = my_strcmp(p1, p2);
printf("%d\n", ret);
}
strncmp()字符串比较
int main()
{
//strncmp字符串比较
const char* p1 = "abcdef";
const char* p2 = "abcrty";
//int ret = strcmp(p1, p2);
int ret = strncmp(p1, p2, 3);
printf("%d\n", ret);
}
strstr()查找字符串
返回一个指针指向第一次出现查找字符串的地址,如果找不到返回一个空指针
int main()
{
char* p1 = "abcedfabcdef";
char* p2 = "edf";
char* ret = strstr(p1, p2);//指针变量存放的是地址 strstr返回的是地址
if (ret == NULL)
{
printf("子串不存在\n");
}
else
{
printf("%s\n", ret);
}
}
模拟实现strstr()
#include<assert.h>
char* my_strstr(const char* p1, const char* p2)
{
assert(p1 != NULL);
assert(p2 != NULL);
char* s1 = NULL;//初始化
char* s2 = NULL;
char* cur = (char *)p1;
if (*p2=='\0')//p2字符串为空时,指针指向\0的地址
{
return (char*)p1;//p1受保护,返回类型不受保护,强制转换
}
while (*cur)
{
s1 = cur;//cur指向字符串开始位置的地址
s2 = (char*)p2;
while (*s1 && *s2 && (*s1 == *s2))//*s1!=\0 *s2!=\0 *s1=*s2
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return cur;//找到子串
}
if (*s1 == '\0')
{
return NULL;//提前终止,找不到子串
}
cur++;//cur指向两个字符串字符相等开始的位置
}
return NULL;//找不到子串
}
int main()
{
char* p1 = "abc";
char* p2 = "abcdedf";
char* ret = my_strstr(p1, p2);//指针变量存放的是地址 strstr返回的是地址
if (ret == NULL)
{
printf("子串不存在\n");
}
else
{
printf("%s\n", ret);
}
}
char strtok (charstr,const char*sep)
sep参数是个字符串,定义了用作分隔符的字符集合
第一个参数指定一个字符串,它包含了0个或多个由sep字符串中一个或多个分隔符分割的标记
strtok函数找到str中的下一个标记,并将其用\0结尾,返回一个指向这个标记的指针 ,第二次指向\0的指针(会改变被操作的字符串,所以切分的字符串一般都是临时拷贝的内容并且可修改 )
strtok函数第一个参数不为NULL,函数将找到str中第一个标记,保存它在字符串中的位置
strtok函数第一个参数为NULL,函数将在同一字符串中被保存的位置开始,查找下一个标记
如果字符串中不存在更多标记,则返回NULL指针
int main()
{
char arr[] = "zcy@163.com";
char* p = "@.";
char buf[1024] = { 0 };
strcpy(buf, arr);
//切割buf中的字符串
char* ret = NULL;
for (ret = strtok(arr, p); ret != NULL; ret = strtok(NULL, p))//找不到更多标记返回NULL指针,结束循环
{
printf("%s\n", ret);
}
}
strerror返回错误码所对应的错误信息
#include<errno.h>
int main()
{
//错误码 错误信息
//0 No error
//1 Operation not permitted
//2 No such file or directory
char* str = strerror(0);
char* str = strerror(errno);
//errno是一个全局的错误码变量
//当c语言的库函数在执行过程中,发生了错误,就会把对应的错误码,赋值到errno中
printf("%s\n", str);
字符操作
#include<ctype.h>
int main()
{
//char ch ='w';
//int ret = islower(ch);
//printf("%d\n", ret);
char ch = '9';
int ret = isdigit(ch);
printf("%d\n", ret);
}
字符转换函数 tolower toupper
int main()
{
//char ch = tolower('A');
//printf("%c\n", ch);
char ch = toupper('a');
putchar(ch);
}
int main()
{
char arr[] = "I Am A Student";
int i = 0;
while (arr[i])
{
if (isupper(arr[i]))
{
arr[i] = tolower(arr[i]);
}
i++;
}
printf("%s\n", arr);
}
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 0 };
strcpy(arr2, arr1);
}
只拷贝一个1,计算机中小端存放,
01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
strcpy拷贝字符当把01拷贝下来,拷贝00结束符
memcpy将arr1拷贝到arr2中(任意类型都可以),拷贝num个字节大小
模拟实现memcpy
void* my_memcpy(void* dest, const void* src, size_t num)
{
void* ret = dest;//dest改变 ret不变
assert(dest != NULL);
assert(src != NULL);
while (num--)//num单位为字节
{
*(char*)dest = *(char*)src;//将类型强制转换为char*类型 一次拷贝一个字节
++(char*)dest;//由于强制类型转换优先级低于++,但是要先强制类型转换后++
++(char*)src;//给(char*)src ++
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[5] = { 0 };
my_memcpy(arr2, arr1, sizeof(arr1));
//strcpy(arr2, arr1);
struct S arr3[] = { {"zhangsan",20},{"lisi",21} };
struct S arr4[3] = { 0 };
my_memcpy(arr4, arr3, sizeof(arr3));
}
memmove处理内存重叠的情况
对于memcpy的要求拷贝不重叠的就可以,但是memcpy可以处理重叠拷贝(不保证所有平台都可以拷贝 )
void* my_memmove(void* dest, const void* src, size_t num)
{
char* ret = dest;
assert(dest != NULL);
assert(src != NULL);
//if (dest<src || dest>(char*)src + num)
//{
// //从前向后
//}
//else
//{
// //从后向前
//}
if (dest < src)
{
//从前向后
while (num--)//先使用后-- num=20 num--=19 num=1 num--=0 20次
{
*(char*)dest = *(char*)src;
++(char*)dest;
++(char*)src;
}
}
else
{
//从后向前
while (num--)// num是变量,看num
//先使用后-- 先使用num=20进循环 后-- num--=19 ,循环内进行运算
//先使用num=1进循环 后-- num--=0 循环内+的是0
{
*((char*)dest + num) = *((char*)src + num);
}
}
return ret;
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
//memcpy(arr + 2, arr, 20);
my_memmove(arr + 2, arr, 20);
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
}
memcpy
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 1,2,5,4,3 };
int ret = memcmp(arr1, arr2, 9);
printf(“%d\n”, ret);
}
memset()将指针空间前num个字节,设置为value
int main()
{
char arr[] = "hello world";
memset(arr, '*', 5);
printf("%s\n", arr);
}