主函数参数
一开始我们写主函数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);
}
运行结果为: