C语言学习第010课——指针和字符串

主函数参数

一开始我们写主函数main(void),后面直接跟一个void,但是一般情况下,主函数是带有两个参数的,而且不能改变,或者颠倒位置

int main(int argc,char* argv[]){}

这个参数一般是使用命令行启动的时候传进去的,比如:

gcc -o hello hello.c

int类型的argc表示参数的个数,上面这个命令行就有4个参数,命令本身gcc占一个参数,各参数以空格分隔
char*类型的参数具体内容,上面这个命令行的参数具体内容就是:

char* argv[] = {"gcc","-o","hello","hello.c"};

试着自己写一个函数打印一下

int main(int argc,char* argv[]){
   for(int i = 0;i<argc;i++){
        printf("%s\n",argv[i] );
   }
}

编译命令

gcc -o hello.exe hello.c

生成一个hello.exe的可执行文件,将hello.exe拖入到命令行中,后面随便加一下参数,用空格分开,例如:

hello.exe nihao ok goodbye

打印结果为:

hello.exe
nihao
ok
goodbye

有时候可以根据argc参数的值进行判断是否缺少参数,直接返回错误,结束程序运行,并给出提示信息,例如hello.exe需要3个参数,所以main函数里面,判断argc小于3,即可返回错误

#include<Windows.h>
#include<stdio.h>
int main(int argc,char* argv[]){
    if(argc < 3){
        printf("缺少参数\n");
        return -1;
    }
   for(int i = 0;i<argc;i++){
        printf("%s\n",argv[i] );
   }
   return 0;
}

项目开发中常用的字符串应用模型

strstr实现 while和do while模型

练习:统计一长串字符串中,特定的小字符串出现过几次
思路:之前学习过,在一个长串字符串中,查找特定字符串出现的位置,并返回指针,这里可以借用一下这个函数,while循环,判断my_strstr返回的指针不为空,就可以将该指针再传给while条件,继续循环。

#include<stdio.h>
#include<string.h>
extern char* my_strstr(char* str,char* ch);
int main(void){
    char* str = "11abcd111122abcd333abcd3322abcd3333322qqq";
    char ch[] = "abcd";				需求:在上面字符串中统计abcd出现的次数
    int count = 0;					count存储出现的次数
    char* p = my_strstr(str,ch);
    while(p){						如果返回的指针不为空,即可继续循环
        count++;
        p+=strlen(ch);				指针往后跳一个abcd的长度
        p = my_strstr(p,ch);		继续查询,返回的结果再次赋值给p
    }
    printf("%d\n",count);
}
char* my_strstr(char* src,char* dest){
    char* fsrc = src;			定义一个源字符串的指针
    char* fdest = dest;			定义一个目的字符串的指针
    char* fp = src;				定义一个最后要返回的指针
    while(*fsrc){				判断源字符串指针是否为空
        fp = fsrc;				待返回的指针一个一个的跟着源指针走
        while((*fsrc++ == *fdest++) && *fdest!='\0');
        						判断源指针和目的指针是否相同,相同的话,待返回指针就先不动了,源指针和目标指针自增,
        						而且等自增到目标指针结束,就表示匹配上了,返回指针
        if(*fdest=='\0'){
            return fp;
        }else{
            fsrc = fp;			如果没匹配上,源指针回到目标指针那里
            fsrc++;				源指针自增,
            fdest = dest;		目标指针回到开始的位置
        }
    }
    return NULL;
}

以上是一种实现方法,使用的是while循环,也可以使用do-while循环

#include<stdio.h>
#include<string.h>
extern char* my_strstr(char* str,char* ch);
int main(void){
    char* str = "11abcd111122abcd333abcd3322abcd3333322qqq";
    char ch[] = "abcd";
    int count = 0;
    char* p = my_strstr(str,ch);
    do{
        if(p){
            count++;
            p+=strlen(ch);
            p = my_strstr(p,ch);
        }
    }while(p);
    printf("%d\n",count);
}

原理一样,只不过是两种写法。

字符串的去空格和统计字符

求一个字符串内非空字符的个数


#include<stdio.h>

extern int getstrlen(char* ch);
int main(void){
    char ch[] = "     hello world      ";
    int len = getstrlen(ch);
    printf("字符串中的非空字符一共有%d个\n",len);
}

int getstrlen(char* ch){
    int count = 0;
    int i = 0;
    while(ch[i]){
        if(ch[i]!=' '){
            count++;
        }
        i++;
    }
    return count;
}

以上为数组思路,指针思路:


#include<stdio.h>

extern int getstrlen(char* ch);
int main(void){
    char ch[] = "     hello world      ";
    int len = getstrlen(ch);
    printf("字符串中的非空字符一共有%d个\n",len);
}

int getstrlen(char* ch){
    int count = 0;
    while(*ch){
        if(*ch != ' ')count++;
        ch++;
    }
    return count;
}

统计一段字符串中各个字符出现的次数
例如:有如下字符串

char ch[] = "nichoushachounizadizaichouyigeshishishishijiushishi";

想想怎么统计一个字符出现的次数,
思路一:第一步:使用while循环做非0判断,第二步:循环里面遇到该字符串,count++,最后count就是该字符出现的次数,
但是这有一个问题,就是一次只能解决一个字符出现的次数,如果需要计算很多个字符例如 a b c d e f g出现过多少次,只能增加代码量,一个一个的增加,还增加了很多变量。
试想,这样一个问题该怎么解决呢?
思路二:定义一个int类型的数组,里面专门存放字符出现的次数,第一个位置存a出现的次数,第二个位置存b出现过的次数,所以定义一个长度为26的int类型的数组即可,然后遍历字符串,第一个字母为n,所以应该存储到下标为’n’-'a’的位置,然后该值自增,同理。

#include<stdio.h>

int main(void){
    char ch[] = "nishoushachounizadizaichouyigeshishishishijiushishi";
    int arr[26] = {0};              定义一个数组用来存储字符串出现的次数
    for(int i=0;i<strlen(ch);i++){  遍历字符串
        arr[ch[i]-'a']++;           字符-a 即下标,a对应0 b对应1,
        							遍历到一个字符,该下标的值直接自增
    }
    for(int i=0;i<26;i++){          遍历打印结果
        printf("字母%c出现的次数为%d\n",'a'+i,arr[i]);
    }
}

运行结果为:

字符串反转模型

将一段字符串逆置
数组思路:定义两个角标,一个为0,一个位数组长度-1,两个对调位置

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

extern void my_inverse(char* str);

int main(void){
    char ch[] = "hello world";
    my_inverse(ch);
    printf("%s\n",ch);
}
void my_inverse(char* str){
    int i = 0;
    int j = strlen(str)-1;
    while(i<j){
        char temp = str[i];
        str[i] = str[j];
        str[j] = temp;
        i++;
        j--;
    }
    return;
}

指针思路:其实和数组思路一样,只不过将数组换成了指针

#include<stdio.h>
#include<string.h>
extern void my_inverse(char* str);
int main(void){
    char ch[] = "hello world";
    my_inverse(ch);
    printf("%s\n",ch);
}
void my_inverse(char* str){
    char* fstr = str;
    char* bstr = str + strlen(str) -1;
    while(fstr < bstr){
        char temp = *fstr;
        *fstr = *bstr;
        *bstr = temp;
        fstr++;
        bstr--;
    }
    return;
}
回文字符串

类似abcba hellolleh这样对称的字符串,就是回文字符串
检查一段字符串是否是回文字符串
如果是,返回1,不是返回0

#include<stdio.h>
#include<string.h>
extern int symm(char* str);
int main(void){
    char ch[] = "hellolleh";
    int n = symm(ch);
    if(n){
        printf("是回文字符串\n");
    }else{
        printf("不是回文字符串\n");
    }
}
int symm(char* str){
    char* fstr = str;
    char* bstr = str + strlen(str) -1;
    while(fstr < bstr){
        if(*fstr!=*bstr){
            return 0;
        }
        fstr++;
        bstr--;
    }
    return 1;
}

字符串处理函数

strcpy()字符串拷贝
#include<string.h>
char* strcpy(char* dest,char* src);
功能:把src所指向的字符串复制到dest所指向的空间中,'\0'也会被拷贝过去
参数:dest:目的字符串首地址
	 src:源字符串首地址
返回值:成功:返回dest字符串的首地址
注意:如果参数dest所指的内存空间不够大,可能会造成缓冲溢出的错误情况

使用示例:

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

int main(void){
    char* src = "hello world";
    char dest[50];   这里注意目标缓冲区要足够大能放下源字符串,否则会报错
    strcpy(dest,src);
    printf("%s\n",dest);
}

自己实现一下strcpy()

#include<stdio.h>

extern void my_strcpy(char* dest,const char* src);

