文章目录
1、介绍
指针:是存放某种变量的地址的变量,它本身也是一个变量。
字符:单引号内的字符,占八位。
字符串:多个字符加一个结束符’\0’组成,即 " " = ’ ’ +’ \0’;
字符串型:专门用于存放字符串的变量类型,其本质是一个指向首个字符的指针。
2、C语言定义字符串的几种方法
方法一,定义一个固定长度的字符数组来存放字符串。
char str[1024] = "string!!!";
方法二,定义一个字符数组,由计算机分配空间。
char str[] = "string!!!"
方法三,用指针来存放字符串。
char *p = "string"
以上三种方法都是在声明变量的同时也定义字符串,但很多时候我们希望的是在代码中间赋予字符串,到底如何做请看4.3。
3、C语言字符串的几种错误用法
错误用法一,声明一个字符数组但未来赋值,导致计算机不知道分配多少空间给这个数组。
char str[] //❌
错误用法二,声明一个固定长度的字符数组,但在赋值上出现问题。
char str[1024];
str = "string"; //❌
在以上代码中,str表示该数组第一个字符的地址,所以这样的操作等于给第一个字符的地址写入一个字符串,那当然错误。同理,下面这样也是错误的。
char str[1024];
*str = "string"; //❌
因为即便解引用了,还是第一个字符的空间而已,不能赋予整个字符串。
那么正确用法自然是这样,解引用每个地址,然后放入一个个字符。
char str[10];
str[0] = 'h';
str[1] = 'l';
错误用法三,修改只读字符串。
char *p = "string"; //指针p存放字符串"string"的地址,但字符串"string"存放在只读存储区中。
p[0] = 'a'; //❌
以上这种方法是最常遇到的错误,指针存放到应该是一个左值(lvalue: locator value)的地址,而不能使一个右值(rvalue: read value)的地址。右值一般是存在寄存器中,有临时性,且只读。
错误用法四,往指针的地址区写入字符串,指针名其实是一个地址,如果往一个地址区域写入字符串那自然是错误的。
char *p;
p = "hello world!" //❌
错误用法五,字符串的长度超过数组容量。
char str[5] = "hello"; //❌
hello虽然刚好是五个字符,且在一些时候计算机也没给我们报错,正常运行了。但隐患还是存在的,str[5]虽然声明了五个字符的空间,但最后一个字符要用来放置结束符号’\0’,所以实际该数组只能放四个字符。
4、C语言字符串相关的操作
本章节所以函数都参考于C语言的官方文档,也建议大家在使用之前对照一下这个文档,因为文档中有提示你包含一些头文件。
https://c-cpp.com/c/string
4.1、判断字符串的长度(strlen)
strlen()函数可以判断字符串中的有多少个字节,函数原型如下。
size_t strlen( const char *str );
它和sizeof还是有所区别的,看以下例子:
char buf[512];
strcpy(buf, "hello!");
printf("%d %d", strlen(buf), sizeof(buf));
输出结果是6 512,即strlen只计算buf中的字符串长度,而sizeof会计算整个buf所占的内存空间大小。
4.2、拼接字符串(strcat)
函数原型如下:
char *strcat( char *dest, const char *src );
使用例子:
char str[50] = "Hello ";
char str2[50] = "World!";
strcat(str, str2);
4.3、字符串赋值(strcpy)
通过该函数,我们可以给指定字符数组赋值。
char *strcpy( char *dest, const char *src );
使用例子
char temp[10];
strcpy(temp, "hello");
4.4、字符串比较(strcmp)
所谓字符串比较本不是比较字符串长度,而是比较字符串某个字符的ASCII码大小,而strcmp只能用于比较第一位不同位的ASCII码值大小,strncmp就可以比较指定位置的字符ascll码大小。
strcmp函数原型如下:
int strcmp( const char *lhs, const char *rhs );
注意到这个函数有返回值,若lhs大于rhs的第一位不同位,则返回1,否则返回-1。若全部相等则返回0。
该返回值可以用于判断两个字符串是否一样。下面给个例子解释一下什么叫第一位不同位。
char buf_1[10] = "abcde";
char buf_2[10] = "abedf";
printf("%d\n", strcmp(buf_1, buf_2)); //print -1
printf("%d\n", strcmp(buf_2, buf_1)); //print 1
像上面两个字符串,前面四位都是abcd,那么第一位不同位就是第五位。
由于C语言中,如果你想比较"a"是不是等于"a",双等于号(==)是去索引字符串所在的内存区域,然后判断它们两个字母是不是在同一个区域,若相同则判断成功,不同则判断失败。但很显然,许多时候,字符串内存是相同的,但地址不一样。这个时候再用双等号就不合适。那么就需要用到strcmp来比较两个字符串是否相同。
if("a" == "a") //不建议
char s1[10] = "hello";
if(strcmp(s1, "hello") == 0)
{
printf("they are the same\n");
}
else
{
printf("they are not the same\n");
}
4.5、把字符串写入某个内存空间(strncpy)
如果想把字符串写入到某个内存空间,我们可以使用strncpy的函数,其函数原型如下:
char *strncpy( char *dest, const char *src, size_t count );
在C99标准之后还可以增加一个参数:
char *strncpy( char *restrict dest, const char *restrict src, size_t count );
由于第一个比较常用,这里只说第一个。dest参数为某内存空间的地址,src为写入的字符串的地址,count用于设置写入如字符串的字符数(注意不是字节数)。返回值一般不用管它,它返回的是被写入的字符串的地址。
举个例子:
char s[10];
strncpy(s, "hello!", 6);
printf("%s", s); //print hello! in the terminal
4.6、填充内存空间(memset)
在编程中,经常需要反复读写一个存放字符串的内存空间,而为了不让数据影响到下一次使用,就需要使用对内存空间全部设置为0。用到的函数就是memset (memory set),函数原型如下:
void* memset(void* dest, int ch, std::size_t count );
使用例子:
char buf[512];
strcpy(buf, "hello!";)
memset(buf, 0, sizeof(buf));
printf("%s", buf); //print nonthing
当然memset的用途很广泛,这只是其中一种。
示例一
任务:判断“hello world!”中有多少个o。
代码如下:
#include<stdio.h>
char str[]="hello world!";
char *p;
int i,temp,sum;
void main()
{
//由于str本质是指针,故可以直接赋值,但注意此时p拿到的仅仅是str中第一个字符的地址。
p=str;
temp=sizeof(str);
for(i=0;i<temp;i++)
{
if('o'==*(p+i)){sum+=1;}
}
printf("%d\n",sum);
}
运行结果:sum==2
示例二
任务:用一个结构体,其成员包括名字和分数。如name:Xiao Ming,score:85
代码如下:
#include<stdio.h>
char str[20];
void main()
{
struct{
char *name;
int score;
}student;
//从键盘读取输入
printf("please enter the name:");
gets(str); //从键盘读取字符串
student.name=str; //字符串赋值给指针
printf("please enter the score:");
scanf("%d",&student.score);
//最终结果显示
printf("Name: %s\nScore: %d\n",student.name,student.score);
}
结果: