word[0] | H |
---|---|
word[1] | e |
word[2] | l |
word[3] | l |
word[4] | o |
word[5] | ! |
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
里有很多处理字符串的函数
字符串是数组; 这些变量都是字符数组的变量
0
。 {‘H’, ‘e’, ‘l’, ‘l’, ‘o’,‘!’,’\0’};像这样的字符数组;所以还需加一.\
表示下一行还是这个字符串常量字符串
" "
(双引号)字面量可以用来初始化字符数组char* s = "Hello, world!";
const char* s
,但是由于历史的原因,编译器接受不带const
的写法,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*
不一定是字符串
int*
一样; int*
不一定是数组; 它可能指向单个int,也可能指向数组)比如:char* word = {‘H’, ‘e’, ‘l’, ‘l’, ‘o’,‘!’,’\0’};
char *t = “title”;
char *s;
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);
#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 buffer[100]=””;
buffer[0] == ‘\0’
char buffer[] = “”;
char **a
char a[][]
char a[][10]
char *a[]
#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;
}
这样写就没问题, 他们的区别是:
用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]
是命令本身
#include
int main(int argc, char const*argv[])
{
int i;
for(i=0; i<argc; i++){
printf("%d:%s\n",i, argv);
}//遍历输出数组
return 0;
}
int putchar(int c);
//虽然是传入一个int类型参数; 但是它int型能接收的只是一个字符charEOF(-1)
表示写失败每一次只写一个字符的意思是,每次
int getchar(void);
//没有参数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
strlen
函数
size_t strlen(const char *s);
//传入的字符串不会修改 (const)sizeof
有区别; (sizeof
是直接返回占用内存空间的字节长度大小; 而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
表明src
和dst
不重叠 (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
函数
安全问题
strcpy
和strcat
都可能出现安全问题, 可能造成数组越界, 超出存储范围, 就会有可能占到别的数据存放的地方, 这样会导致其他数据不安全,容易丢失。strcpy
和strcat
安全版本(多了个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
第一次出现的位置, 并返回指针举个栗子: 一个数组字符串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);