前篇:
目录
1.字符串常量/字符串字面量
用双引号括起来的内容称为字符串字面量,也叫字符串常量,存储时,字符和编译器加的\0都被存在内存里。
字符串常量属于静态存储类别,存储一次,可以使用多次。双引号括起来的内容 就相当于 指向该字符串存储位置的指针。
如果我们尝试解引用一下这个双括号东东,就会得到第一个字符。
(这就类似于数组名不仅仅是一个名字,还是一个指向数组位置的指针)。
关门,放图!!!
2.字符数组
- 标准的数组初始化形式:
stupidstupidchar c[5]={'L','o','v','e','\0'};
注意加上\0,字符数组才能变成字符串;如果没有’\0’结束符,字符数组使用%s输出会乱码;因为没有\0,它不知道什么时候停止。
- 用字符串初始化数组: char cc[ ]="Rabbit";
不用自己加空字符,编译器会自动加上;
不过它与字符串本身还是有区别的,我们把这一部分放到下一板块和指针一起讲,比较着更能看出不同~。
另外,我们可以让编译器自己计算数组的大小,虽然只能是在初始化时这样做。(但是这足够了,哪有人想要一个一个数字母的个数啊zzz)
既然创建了一个字符串数组,像从前一样,我们来聊聊数组名~
它同样代表了数组首元素的地址:
cc==&cc[0]; *cc=='R'; *(cc+1)=='a';
既然如此,我们隆重推出指针表示法来创建一个字符串;
3.用指针表示法创建字符串
初始化: char *pt="Rabbit" ;
char cc[ ]="Rabbit" ;
赋值: char *pp ; pp="Great" ;
但是数组不能直接赋值;
这很很简单,pp、pt和cc现在都是指针了,我们不多赘述(我居然还会用成语VAV)
先讲讲上一个板块遗留的问题,关于用字符串初始化数组与字符串本身的区别(不好意思,名字起的有点绕)
- 字符串字面量储存在静态储存区中
- 数组形式:程序运行时为数组分配内存,将字符串的内容拷贝到数组中。也就是说这个时候其实有两个字符串副本。
- 指针形式:初始化指针时,是把字符串的地址拷贝给指针,即pt和“Rabbit”的地址相同。
这里剩下的内容简单,我们还是做一个表格清楚一点,也简洁~
麻烦手机版的uu们滑动看一下这个表格~
初始化 | 类型 | 自增 | 加法 | 使用 | |
---|---|---|---|---|---|
数组形式 | char cc[]="Rabbit" | cc 地址常量 改变cc就是改变地址 | cc++ 违法 | cc+1 指向下一字符 | 数组表示法cc[2] 指针表示法 *(cc+2) |
指针形式 | char *pt="Rabbit" | pt 指针变量 改变pt就是改变指向 | pt++ 指向下一字符 | pt+1 指向下一字符 | 数组表示法pt[2] 指针表示法 *(pt+2) |
练习5.1:
用指针形式、数组形式,指针表示法、数组表示法表示字符串;
#include<stdio.h>
int main()
{
char c[]="YOU ARE SO BRIGHT.";
char *pt="I love Rabbit.";
printf("%s\n",c);
printf("%s\n",pt);
printf("%c\n",*c);
printf("%c\n",*pt);
for(int i=0;c[i]!='\0';i++)
printf("%c",c[i]);
printf("\n**************************************\n");
for(int i=0;*(c+i)!='\0';i++)
printf("%c",*(c+i));
printf("\n**************************************\n");
for(int i=0;pt[i]!='\0';i++)
printf("%c",pt[i]);
printf("\n**************************************\n");
for(int i=0;*(pt+i)!='\0';i++)
printf("%c",*(pt+i));
printf("\n**************************************\n");
for(int i=0;*pt!='\0';pt++)
printf("%c",*pt);
printf("\n**************************************\n");
}
练习5.2:
复制字符串
#include<stdio.h>
int main()
{
char a[]="happy birthday to you!";
char b[30];
int i;
for(i=0;*(a+i)!='\0';i++)//指针法
{
*(b+i)=*(a+i);
}
*(b+i)='\0';
//上面的循环没有复制到空字符,所以这句一定要加,这样才是字符串,
printf("the string a is: %s\n",a);
printf("the string b is: ");
for(i=0;b[i]!='\0';i++)
printf("%c",b[i]);//下标法
printf("\nanother method:\n");
char *c;
c=b;
for(;*c!='\0';c++)//指针法
printf("%c",*c);
}
4.字符串数组
创建一个字符串数组,我们就可以访问多个不同的的字符串。
char my[3][10]={ "happy" , "birthday" , "to you" };
char *you[3]={ "thank" ,"you" ,"very much" };
第一个是char类型数组的数据,第二个是指向字符串的指针数组。
和往常一样,我们放一张表格。
含 | 元素大小 | 类型 | 修改字符串 | |
---|---|---|---|---|
char c[N][M] | 内含N个 数组 | 相同 为M | char | 被定义后只能读取不能修改,之后对它的赋值都是错误的 |
char *p[N] | 内含N个 指针 | 可以不同 | char* | 定义后可以读取和修改每个字符 |
5.指向指针的指针 (禁止套娃)
char you[3][10]={ "thank" ,"you" ,"very much" };
char *ap[10];
for(int i=0;i<3;i++) ap[i]=you[i];
char **p=ap;
关于**p,我们可以理解为*(*p),括号里面是一个指针。
*p用指针来指向指针数组中的一个字符串,通过p递增指向指针数组中不同的字符串;**p指向字符串里的某个字符;
看完上一篇,这个对你来说应该so easy叭~~
#include<stdio.h>
int main()
{
char you[3][10]={ "thank" ,"you" ,"very much" };
char *ap[10];//每个元素的内容,是一个字符串指针
for(int i=0;i<3;i++)
ap[i]=you[i];
printf("*ap=%s\n",*ap);
printf("**ap=%c\n",**ap);
char **p=ap;
printf("*p=%s\n",*p);
printf("**p=%c\n",**p);
for(int i=0;i<3;i++,p++)
printf("%s ",*p);
}
再看另一段代码:
warning!虽然可以运行,但是在运行时给出了警告:
deprecated conversion from string constant to 'char*' [-Wwrite-strings]
已弃用从string常量到'char*'的转换[-Wwrite-strings]
所以指针数组不能直接指向字符串常量了,我们在使用的时候还是指向字符串数组吧。
6.字符串指针做函数参数
字符串在函数间传递时,可以用字符数组名或者指向字符串的指针变量作为参数。
但由于数组名和指针实际含义不同,在使用时要十分小心。
1.因为指针指向字符串字面量本身的地址,如果我们不想修改字符串内容,却传递了指针(字符串字面量的地址)给形参,被调函数就是在改变字符串常量本身;
当然,如果你加上了const限定符,当我没说。
2.而如果我们传递的数组名,那没关系,因为数组里存的是字符串字面量的副本。
所以如果不希望改变字符串字面量本身,实参用数组名,形参用指针,高效安全。
我们先编个程序看看1会发生什么;
[Warning] deprecated conversion from string constant to 'char*' [-Wwrite-strings]
[警告]不推荐将字符串常量转换为“char*”[-Wwrite strings]
程序可以运行,但不是我们的预期,所以传给函数的字面常量是没法被修改的。
练习5.4:
依然是复制字符串,不过这次我们写一个copy函数;
#include<stdio.h>
void copy_string(char*from,char*to);
int main()
{
char a[]="happy birthday to you!";
char b[]="you are so kind!";
int i;
printf("the string a is: %s\n",a);
printf("the string b is: %s\b",b);
copy_string(a,b);
printf("\nafter the copy:\n");
printf("the string a is: %s\n",a);
printf("the string b is: %s\b",b);
}
void copy_string(char*from,char*to)
{
for(;*from!='\0';from++,to++)
*to=*from;
*to='\0';//这句话很重要我要再说一遍!!!
}
练习5.3:
写一个函数,将两个字符串连接,即编写strcat函数。
如果两个字符串相等,则不连接。
例如:字符串1为 stu,字符串2为dent,粘贴之后,字符串1变为:student。
判断字符串相等,可以使用:strcmp函数(来自老师的实验作业题,我就在这里交作业了hhh)
#include<stdio.h>
#include<string.h>
void add_string(char*before,char*after);
int main()
{
char a[]="happy birthday ";
char b[]="to you!";
int i;
printf("the string a is: %s\n",a);
printf("the string b is: %s\b",b);
if(strcmp(a,b)==0)
return 0;
add_string(a,b);
printf("\nafter the add:\n");
printf("the string a is: %s\n",a);
printf("the string b is: %s\b",b);
}
void add_string(char*before,char*after)
{
while(*before!='\0')
before++;
//循环转到第一个字符串结束
for(;*after!='\0';)
*(before++)=*(after++);
*before='\0';//这句话很重要我要再再再说一遍!!!
}
我更新很勤快吧~~这可是比微积分和线性代数都优先的事情呢~
分享就结束啦,感谢阅读!!!!阿里嘎多!!!
2021.12.2
SThree楠钰子.