int main(void){
    char* src = "hello world";
    char dest[50];
    my_strcpy(dest,src);
    printf("%s\n",dest);
}
void my_strcpy(char* dest,const char* src){    src的char*可以使用const修饰,dest的char*因为要给他赋值,所以不能用const修饰
    while(*dest++ = *src++);
}

strncpy() 字符串有限拷贝
include<string.h>
char* strncpy(char* dest,const char* src,size_t n);
功能:把src字符串的前n个字符复制到dest所指向的空间中,是否拷贝结束看指定的长度是否包含'\0'
参数:dest 目的字符串首地址
	 src  源字符首地址
	 n    指定需要拷贝字符串个数
返回值:成功:返回dest字符串的首地址
	   失败:NULL

使用示例:

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

int main(void){
    char* src = "hello world";
    char dest[50];
    strncpy(dest,src,7);
    printf("%s\n",dest);
}
运行结果:hello w%……&¥……*(乱码)

为什么会有乱码呢?因为上面说了,是否拷贝结束,看拷贝的长度里面有没有包含了字符串结束标志’\0’,上面的代码,很明显没有包含\0,所以,最后读取字符串的时候,没有结束标志,就一直往后面读。
解决这个问题只需要将dest字符串初始化一下,全部为0即可

char dest[50] = {0};

上面的例子中,源字符串长度为12,复制了7个字符,那如果我给写的长度是20呢?他会怎么读?

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

int main(void){
    char* src = "hello world";
    char dest[50];
    strncpy(dest,src,20);
    printf("%s\n",dest);
}
运行结果:hello world

因为赋值到第12个长度的时候,内容为\0,就会停止复制
自己写一个字符串有限拷贝

#include<stdio.h>

extern void my_strncpy(char* dest,const char* src,size_t n);

int main(void){
    char* src = "hello world";
    char dest[50] = {0};
    my_strncpy(dest,src,7);
    printf("%s\n",dest);
}

void my_strncpy(char* dest,const char* src,size_t n){
    while((*dest++ = *src++) && --n);
    这里为什么是--n?
    因为假如n=3 赋值操作在条件中,所以赋值过程和n的值经过就是
    赋值->n=2->赋值->n=1->赋值->n=0->停止
    以上过程可以看出 是赋值了3}

strcat()字符串追加
#include <string.h>
char *strcat(char *dest, const char *src);
功能:将src字符串连接到dest的尾部,‘\0’也会追加过去
参数:
	dest:目的字符串首地址
	src:源字符首地址
返回值:
	成功:返回dest字符串的首地址
	失败:NULL

使用示例:

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

int main(void){
    char* src = "world";
    char dest[50] = "hello";  这里要留有足够的空间,来放下源字符串,否则会越界
    strcat(dest,src);
    printf("%s\n",dest);
}

自己写一个字符串追加

#include<stdio.h>

extern char* my_strcat(char* dest,const char* src);

int main(void){
    char* src = "world";
    char dest[50] = "hello";  这里要留有足够的空间,来放下源字符串,否则会越界
    printf("%s\n",my_strcat(dest,src));
}
char* my_strcat(char* dest,const char* src){
    char* temp = dest;
    while(*temp)temp++;  		找到dest'\0’的位置
    while(*temp++ = *src++);
    return dest;
}
strncat()字符串有限追加
#include <string.h>
char *strncat(char *dest, const char *src, size_t n);
功能:将src字符串前n个字符连接到dest的尾部,‘\0’也会追加过去
参数:
	dest:目的字符串首地址
	src:源字符首地址
	n:指定需要追加字符串个数
返回值:
	成功:返回dest字符串的首地址
	失败:NULL

使用示例:

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

int main(void){
    char* src = "world";
    char dest[50] = "hello";  这里要留有足够的空间,来放下源字符串,否则会越界
    strncat(dest,src,3);
    printf("dest = %s\n",dest);
}

自己实现字符串有限追加

#include<stdio.h>

extern char* my_strncat(char* dest,const char* src,size_t n);

int main(void){
    char* src = "world";
    char dest[50] = "hello";     这里要留有足够的空间,来放下源字符串,否则会越界
    printf("dest = %s\n",my_strncat(dest,src,3));
}
char* my_strncat(char* dest,const char* src,size_t n){
    char* temp = dest;
    while(*temp)temp++;
    while((*temp++ = *src++) && --n);
    return dest;
}
strcmp()字符串比较
#include <string.h>
int strcmp(const char *s1, const char *s2);
功能:比较 s1 和 s2 的大小,比较的是字符ASCII码大小。
参数:
	s1:字符串1首地址
	s2:字符串2首地址
