系列文章目录
提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 Python 机器学习入门之pandas的使用
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 系列文章目录
- 前言
- 一、基本概念
- 二、函数传参
-
- 参数概念
- 全局变量
- 值传递
- 指针传递
- 一维数组在函数间传参
- 二维数组(及多维数组)传参
-
- [二维数组在函数间传参 - 一级指针](#二维数组在函数间传参 - 一级指针)
- [二维数组在函数间传参 - 行指针](#二维数组在函数间传参 - 行指针)
- [二维数组在函数间传参 - 二级指针](#二维数组在函数间传参 - 二级指针)
- 三、指针函数
- 四、函数指针
- 五、递归函数
- 六、字符串操作函数
- 七、字符操作函数
前言
提示:这里可以添加本文要记录的大概内容:
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。
提示:以下是本篇文章正文内容,下面案例可供参考
一、基本概念
main函数
main函数可带参也可不带参
带参形式:
c
int main(int argc,char *argv[]){
printf("argc = %d\n",argc); //打印1
printf("%s\n",argv[0]); //打印./a.out
return 0;
}
- argv :接收来自命令行的各个字符串参数,
argv[0]为:./该程序文件名argv[1]到argv[argc-1]是用户传入的参数。
- argc :记录了接收字符串的个数

函数的组成结构
函数是一个完成特定功能的代码模块,其程序代码独立,通常要求有返回值,也可以是空值。
函数定义:
c
<数据类型> <函数名称>( <形式参数说明> ) {
语句序列;
return[(<表达式>)];
}
<数据类型>是整个函数的返回值类型。如无返回值应该写为void型return[(<表达式>)]语句中表达式的值,要和<数据类型>保持一致。<形式参数说明>是以逗号","分隔的多个变量的说明形式- 大括弧对 {<语句序列> },称为函数体;<语句序列>是大于等于零个语句构成的
函数的调用
函数的使用就叫函数调用,形式如下:
c
函数名称(〈实际参数〉)
- 对于无返回值的函数来讲,只能形成一个函数调用语句。
- 函数调用可以作为一个运算量出现在表达式中,也可以单独形成一个语句。
- 实参就是在使用函数时,调用函数传递给被调用函数的数据。
c
#include <stdio.h>
int get_max(int a, int b){ //函数定义
if(a > b)
return a;
else
return b;
}
int main(int argc,const char *argv[]){
int max;
int a = 29,b = 60;
max = get_max(a,b); //函数调用
printf("%d\n",max);
return 0;
}
函数的声明
编译器从上往下顺序进行编译,如果函数定义在调用之后,就需要在调用之前进行函数声明。如:
c
#include <stdio.h>
int get_max(int, int b); //先声明在使用,否之编译到调用的时候编译器没见过该函数,它不认识
int main(int argc,const char *argv[]){
int max;
int a = 29,b = 60;
max = get_max(a,b); //函数调用
printf("%d\n",max);
return 0;
}
int get_max(int a, int b){ //函数定义在调用之后
if(a > b)
return a;
else
return b;
}
其中,<形式参数说明>可以缺省说明的变量名称,但类型不能缺省。就算变量名与定义不一致也没问题。但返回类型,函数名,参数类型和参数个数要与定义时保持一致。
例:定义求x^n值的函数(x是实数, n为正整数)
c
#include <stdio.h>
double pow_fun(double x, unsigned int n){
double sum = 1.0;
for(;n > 0; n--){
sum *= x;
}
return sum;
}
int main(int argc,const char *argv[]){
int n;
double x,ret;
scanf("%lf%d",&x,&n);
ret = pow_fun(x,n);
printf("%lf\n",ret);
return 0;
}
二、函数传参
参数概念
形参
- 形参,全称为"形式参数"
- 形参是在定义函数的时候使用的参数
- 目的是用来接收调用该函数时传递的参数
实参
- 实参,全称为"实际参数"
- 实参是在调用时传递给函数的参数,即传递给被调用函数的值。
- 在主调函数中调用一个函数时,函数名后面括弧中的参数(可以是一个表达式)称为"实际参数"。
参数传递
-
在程序运行过程中,将实际参数的值或者地址传递给被调用函数的形式参数,从而在函数中完成对数据处理和返回的过程。
-
在C语言当中,参数的传递方式本质上只有一种,就是值传递。
-
但为了更好区分理解,划分了三种方式
- 全局变量
- 值传递方式
- 指针传递方式
-
核心原则:一切皆为值传递
全局变量
- 全局变量就是在函数体外(所有函数外)说明的变量
- 全局变量一经定义后就会在程序的任何地方可见。
- 函数调用的位置不同,程序的执行结果可能会受到影响。不建议使用
值传递
- 调用函数将实参传递给被调用函数,被调用函数将创建同类型的形参并用实参初始化
- 形参是新开辟的存储空间,在函数中改变形参的值,不会影响到实参
- 示意图

- C 语言在调用函数时,会将实参的值复制一份 传递给形参。形参和实参在内存中位于不同的位置,形参是实参的副本。
例:值传递是否可以交换两个变量的值
c
#include <stdio.h>
void fun_swap(int a, int b);
int main() {
int x = 200, y = 100;
printf("before:x=%d y=%d %p %p\n", x, y, &x, &y);
fun_swap(x, y);
printf("after:x=%d y=%d\n", x, y); //x=200 y=100
return 0;
}
void fun_swap(int a, int b) {//int a=x; int b=y;
int t = a;
a = b;
b = t;
printf("a=%d b=%d %p %p\n", a, b, &a, &b); //a=100 b=200, 修改的是形参副本
}
从内存视角看:
-
调用
fun_swap(x, y)时,系统在栈上为形参a,b分配空间,并将x,y的值复制到a,b中。 -
函数内部修改
a,b不影响外部的x,y。
指针传递
如果想在函数内部修改外部的变量,就必须传递变量的地址 (指针)。此时复制的是地址值,函数通过该地址可以间接访问原始变量。
- 按地址传递,实参为变量的地址,而形参为同类型的指针
- 被调用函数中对形参的操作,将直接改变实参的值
- 被调用函数对指针的目标操作,相当于对实参本身的操作
例:实现两个数交换的函数
c
#include <stdio.h>
void fun_swap(int *, int *);
int main() {
int x = 200, y = 100;
printf("before:x=%d y=%d %p %p\n", x, y, &x, &y);
fun_swap(&x, &y); //复制x和y的地址给形参变量a,b,此时a,b指向x,y的内存位置,解引用后修改x,y本身
printf("after:x=%d y=%d\n", x, y); //x=200 y=100
return 0;
}
void fun_swap(int *a, int *b) {
int t = *a;
*a = *b;
*b = t;
printf("a=%d b=%d %p %p\n", *a, *b, &a, &b); //a=100 b=200
}
例: 编写一个函数,统计字符串中小写字母的个数,并把字符串中的小写字母转化成大写字母
c
#include <stdio.h>
int fun_string(char *);
int main() {
char s[100];
int n;
fgets(s,100,stdin);
n = fun_string(s);
printf("%s%d\n",s,n);
return 0;
}
int fun_string(char *p) {
int t = 0;
while(*p != '\0'){
if(*p >= 'a' && *p <= 'z'){
*p -= 32;
t++;
}
p++;
}
return t;
}
一维数组在函数间传参
传递参数是普通数组
数组名形式的传参
当数组名作为实参传递时,它自动退化为指向首元素的指针 。形参可以写成数组形式或指针形式,二者完全等价。
c
void fun(int arr[ ], int size) ;
void fun(int *arr, int size) ;
void fun(int arr[10], int size) ;// 长度10被忽略,仅起文档作用
size用来指定数元素个数。- 关键点 :在函数内部 sizeof(arr) 得到的是指针的大小(4 或 8 字节),而不是整个数组的大小。因此必须额外传递数组长度。
例:编写函数,计算一个一维整形数组的所有元素的和
c
#include <stdio.h>
int fun(int *,int);
int fun1(int [],int);
int main() {
int arr[] = {100,1,2,3,4,5,6,7,8,9};
int n = sizeof(arr) / sizeof(int);
int add = fun(arr,n);
int add1 = fun1(arr,n);
printf("%d\n%d\n",add,add1);
return 0;
}
int fun(int *p,int size){
int add = 0;
for(int i = 0; i < size; i++){
add += *p++;
}
return add;
}
int fun1(int p[],int size){
printf("%ld\n",sizeof(p)); //打印的是8,并不是数组的大小,本质还是指针
int add = 0;
for(int i = 0; i < size; i++){
add += p[i];
}
return add;
}
例: 对整型数组进行排序,编写排序函数
c
#include <stdio.h>
#include <stdbool.h>
void fun(int *,int);
int main() {
int arr[] = {100,11,256,39,48,500,656,76,8,91};
int n = sizeof(arr) / sizeof(int);
fun(arr,n);
for(int i = 0; i < n; i++){
printf("%d ",arr[i]);
}
putchar('\n');
return 0;
}
void fun(int *p,int size){
int t = 0;
bool flag = true;
for(int i = 0; i < size-1; i++){
if(!flag){
break;
}
flag = false;
for(int j = 0; j < size-1 -i; j++){
if(*(p+j) > *(p+j+1)){
t = *(p+j);
*(p+j) = *(p+j+1);
*(p+j+1) = t;
flag = true;
}
}
}
}
字符数组传参
c
#include <stdio.h>
#include <stdbool.h>
void del_space(char *);
int main() {
char s[100] = " abcd efg hijk ";
del_space(s);
printf("%s\n",s);
return 0;
}
void del_space(char *s){
char *p = s;
char *q = s;
while(*p != '\0'){
if(*p != ' '){
*q = *p;
p++;
q++;
}else{
p++;
}
}
*q = '\0';
}
/*
void del_space(char *s){
char *p = s;
char *q = s;
while(*p != '\0'){
if((*p != ' ' && *q != ' ') || (*p == ' ' && *q != ' ')){
p++;
q++;
}else if(*p == ' ' && *q == ' '){
p++;
}else if(*p != ' ' && *q == ' '){
*q = *p;
*p = ' ';
p++;
q++;
}else{
p++;
}
}
}
*/
二维数组(及多维数组)传参
同样会退化,但只能省略第一维的长度,其余维度必须明确指定。
二维数组在函数间传参 - 一级指针
c
const int N = 2;//要改变二维数组的行和列
const int M = 3;//只需改变这里即可
void fun(int*,int ,int);
这时实参写一维数组名arr[0],会退化为首元素指针(&arr[0][0])
c
#include <stdio.h>
void print_array(int *a,int n,int m);
int main() {
int arr[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
print_array(arr[0],3,4);
return 0;
}
void print_array(int *a,int n,int m){
for(int i = 0; i < n * m; i++){
printf("%d ",*a++);
}
}
二维数组在函数间传参 - 行指针
c
const int N = 2;//要改变二维数组的行和列
const int M = 3;//只需改变这里即可
void fun(int array[][M], int, int); //列数必须明确
//等价写法
void fun(int (*array)[M], int, int);
这时实参写二维数组名arr,退化为行指针(&arr[0])
例:编写函数,计算一个二维整形数组的奇数的个数
c
#include <stdio.h>
//int get_odd(int (*a)[4],int n,int m);//两种形式等价
int get_odd(int a[][4],int n,int m);
int main() {
int arr[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int ret = get_odd(arr,3,4);
printf("ret = %d\n",ret);
return 0;
}
int get_odd(int a[][4],int n,int m){
int i,j,sum = 0;
for (i = 0; i < n; i++){
for(j = 0; j < m; j++){
if(a[i][j] % 2 != 0)
sum ++;
}
}
return sum;
}
二维数组在函数间传参 - 二级指针
c
const int N = 2;//要改变二维数组的行和列
const int M = 3;//只需改变这里即可
void fun(int**, int, int);
这时实参是一个二级指针
c
#include <stdio.h>
int get_odd2(int ** a, int n, int m);
int main() {
int a[3][4] = {{1, 5, 3, 2}, {3, 6, 9, 8}, {2, 5, 9, 6}};
int * p[3] = {a[0], a[1], a[2]};
int r;
r = get_odd2(p, 3, 4);
printf("r=%d\n", r);
return 0;
}
int get_odd2(int ** a, int n, int m ) {
int i, j, sum = 0;
for (i = 0; i < n; i++) {
for (j = 0; j < m; j++) {
if (*(a[i]+j) % 2 != 0) {
sum++;
}
}
}
return sum;
}
三、指针函数
定义:返回值为指针的函数
c
int * fun(int x, int y); //声明
数据类型 * 函数名(参数列表){ //定义
//函数体
}
因为( )优先级更高,所以 fun与后面的括号结合,是函数名,调用它以后返回一个int ∗ 型的指针。
不能返回局部变量的指针,运行时可能出错
指针函数中可以返回什么样的指针?
- 全局变量的地址
- 字符串常量的地址
- static变量的地址
- 堆的地址
- 主调函数中有效内存
c
#include <stdio.h>
char * mystring( ) {
char *s = "hello world"; //s是静态常量的地址,可以返回
return str;
}
char *get_char(){
char ch[10] = "hello";
return ch; //返回局部变量的地址,编译器可能警告
}
int main(void) {
printf("%s\n", mystring());
printf("%s\n",get_char()); //可能运行失败
return 0;
}
例:实现字符串拷贝strcpy
c
#include <stdio.h>
#include <assert.h>
char *my_strcpy(char *dest, const char *src);
int main(void) {
char *ch = " hello hhhhhhhhh hhhhhhhh ";
char arr[100];
char *s;
s = my_strcpy(arr,ch);
printf("%s\n",s);
return 0;
}
char *my_strcpy(char *dest, const char *src){
/* if(dest == NULL || src == NULL){
return NULL;
}
*/
assert(dest && src); //检查两个指针是否为NULL
char *p = dest;
while(*src != '\0'){
*dest = *src;
src++;
dest++;
//*dest++ = *src++; 写法2
}
//*dest = '\0';
*dest = *src; //此时*src就是'\0'
/*while(*dest++ = *src++); 写法3*/
return p;
}
assert用法
assert 是 标准库中用于程序调试的宏 ,定义在 <assert.h> 头文件中。
c
#undef assert
#ifdef NDEBUG
#define assert(expr) ((void)0)
#else
#define assert(expr) \
((expr) ? (void)0 : \
__assert_fail(#expr, __FILE__, __LINE__, __func__))
#endif
用法:
c
#include <assert.h>
assert(表达式);
作用:
-
当 NDEBUG 定义时:assert(expr) 被替换为 ((void)0),什么也不做。
-
当 NDEBUG 未定义时:在程序运行时检查一个条件表达式,如果条件为假(0),则向标准错误输出一条错误信息并终止程序;如果条件为真(非0),则什么都不做,继续执行。
-
assert 只在 调试阶段 使用。在最终发布的程序中,通常会关闭所有 assert 检查,以避免性能损失。方法是在包含 <assert.h> 之前 定义宏 NDEBUG
c#define NDEBUG // 放在 #include <assert.h> 之前 #include <assert.h>
例:实现字符串连接
c
#include <stdio.h>
char *my_strcat(char *dest, const char *src);
int main(void) {
char *ch1 = " hello hhhhhhhhh hhhhhhhh ";
char ch2[100] = " JHASHb jJABi b";
char *s;
s = my_strcat(ch2,ch1);
printf("%s\n",s);
return 0;
}
char *my_strcat(char *dest, const char *src){
char *p = dest;
while(*src != '\0'){
if(*dest != '\0'){
dest++;
}
else{
*dest = *src;
src++;
dest++;
}
}
*dest = *src; //边界问题,当src为空时,dest被截断,此处需要做处理
return p;
}
直接分两段循环,思路更清晰。
c
char *my_strcat(char *dest, const char *src) {
char *p = dest;
while (*dest) dest++; // 找到 '\0'
while ((*dest++ = *src++)); // 复制,包括最后的 '\0'
return p;
}
例:编写一个指针函数,把整数123转化成字符串"123"
c
#include <stdio.h>
char *my_intchar(char *p,int num);
int main(void) {
int n;
char arr[30],*s;
scanf("%d",&n);
s = my_intchar(arr,n);
printf("%s\n%s\n",s,arr);
return 0;
}
char *my_intchar(char *p, int num){
int i = 0,t = 0;
while(num != 0){
p[i] = num % 10 + 48;
num = num / 10;
i++;
}
p[i] = '\0';
for(int j = 0; j < i / 2; j++){
t = p[j];
p[j] = p[i-1-j];
p[i-1-j] = t;
}
return p;
}
四、函数指针
定义
- 函数指针,即函数的入口地址
- 函数名代表函数的入口地址(右值)
函数指针变量一般形式
c
<数据类型> (*<函数指针名称>)(<参数说明列表>);
int (*p)(int ,int);
指针p的类型是:int (*)(int,int)
- <数据类型>是函数指针所指向的函数的返回值类型
- <参数说明列表>应该与函数指针所指向的函数的形参说明保持一致
- (*<函数指针名称>)中,
*说明为指针,()优先结合,不可缺省,表明为函数的指针
函数指针的调用
-
同函数名(非标准)
-
解引用(标准)
c#include <stdio.h> int add(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; } int mul(int a, int b) { return a * b; } int div(int a, int b) { return a / b; } double f(double a, double b) {return a + b;} int fun_arith(int a, int b, int (*p)(int, int)) {//int (*p)(int, int) = add; return p(a, b); } int main() { int m , n; int (* p)(int, int); scanf("%d%d", &m, &n); p = add; printf("+ %d\n", p(m, n)); p = sub; printf("- %d\n", (*p)(m, n)); p = mul; printf("* %d\n", p(m, n)); p = div; printf("/ %d\n", p(m, n)); //p = f;error printf("fun_arith:%d\n", fun_arith(m, n, add)); return 0; }
函数指针数组
函数指针数组是一个保存若干个函数名的数组
一般形式:
c
<数据类型> (*<函数指针数组名称> [<大小>] )(<参数说明列表> );
int (*arr[4])(int int);
在函数指针基础上加个大小就行,大小表示数组中函数名的个数
c
#include <stdio.h>
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }
int div(int a, int b) { return a / b; }
int main() {
int m , n, i;
int (* p[4])(int, int);
scanf("%d%d", &m, &n);
p[0] = add;
p[1] = sub;
p[2] = mul;
p[3] = div;
for (i = 0; i < 4; i++) {
printf("%d\n", p[i](m, n));
}
return 0;
}
qsort函数
通用快速排序函数 ,它能对任意类型 的数组进行排序,你只需要提供一个比较函数 ,告诉 qsort 如何判断两个元素的大小关系。
c
void qsort(void *base,size_t nmemb,size_t size,int(*comper)(const void *,const void*));
- base:数组首元素地址
- nmemb:数组元素个数
- size:数组元素大小
- comper:比较函数指针
比较函数需要自己实现:
对整数数组排序:
c
int cmp_int(const void *a, const void *b) {
int ia = *(int *)a; //转成对应类型
int ib = *(int *)b;
//return ia - ib; // 升序,注意只能用于较小值的比较,因为很大的整数-负数会溢出
// 若要降序,返回 ib - ia
return (a>b) - (a<b) //升序
//降序,返回:(b>a)-(b<a);
}
对字符串数组排序:
c
int cmp_string(const void *a, const void *b) {
// a 和 b 都是指向 char* 的指针
const char **sa = (const char **)a;
const char **sb = (const char **)b;
return strcmp(*sa, *sb); //字符串比较函数
}
浮点数组排序函数:
c
// 升序比较函数
int cmp_double(const void *a, const void *b) {
double da = *(const double *)a;
double db = *(const double *)b;
if (da < db) return -1;
if (da > db) return 1;
return 0;
}
// 或者更简洁的写法(利用 (da > db) - (da < db) 返回 -1/0/1)
int cmp_double2(const void *a, const void *b) {
double da = *(const double *)a;
double db = *(const double *)b;
return (da > db) - (da < db);
}
字符数组比较函数:
c
int cmp_char(const void *a, const void *b) {
// 直接返回差值,因为 char 可以安全转换为 int 且范围小
return *(const char *)a - *(const char *)b;
}
五、递归函数
- 函数体中直接或间接调用该函数自身
- 必须要有结束条件
- 递归函数的执行过程分为两个阶段:
- 递推阶段:从原问题出发,按递归公式递推从未知到已知,最终达到递归终止条件
- 回归阶段:按递归终止条件求出结果,逆向逐步代入递归公式,回归到原问题求解
- 递归函数可以简化代码结构,可能导致栈溢出
例:递归函数计算n!
c
#include <stdio.h>
int fac(int);
int main(void) {
int n,ret;
scanf("%d",&n);
ret = fac(n);
printf("n! = %d\n",ret);
return 0;
}
int fac(int n){
if(n == 1){
return 1;
}
return n * fac(n-1);
}
例 :递归函数,计算斐波那契数列
其数值为:1、1、2、3、5、8、13、21、34...... 在数学上,这一数列以如下递推的方法定义:
F(0)=1,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2)
c
#include <stdio.h>
int fid(int);
int main(void) {
int n,ret;
scanf("%d",&n);
for(int i = 1; i <= n; i++){
printf("%d ",fid(i));
}
return 0;
}
int fid(int n){
if(n == 1 || n == 2){
return 1;
}
return fid(n-1) + fid(n-2);
}
题:下列代码输出什么?
c
#include <stdio.h>
void printNumbers(int n) {
if (n < 0) return;
printf("%d ", n);
printNumbers(n - 1);
}
int main() {
printNumbers(5);
return 0;
}
答:5 4 3 2 1 0
六、字符串操作函数
字符串长度
c
size_t strlen(const char *s);
返回字符串长度,字符长度不包括'\0'
c
char s[10]={'A','\0','B','C','\0','D'};
char s[ ]="\t\v\\\0will\n"; //\t:(水平制表符)tab ,\v垂直制表符,\\:\本身
char s[ ]="\x69\141\n"; //\xhh表示十六进制数代表的符号 \ddd表示8进制的
字符串拷贝
c
char *strcpy(char *dest, const char *src); //将src指向全部拷贝dest,连同'\0'
char *strncpy(char *dest, const char *src, size_t n); //将src中指定的前n个字符拷贝到dest,如果n小于或等于源字符串的长度,目标串不会自动加'\0',需要手动加'\0'。
//如果源字符串的长度小于n,则复制完源字符串后,会用'\0'填充指定的剩余的空间。
返回dest
要确保dest指向空间足够
c
#include <stdio.h>
#include <string.h>
int main() {
char dest[20] = "**********";
char src[] = "hello world!";
strncpy(dest, src, 8); //将src的前8个字符复制到dest中
dest[8] = '\0'; //由于源字符串的长度大于或等于n,我们需要在dest的后面添加'\0'。
printf("%s\n", dest); //helllo wr 如果不手动加'\0',输出:hello wo**
return 0;
}
c
#include <stdio.h>
#include <string.h>
int main() {
char dest[20] = "**********";
char src[] = "hello";
strncpy(dest, src, 8);
//dest[8] = '\0';
printf("%s\n", dest); //hello
//此时dest的内容: hello\0\0\0\0**\0\0....;源字符串的长度小于n,则复制完源字符串后,会用'\0'填充指定的剩余的空间。
return 0;
}
字符串链接
c
char *strcat(char *dest, const char *src); //连接前,两串均以'\0'结束;连接后,串1的 '\0'取消,新串最后加'\0'
char *strncat(char *dest, const char *src, size_t n); //源字符串的长度小于n,会将源字符串的全部字符追加到目标字符串中。
//源字符串的长度大于或等于n,则只会将源字符串的前n个字符追加到目标字符串中。自动追加'\0'
字符串比较
c
int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t n); //两个字符串的前n个字符进行比较
对两串从左向右逐个字符比较(ASCII码相减),直到遇到不同字符或'\0'为止。若差值为零则继续比较下去;若差值不为零,则返回差值。
a. 若字符串1< 字符串2, 返回负整数
b. 若字符串1> 字符串2, 返回正整数
c. 若字符串1== 字符串2, 返回零
字符串中查找字符
c
char *strchr(const char *s, int c); //返回首次出现c的指针。找不到返回NULL
char *strrchr(const char *s, int c); //返回最后一次出现c的指针。找不到返回NULL
char *strchrnul(const char *s, int c); //返回最后一次出现c的指针。找不到返回指向s结尾处空字节的指针,而不是NULL。
字符串中查找子串
c
char *strstr(const char *haystack, const char *needle); //找子串,区分大小写
char *strcasestr(const char *haystack, const char *needle); //在字串,不区分大小写
返回首个相同字串的首地址,找不到返回NULL
匹配过程不包括终止'\0',但它到此为止。
七、字符操作函数
需要头文件:#include <ctype.h>
判断一个字符是否为字母
c
int isalpha(int c);
返回:0-不是字母,非0-是字母
判一个字符是否为大写字母
c
int isupper(int c);
返回:0-不是大写字母 非0-是大写字母
c
int islower(int c) //判断是否为小写字母
int isdigit(int c) //判断是否为数字字符
int isblank(int c) //判断是否为空白字符
小写转大写
c
int toupper(int c);
int toupper_l(int c, locale_t locale);//可以指定语言环境
如果参数c不是小写字母就不转换
返回:转换后的结果
在 默认的 "C" locale(或 "POSIX" locale)下,toupper() 只对 ASCII 小写字母 'a'--'z' 进行转换,返回对应的大写字母 'A'--'Z'
大写转小写
c
int tolower(int c);
如果给定的字符不是大写字母,则不对它做任何处理,也不会报错或者警告。
返回:小写字母;或原样返回(传入非大写字母时)