字符串——C语言程序设计(八)

C语言程序设计(八)

文章目录

  • C语言程序设计(八)
  • 字符串
    • 字符数组
    • 字符串
    • 字符串变量
    • 字符串常量
    • 用指针还是数组?
    • char*是字符串?
    • 字符串赋值?
    • 字符串输入输出
    • 安全的输入
    • 常见错误
    • 空字符串
    • 字符串数组
      • 附加:之前用switch-case写的月份程序可以用数组来写
    • 程序参数
    • 单字符输入输出
      • putchar函数
      • getchar函数
    • 标准库中的字符串函数
    • string.h
      • 字符串中找字符

字符串

字符数组

  • char word[] = {‘H’, ‘e’, ‘l’, ‘l’, ‘o’,‘!’};
    • 这不是C语言的字符串,因为不能用字符串的方式做计算
word[0] H
word[1] e
word[2] l
word[3] l
word[4] o
word[5] !

字符串

  • char word[] = {‘H’, ‘e’, ‘l’, ‘l’, ‘o’,‘!’,’\0’}; //这才是C语言的字符串
word[0] H
word[1] e
word[2] l
word[3] l
word[4] o
word[5] !
word[6] \0
  • 0(整数0)结尾的一串字符

    • 0\0是一样的,但是和0不同
  • 0标志字符串的结束 ,但它不是字符串的一部分

  • 计算字符串长度的时候不包含这个0

  • 字符串以数组的形式存在,以数组或指针的形式访问

  • 更多的是以指针的形式

  • string.h 里有很多处理字符串的函数

字符串变量

  • char *str = “Hello”; //定义一个指针指向字符数组
  • char word[] = “Hello”; //字符数组
  • char line[10] = “Hello”; //字节长度为10的字符数组; char长度是1字节

字符串是数组; 这些变量都是字符数组的变量

  • “Hello”
  • ″Hello″ 会被编译器变成一个字符数组放在某处,这个数组的长度是6,结尾还有表示结束的0。 {‘H’, ‘e’, ‘l’, ‘l’, ‘o’,‘!’,’\0’};像这样的字符数组;所以还需加一.
  • 两个相邻的字符串常量会被自动连接起来
  • 行末的\表示下一行还是这个字符串常量

字符串

  • C语言的字符串是以字符数组的形态存在的
    • 不能用运算符对字符串做运算
    • 通过数组的方式可以遍历字符串
  • 唯一特殊的地方是字符串" "(双引号)字面量可以用来初始化字符数组
  • 以及标准库提供了一系列字符串函数

字符串常量

  • char* s = "Hello, world!";
  • s 是一个指针,初始化为指向一个字符串常量
    • 由于这个常量所在的地方,所以实际上s是 const char* s ,但是由于历史的原因,编译器接受不带const的写法,
    • 但是试图对s所指的字符串做写入会导致严重的后果
  • 如果需要修改字符串,应该用数组:char s[] = "Hello, world!";
#include 
int main()
{
	int i = 0;
	char *s1 = "Hello World";
	char *s2 = "Hello World";
	char s3[] = "Hello World";
	//其中指针s1和s2指向同一个空间,也就是值相同; 而s3[]的数组是重新开辟一个空间,然后把字符数组的值复制进入 
	printf("&i=%p\n", &i);
	printf("s1=%p\n", s1);
	printf("s2=%p\n", s2);
	printf("s3=%p\n", s3);
	
	for(i=0; i<(sizeof(s3)/sizeof(s3[0])); i++){
		printf("%c", s3[i]);
	}//遍历输出字符数组 
	printf("\n");
	
	
	s3[0] = 'b'; //注意字符数组中的元素要用单引号,不能是双引号 
	printf("s3[0]=%c\n", s3[0]);
	
	for(i=0; i<(sizeof(s3)/sizeof(s3[0])); i++){
		printf("%c", s3[i]);
	}//遍历输出字符数组 
	printf("\n");
	
	return 0;
		
}

输出结果:

&i=000000000062FE0C
s1=0000000000404000
s2=0000000000404000
s3=000000000062FE00
Hello World
s3[0]=b
bello World

注意:

char *s1 = "Hello World";
s1[0] = 'b';
//会直接报错,因为s1是一个指针,初始化为指向一个字符串常量,而这个常量是不可改变的,但是如果用数组就可以用数组:char s1[] = "Hello, world!";而数组是一种特殊的指针, 它指向一个字符串数组的首地址,

字符用单引号, 字符串用双引号