返回值:
	相等:0
	大于:>0 在不同操作系统strcmp结果会不同   返回ASCII差值
	小于:<0

使用示例:

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

int main(void){
    char ch1[] = "helloworld";
    char ch2[] = "helloworld";
    int value = strcmp(ch1,ch2);  结果为0
    printf("value = %d\n",value);
}

自己实现字符串比较函数

#include<stdio.h>

extern int my_strcmp(const char* ch1,const char* ch2);

int main(void){
    char ch1[] = "helloworld";
    char ch2[] = "helloworld";
    int value = my_strcmp(ch1,ch2);
    printf("value = %d\n",value);
}
int my_strcmp(const char* ch1,const char* ch2){
    while(*ch1== *ch2){
        if(*ch1 == '\0'){ 		要判断字符串是否到了结尾了
            return 0;
        }
        ch1++;
        ch2++;
    }
    return *ch1>*ch2?1:-1;
}
strncmp()字符串有限比较
#include <string.h>
int strncmp(const char *s1, const char *s2, size_t n);
功能:比较 s1 和 s2 前n个字符的大小,比较的是字符ASCII码大小。
参数:
	s1:字符串1首地址
	s2:字符串2首地址
	n:指定比较字符串的数量
返回值:
	相等:0
	大于: > 0
	小于: < 0

使用示例:

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

int main(void){
    char ch1[] = "helloworld";
    char ch2[] = "hellowoold";
    int value = strncmp(ch1,ch2,4);   结果为0
    printf("value = %d\n",value);
}

自己实现字符数组有限比较

#include<stdio.h>

extern int my_strncmp(const char* ch1,const char* ch2,size_t n);

int main(void){
    char ch1[] = "halloworld";
    char ch2[] = "hellowarld";
    int value = my_strncmp(ch1,ch2,5);
    printf("value = %d\n",value);
}
int my_strncmp(const char* ch1,const char* ch2,size_t n){
    for(int i = 0;i<n && ch1[i] && ch2[i];i++){   要确定两个字符串没有到结尾,才可以继续循环
        if(ch1[i]!=ch2[i])
            return ch1[i]>ch2[i]?1:-1;
    }
    return 0;
}
sprintf() 转换并格式化字符串
#include <stdio.h>
int sprintf(char *str, const char *format, ...);
功能:根据参数format字符串来转换并格式化数据,然后将结果输出到str指定的空间中,直到出现字符串结束符 '\0'  为止。
参数:
	str:字符串首地址
	format:字符串格式,用法和printf()一样
返回值:
	成功:实际格式化的字符个数
	失败: - 1

使用范例

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

int main(void){
    int a = 10;
    char str[20] = "hello world";
    char dest[25]={0};
    sprintf(dest,"%s---%d",str,a);
    printf("%s\n",dest);
    return 0;
}

运行结果:
在这里插入图片描述

sscanf 从字符串中根据格式转换成为其他类型数据
#include <stdio.h>
int sscanf(const char *str, const char *format, ...);
功能:从str指定的字符串读取数据,并根据参数format字符串来转换并格式化数据。
参数:
	str:指定的字符串首地址
	format:字符串格式,用法和scanf()一样
返回值:
	成功:参数数目,成功转换的值的个数
	失败: - 1

使用示例:

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

int main(void){
    int a,b;
    char str[20] = "a=10,b=20";
    sscanf(str,"a=%d,b=%d",&a,&b);			从字符串中根据字符串格式,取出a和b的值,并转换为int类型
    printf("%d\n",a);
    printf("%d\n",b);
    return 0;
}

运行结果:
在这里插入图片描述

strchr()字符串中的字符查找
#include <string.h>
char *strchr(const char *s, int c);
功能:在字符串s中查找字母c出现的位置
参数:
	s:字符串首地址
	c:匹配字母(字符)
返回值:
	成功:返回第一次出现的c地址
	失败:NULL

使用示例:

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

int main(void){
    char str[] = "halloworld";
    char ch = 'o';
    char* location = strchr(str,ch);
    printf("location = %s\n",location);
}

自己实现字符串中的字符查找

#include<stdio.h>

extern char* my_strchr(const char* str, int c);

int main(void){
    char str[] = "halloworld";
    char ch = 'o';
    char* location = my_strchr(str,ch);
    printf("location = %s\n",location);
}

char* my_strchr(const char* str,int c){
    while(*str){
        if(*str==c){
            return str;
        }
        str++;
    }
    return NULL;
}
strstr() 字符串中查找字符串出现的位置
#include <string.h>
char *strstr(const char *haystack, const char *needle);
功能:在字符串haystack中查找字符串needle出现的位置
参数:
	haystack:源字符串首地址
	needle:匹配字符串首地址
返回值:
	成功:返回第一次出现的needle地址
	失败:NULL

使用示例:

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

int main(void){
    char str[] = "halloworld";
    char sub[] = "llo";
    char* substring = strstr(str,sub);
    printf("substring = %s\n",substring);
}

自己实现的部分,在本文第二章,第一节

strstr实现while和do while模型

strtok()字符串的分割
#include <string.h>
char *strtok(char *str, const char *delim);
功能:来将字符串分割成一个个片段。当strtok()在参数s的字符串中发现参数delim中包含的分割字符时, 则会将该字符改为\0 字符,当连续出现多个时只替换第一个为\0。
参数:
	str:指向欲分割的字符串
	delim:为分割字符串中包含的所有字符
返回值:
	成功:分割后字符串首地址
	失败:NULL

使用示例:

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

int main(void){
    char str[] = "www.baidu.com";
    char* p = strtok(str,".");
    printf("%s\n",p);
}

运行结果:www
思考:strtok()函数返回来的是截取下来的一段,那么剩下的字符串怎么截取呢?
先看一下strtok函数调用完之后,str变成啥了

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

int main(void){
    char str[] = "www.baidu.com";
    char* p = strtok(str,".");
    printf("%s\n",p);			www
    printf("%s\n",str);			www

    printf("%p\n",p);			0028FF2E
    printf("%p\n",str);			0028FF2E
}

发现1、str内容也被变成了www,这是因为strtok会破坏源字符串,用\0替换分割的标志位,读取字符串的时候,遇到\0就停下了。

发现2:p和str地址一样,因为上面的原理,所以指针指向同一个字符串的首位置,所以地址也一样。

以上代码是将www截取出来了,那如果想截取www.baidu.com中的baidu呢?

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

int main(void){
    char str[] = "www.baidu.com";
    char* p = strtok(str,".");   截取www
    printf("%s\n",p);

    p = strtok(NULL,".");		 字符串没有截取完,在缓冲区还留有一份,当前位置为\0,
    							 所以NULL表示跳过该位置,从下一个指针开始读取
    printf("%s\n",p);
}

运行结果:
在这里插入图片描述
最后的com取出也是一样的道理,strtok函数,如果到末尾还没有找到分割标志位,就会原封不动的输出

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

int main(void){
    char str[] = "www.baidu.com";
    char* p = strtok(str,".");
    printf("%s\n",p);

    p = strtok(NULL,".");
    printf("%s\n",p);

    p = strtok(NULL,".");
    printf("%s\n",p);
}

运行结果为:
在这里插入图片描述
如果字符串较长,类似的分隔符较多,可以写一个循环

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

int main(void){
    char str[] = "nishishui\nwoshinibaba\nwoshinibaba\nwonidie";
    char* p = strtok(str,"\n");
    while(p){
        printf("%s\n",p);
        p = strtok(NULL,"\n");
    }
}

运行结果为:
在这里插入图片描述

atoi()字符串变整形
#include <stdlib.h>
int atoi(const char *nptr);
功能:atoi()会扫描nptr字符串,跳过前面的空格字符,直到遇到数字或正负号才开始做转换,而遇到非数字或字符串结束符('\0')才结束转换,并将结果返回返回值。
参数:
	nptr:待转换的字符串
返回值:成功转换后整数

使用示例:

#include<stdio.h>
#include<stdlib.h>

int main(void){
    char str[] = "562";  		562		-34 	-12ab43  	ab
    int i = atoi(str);
    printf("%d\n",i);			562		-34		-12			0
}

类似的函数还有:
atof():把一个小数形式的字符串转化成一个浮点数
atol():把一个字符串转化为long类型

#include<stdio.h>
#include<stdlib.h>

int main(void){
    char str1[] = "3.14";
    double i = atof(str1);
    printf("%.3lf\n",i);

    char str2[] = "78933";
    long l = atol(str2);
    printf("%ld\n",l);
}

运行结果为:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值