用指针还是数组?

  • char *str = “Hello”;

  • char word[] = “Hello”;`

  • 数组:这个字符串在这里

    • 作为本地变量空间自动被回收
  • 指针:这个字符串不知道在哪里

    • 处理参数
    • 动态分配空间
  • 所以如果要构造一个字符串用数组; 如果要处理一个字符串用指针

char*是字符串?

  • 字符串可以表达为char*的形式;
  • char*不一定是字符串
    • 本意是指向字符的指针,可能指向的是字符的数组(就像int*一样; int* 不一定是数组; 它可能指向单个int,也可能指向数组)
    • 只有它所指的字符数组有结尾的0,才能说它所指的是字符串。比如:char* word = {‘H’, ‘e’, ‘l’, ‘l’, ‘o’,‘!’,’\0’};

字符串赋值?

char *t = “title”;
char *s;
s = t;
  • 并没有产生新的字符串,只是让指针s指向了t所指的字符串,对s的任何操作就是对t做的

字符串输入输出

char string[8];
scanf(%s”, string);
printf(%s”, string);
#include 
int main()
{
	char word[8];//可以放8个char类型,超过的话,字符数组会越界
	char word2[8];
	while(1){
    scanf("%s", word);
	scanf("%s", word2);
	printf("%s##%s##\n", word, word2);
    }
	return 0;
}

输出:

hello world
hello##world##

hello
world
hello##world##

helloworld
hello_world
helloworld##hello_world##

可以发现%s读入一个单词(到空格、tab或回车为止)

  • scanf读入一个单词(到空格、tab或回车为止)
  • scanf是不安全的,因为不知道要读入的内容的长度

安全的输入

char string[8];
scanf(%7s”, string);
  • 在%和s之间的数字表示最多允许读入的字符的数量,这个数字应该比数组的大小小一; 最后一个字符要放0.
    • 下一次scanf从哪里开始?根据个数来读内容而不是空格或回车符.
#include 
int main()
{
	char word[8];//可以放8个char类型,超过的话,字符数组会越界
	char word2[8];
	while(1){
    scanf("%7s", word);//可以放8个char类型,但只能限制输入7个字符,最后一个字符要放0
	scanf("%7s", word2);
	printf("%s##%s##\n", word, word2);
	printf("\n"); 
    }
	return 0;
}
helloworld
hellowo##rld##

1234
123456
1234##123456##

12345678
1234567##8##

常见错误

char *string;
scanf(%s”, string);
  • 以为char*是字符串类型,定义了一个字符串类型的变量string就可以直接使用了
    • 由于没有对string初始化为0,所以不一定每次运行都出错

空字符串

  • char buffer[100]=””;
    • 这是一个空的字符串,buffer[0] == ‘\0’
  • char buffer[] = “”;
    • 这个数组的长度只有1!

字符串数组

  • char **a
    • a是一个指针,指向另一个指针,那个指针指向一个字符(串)
  • char a[][]
    • a是一个二维数组,第二个维度的大小不知道,不能编译,所以二维数组必须要有第二个维度的大小;
  • char a[][10]
    • a是一个二维数组,a[x]是一个char[10]; 是指每个a[]单元最多有10个字符元素
  • char *a[]
    • a是一个一维数组,a[x]是一个char*; 是一个指针
#include 
int main()
{
	char a[][10]={  
		"Hello",
		"World",
		"1234567890" 
			
	};  //类似于数组的元素集合 ,因为是集合而不是块,所以得用分号; 
	return 0;

} 

这样编译会出错,因为"1234567890"超过了9个char长度的大小,char a[][10] 是指每一行都不超过9个字符元素,(因为C语言中是字符串后面是\0结尾占据一个空间,好比顾头不顾腚)。

But:

#include 
int main()
{
	char *a[]={  
		"Hello",
		"World",
		"1234567890" 
			
	};  //类似于数组的元素集合 ,因为是集合而不是块,所以得用分号; 
	return 0;

} 

这样写就没问题, 他们的区别是:

字符串——C语言程序设计(八)_第1张图片

附加:之前用switch-case写的月份程序可以用数组来写

  • 用char month[][20]
#include 
int main()
{
    char month[][20]= {
        "null",
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December",
    };
    int i;
    while (1) {
        scanf("%d",&i);
        printf("%s\n",month[i]);
    }   
}

char *month[]

#include 

int main()
{
    char *month[] = {
        "null",
        "January",
        "February",
        'March',
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December",
    };
    int i;
    printf("请输入月份:"); 
    scanf("%d",&i);
    printf("%s\n", month[i]);
}

程序参数

  • int main(int argc, char const *argv[]) //括号里的参数(一个整数 一个字符串数组); 前面的整数是说明字符串数组有多大
  • argv[0]是命令本身
    • 当使用Unix的符号链接时,反映符号链接的名字
#include 
int main(int argc, char const*argv[])
{
	int i;
	for(i=0; i<argc; i++){
		printf("%d:%s\n",i, argv);
	}//遍历输出数组 
	return 0; 
} 
  • 得在Unix上使用;windows上不能直接运行出结果

单字符输入输出

putchar函数

  • int putchar(int c); //虽然是传入一个int类型参数; 但是它int型能接收的只是一个字符char
  • 向标准输出写一个字符(每一次只写入一个字符) put char:输出字符
  • 返回写了几个字符,EOF(-1)表示写失败

每一次只写一个字符的意思是,每次

getchar函数

  • int getchar(void);//没有参数
  • 从标准输入读入一个字符 get char 读入字符
  • 返回类型是int是为了返回EOF(-1)
    • Windows—>Ctrl-Z
    • Unix—>Ctrl-D
#include 
int main(int argc, char const*argv[])
{
	int ch;
	
	while( (ch = getchar()) != EOF){
		putchar(ch);
	}
	printf("EOF\n"); 
	return 0;
 } 

输出:

1234
1234
12656
12656
ch
ch
EOF
EOF

^Z
EOF

在windows系统中Ctrl + Z 再加回车会返回EOF ; 在Unix上用Ctrl + D

其他的输出什么就返回什么.

标准库中的字符串函数

  • C的标准库中的函数

  • 头文件: string.h

  • 使用这些函数时得输入#include

string.h

  • strlen函数
    • size_t strlen(const char *s); //传入的字符串不会修改 (const)
    • 返回s的字符串长度(不包括结尾的0); 就是专门用来输出数组字符串的长度, 与sizeof有区别; (sizeof是直接返回占用内存空间的字节长度大小; 而strlen呢是直接返回字符串的个数)
    • strlen函数的作用是它会告诉你字符串的长度
#include 
#include 
int main(int argc, char const*argv[])
{
	char line[] = "Hello";
	printf("strlen=%lu\n",strlen(line));
	printf("sizeof=%lu\n",sizeof(line));
	
	return 0;
 } 

输出:

strlen=5
sizeof=6

之所以sizeof=6, 是因为字符串数组中含有/0, 而这个不会被strlen函数记进去。

仿写strlen函数

#include 
//#include 
size_t mylen(const char* s)
{
	
	int idx = 0;
	while (s[idx]!='\0'){
		idx++;	
	}
	return idx;
} //仿照strlen函数

int main(int argc, char const*argv[])
{
	char line[] = "Hello";
//	printf("strlen=%lu\n",strlen(line));
	printf("sizeof=%lu\n",sizeof(line));
	printf("mylen =%lu\n", mylen(line));
	return 0;
 } 

输出:

sizeof=6
mylen =5

其中这自己定义的mylen函数与标准库里的strlen函数功能一样。

  • strcmp函数

    • int strcmp(const char *s1, const char *s2);

    • strcmp函数的作用是:比较两个字符串,返回:

    • 0:s1==s2

    • 1:s1>s2

    • -1:s1

#include 
#include 
int main()
{
	char s1[] = "abc";
	char s2[] = "abc";
	printf("%d\n", strcmp(s1, s2));//字符串相同,则返回0
	printf("%d\n", 'a'-'b');
	
	char s3[] = "abc";
	char s4[] = "ABc";
	printf("%d\n", strcmp(s3, s4)); //s3和s4不同, s3>s4
	printf("%d\n", 'a'-'A');
	
	return 0;
 } 

输出:

0
-1
1
32

仿写strcmp函数

#include 
#include 

int mycmp(const char *a, const char *b)
{
	int c;
	while (*a == *b && *a != '\0'){
		a++;
		b++;
	}
	if (*a-*b > 0){
		c = 1;
	}else if(*a-*b <0){
		c = -1;
	}else{
		c = 0;
	}
	return c;
}

int main(int argc, char const *argv[])
{
	char s1[] = "abc";
	char s2[] = "abc";
	printf("%d\n", mycmp(s1, s2));
	printf("%d\n", 'a'-'b');

 } 

输出:

0
-1
  • strcpy 函数
    • char * strcpy(char *restrict dst, const char *restrict src);

    • src的字符串拷贝到dst //注意是将后面的参数拷贝到前面

      • restrict表明srcdst不重叠 (only C99)
    • 返回dst

    • 为了能链起代码来

    • 复制一个字符串

    • 仿写一个strcpy函数

char *dst = (char*)malloc(strlen(src)+1);//先动态申请内存空间, , 字符串的长度要+1, 因为要包括最后的'\0'
strcpy(dst, src);

举个栗子:

#include 
#include 
#include 
int main()
{
	char s1[]  = "abc";
	char *s2 = (char*) malloc(strlen(s1)+1);//注意分配的内存大小要加1 
	strcpy(s2, s1); //将s1拷贝到s2,  
	int i;
	for(i=0; i<strlen(s2); i++){
		printf("%c", s2[i]); 
	}
	return 0;
}

输出:

abc

也可以这样, 直接相同大小空间直接替换过去

#include 
#include 

int main()
{
	char s1[] = "abc";
	char s2[] = "ABC";
	strcpy(s2, s1); //将s1拷贝到s2,  
//	int i;
//	for(i=0; i
//		printf("%c", s2[i]); 
//	}
    //可以不必要用for遍历输出字符串数组
    
    //直接%s就可以输出字符串数组
    printf("%s\n", s2); 
	return 0;
}

输出:

abc

仿写strcpy函数(只写部分代码)

//第一种:
char* mycpy(char* dst, const char* src)
{
	int idx = 0;
    while (src[idx]){
        dst[idx] = src[idx];
        idx++;
    }
    dst[idx] = '\0';
    
    return 0;
}//用数组定义的mycpy函数

//第二种:
char* mycpy(char* dst, const char* src)
{
	char* ret = dst;//先保留起始地址
    while (dst++ = src++); 
    dst[idx] = '\0';
    return ret;
}//用指针定义的mycpy函数
  • strcat函数

    • 字符串——C语言程序设计(八)_第2张图片

    • char * strcat(char *restrict s1, const char *restrict s2);

      • 把s2拷贝到s1的后面,接成一个长的字符串 (也就是连接起来)
      • 返回s1
      • s1必须具有足够的空间
  • 安全问题

    • strcpystrcat都可能出现安全问题, 可能造成数组越界, 超出存储范围, 就会有可能占到别的数据存放的地方, 这样会导致其他数据不安全,容易丢失。
    • 如果目的地没有足够的空间?
    • 所以建议是尽量少用或者不用strcpystrcat
  • 安全版本(多了个n; 即最多能拷贝过去n的范围大小)

    • char * strncpy(char *restrict dst, const char *restrict src, size_t n);

    • char * strncat(char *restrict s1, const char *restrict s2, size_t n);

    • int strncmp(const char *s1, const char *s2, size_t n); //这里的n是只是让它比较到第n个

字符串中找字符

  • strchr

    • char * strchr(const char *s, int c); //在s中,从左边寻找c第一次出现的位置, 并返回指针
    • char * strrchr(const char *s, int c);//在s中,从右边寻找c第一次出现的位置, 并返回指针
    • 它们都是寻找单个字符的
    • 返回NULL表示没有找到

举个栗子: 一个数组字符串hello,找l的后半段

#include 
#include 

int main()
{
	char s[]  = "hello";
	char *p = strchr(s, 'l'); //从左边第一个'l'读起 ,strchr返回一个指针,指向一个你要找的字符 
	printf("%s\n", p); //返回的指针赋给指针p, 从指针p所指的地址开始输出字符串 ,因此不需要用*p ,与之前的指针指向某个变量的值不一样, 这个指针相当于数组头指针, 它指向的是一连串的数组空间 

	return 0;
}


输出:

llo

怎样找第二个l呢 ?

#include 
#include 

int main()
{
	char s[]  = "hello";
	char *p = strchr(s, 'l'); //从左边第一个'l'读起 ,strchr返回一个指针,指向一个你要找的字符 
	//寻找第二个l开始 
	p = strchr(p+1, 'l');
	printf("%s\n", p); 
}

输出:

lo

也可以将找到的那部分拷贝出来

#include 
#include 
#include 

int main()
{
	char s[]  = "hello";
	char *p = strchr(s, 'l'); //从左边第一个'l'读起 ,strchr返回一个指针,指向一个你要找的字符 
	char *t = (char*)malloc(strlen(p)+1);
	strcpy(t, p);//将p的字符串拷贝到t中 
	printf("%s\n", t);
	free(t);
	return 0;
}

输出:

llo

如果是要l的前一段呢?

这里要用到一些小技巧

#include 
#include 
#include 

int main()
{
	char s[]  = "hello";
	char *p = strchr(s, 'l'); //从左边第一个'l'读起 ,strchr返回一个指针,指向一个你要找的字符 
	char c = *p;//先保存p所指的空间上存放的值 
	*p = '\0'; //再将p所指空间上的值改为结束时的'\0' 
	printf("%s\n", s);//这时输出就只有'l'的前部分 
	char *t = (char*)malloc(strlen(s)+1);
	strcpy(t, s);//将p的字符串拷贝到t中 
	printf("%s\n", t);//这时输出'l' 前面的部分 
	free(t);
	*p = c; //将p所指的那个空间的原来的值恢复回来 
	printf("%s\n", s);//这时就是原来的值了 
	return 0;
}

输出:

he
he
hello

附注: 要输出字符串数组得用%s, (%c输出不了,除非用数组遍历才可以,那就相对比较麻烦)

  • strstr

    • 是字符串中寻找一个字符串的
    • char * strstr(const char *s1, const char *s2);
    • char * strcasestr(const char *s1, const char *s2);
    • 返回NULL表示没有找到
    • …省略说明…

你可能感兴趣的:(笔记,C语言程序设计,c语言)