c语言语法的学习——字符串
精华提取
1.用双括号括起来的内容被视为指向该字符串存储位置的指针。并且字符串常量属于静态存储类别,这说明在函数中使用字符串常量,该字符串只会存储一次,在整个程序的生命周期依然存在。
字符串在数组的存储方式:
2.字符串的表示有两种方法,1.指针表示方法 2.数组表示方法
#define MAG "我宣布一个事"
#define num 81
#include <stdio.h>
int main(void)
{
char ch[num] = "我是一个傻逼";//这是数组表示方法
const char * p = "没毛病,兄弟们";//这是指针表示方法
//char *p= "没毛病,兄弟们";是错误的,不知道为什么?——闪光点解决了。
puts("虎哥发言");
puts(ch);
puts(p);
ch[1] = 'i';
puts(ch);
return 0;
}
两者的选择:
数组表示是:字符串由于是静态存储类别,所以存储到静态存储区里面,当程序开始执行的时候,为数组开辟一个内存空间,用数组 名表示那个内存空间的地址,此时程序中字符串有两个副本,一个是在静态存储区里面的原来的字符串,一个是在数组里面的副本。
——将静态存储区的字符串拷贝给数组中
数组表示法——由于数组名是一个地址常量,所以不能进行递增运算,但是可以进行字符串值得改变。
指针表示是:由于只是将地址拷贝给指针,所以不能改变指针指向的值——字符串,但是可以改变指针本身。
——只是把字符串的地址拷贝给指针,与在静态存储区中字符串的地址相同。
3.
int (*p)[2]//他的意思就是设置一个指针指向内含2个int类型的数组
int * p[2]//由于[]优先级高,所以意思是p是一个内含2个指针元素的数组,其中每一个元素都是指向int的指针
这两者的区别以及联系
int (*p)[2]——它的应用主要是为了平替二维数组名,
#include <stdio.h>
//10.16就是二维数组的初始化
//闪光点:没有闪光点
int main(void)
{
int a[3][2]={{2,3},{2,5},{2,6}};
int (*pz)[2];//pz指向一个内含两个int类型值的数组
//这里pz2的取值关键就是,一会代码敲出来就知道了。
pz=a;
printf("pz是%p, pz+1是%p\n",pz,pz+1);
printf("pz[0]是%p,pz[0]+1是%p\n",pz[0],pz[0]+1);//pz[0]
printf("*pz对应的值是%p,*pz对应的值加1是%p\n",*pz,*pz+1);//*pz
printf("pz[0][0]是%p,pz[0][0]+1是%p\n",pz[0][0],pz[0][0]+1);//pz[0][0]
printf("*pz[0]是%p,*pz[0]+1是%p\n",*pz[0],*pz[0]+1);//*pz[0]
printf("**pz是%d,**pz+1是%d\n",**pz,**pz+1);//**pz
//pz[2][1]
return 0;
}
int * p[2]——它的主要作用就是为了表示字符串的初始化,因为每一个被引号包括的都是指向引号内容的指针。由于这一个特性,才可以用于表示二维字符串。
#define LINES 5
#define ROWS 60
#include <stdio.h>
int main(void)
{
const char* mytalent[LINES]=//注意这和之前指向二维数组的指针不一样.不能用到数字上面
{
"Adding numbers swiftly",
"Multiplying accurately",
"Stashing data",
"Following instructions to the letter",
"Understanding the C language"
};
char yourtalent[LINES][ROWS] =
{
"Walking in a straight line",
"Sleeping",
"Watching television",
"Mailing letters",
"Reading email"
};
int i;
printf("%-36s %-25s\n", "mytalent","yourtalent");
for (i = 0; i < LINES; i++)
{
printf("%-36s,%-25s\n", mytalent[i], yourtalent[i]);
}
return 0;
}
字符串函数
1.gets()函数
gets()函数,读取整行输入,直至遇到换行符,然后丢弃换行符,存储其余字符,并在这些字符的末尾添加一个空字符使其变成一个c字符串。很无脑,但是有缺陷。就是容易报错,并且新的标准 不允许使用这个函数,所以就不说明gets()函数的用法
2.fgets()函数
char *fgets(char *s, int size, FILE *);//函数原型,输入:第一个输入的是字符串对应的地址(也可以说成为指针),第二个输入就是获得字符的最大数量,size。fgets()函数将读入size-1个字符,或者读到第一个换行符。第三个输入是基本写代码就是stdin.
//输出1.输出就是返回指向char的指针,如果一切顺利,将返回输入的第一个参数。如果遇到文件的结尾,那就那就输出空指针。
用法注意:
1.如果fgets()函数读到一个换行符,会把它存储到字符串中。
[^1.通过fgets()函数输入apple pie,apple pie\n\0被存储在数组中。2.通过fgets()函数输入straweety shortcake,strawberry shortcake,超过了大小的限制,所以fgets()只读入了13个字符,数组中存储内容为strawberry sh\0]:
参考代码:
#include <stdio.h>
#define size 5
int main(void)
{
char h[size];
char* p;
puts("请输入一串字符放到数组里面");
p=fgets(h,size,stdin);
printf("%p\n", p);
puts(h);
return 0;
}
3.puts()函数
int puts(const char *str) //输入:字符串或者是指针,亦或是代表字符串的地址
//当puts
//输出:
用法注意:
puts()会显示字符串的时候会自动在末尾添加一个换行符。当遇到空字符的时候就停止输出。
参考代码:
#define MDN "l am a foolish man"
#define num 100
#include <stdio.h>
int main(void)
{
char ch[100] = "hata you";
const char* p = "l love you zhang";
puts(ch);
puts(MDN);
puts(p);
//puts(ch[2]);//为什么这样就不会运行
puts(&ch[2]);//这样就运行了.所以这个就判断puts()输入的参数
puts(p + 2);
return 0;
}
4.fputs()函数
函数原型
Int fputs (const char *str, FILE *stream)
//输入多加了stdout
//输出和puts()函数基本一致,就是参考上面puts()函数。
用法注意:
没什么注意的,和puts()函数功能一致,但是fputs函数不会在输出的末尾添加换行符。其他没什么注意的。
参考代码:
#include <stdio.h>
#define lines 10
int main(void)
{
char ch[lines];
puts("请输入一串字符串,直到你输入空行结束");
while ((fgets(ch, lines, stdin) != NULL) && ch[0] != '\n')
{
fputs(ch, stdout);
}
puts("done");
return 0;
}
5.putchar以及getchar函数
函数原型
int getchar(void)//getchar()的函数原型
int putchar(int char)//putchar()的函数原型
用法注意:
1.getchar函数只能接受单个字符,输入数字也按字符处理。输入多于一个字符时,只接收第一个字符。getchar()可以接受单个字符,包括空格,制表符,以及换行符。
getchar函数的返回值是用户输入的第一个字符的ASCII码, 如出错返回 - 1, 且将用户输入的字符回显到屏幕.
[^getchar有一个int型的返回值.当程序调用getchar时.程序就等着用户按键.用户输入的字符被存放在键盘缓冲区中.直到用户按回车为止(回车字符也放在缓冲区中).当用户键入回车之后, getchar才开始从stdin流中每次读入一个字符.getchar函数的返回值是用户输入的第一个字符的ASCII码, 如出错返回 - 1, 且将用户输入的字符回显到屏幕.如用户在按回车之前输入了不止一个字符, 其他字符会保留在键盘缓存区中, 等待后续getchar调用读取.也就是说, 后续的getchar调用不会等待用户按键, 而直接读取缓冲区中的字符, 直到缓冲区中的字符读完为后, 才等待用户按键.]:
6.strlen()函数
函数原型
unsigned int strlen(const char *str);//
//输入:代表字符串的指针或者是字符串对应的地址
//输出字符串的长度。
//size_t strlen(const char* s);
//该函数返回s字符串中的字符数,不包括末尾的空字符
用法注意:
没有------记得区分sizeof()函数
参考代码:
#include <stdio.h>
#include <string.h>
void fit_char(char* ch, int size);
#define MG 80
int main(void)
{
char ch[MG] = "i am a iron man,but l like you zhang";
puts(ch);
fit_char(ch, 5);
puts(ch);
puts(ch + 2);
puts(ch);
return 0;
}
void fit_char(char* ch, int size)//得到一串字符串,如果超出范围,就把他舍弃掉
{
while (strlen(ch) > size)
ch[size] = '\0';
}
7.strncat()来代替strcat()函数
函数原型
char* strcat(char* restrict s1, const char* restrict s2);
该函数把s2指向的字符串拷贝至s1指向的字符串末尾。s2字符串的第1个字符将覆盖s1字符串末尾的空字符。该函数返回s1。
char* strncat(char* restrict s1, const char* restrict s2, size_t n);
该函数把s2字符串中的n个字符拷贝至s1字符串末尾。s2字符串的第1个字符将覆盖s1字符串末尾的空字符。不会拷贝s2字符串中空字符和其后的字符,并在拷贝字符的末尾添加一个空字符。该函数返回s1。
8.strncmp()来代替strcmp()函数
函数原型:
int strcmp(const char* s1, const char* s2);
如果s1字符串在机器排序序列中位于s2字符串的后面,该函数返回一个正数;如果两个字符串相等,则返回0;如果s1字符串在机器排序序列中位于s2字符串的前面,则返回一个负数。
int strncmp(const char* s1, const char* s2, size_t n);
该函数的作用和strcmp()类似,不同的是,该函数在比较n个字符后或遇到第1个空字符时停止比较。
参考代码
#include <stdio.h>
#include <string.h>
#define SIZE 6
#define key "astro"
char* get_s(char* s, int size);
int main(void)
{
const char* p[SIZE] = {
"astronomy", "astounding",
"astrophysics", "ostracize",
"asterism", "astrophobia"
};
int astronum = 0;
int i;
//while (strncmp(p[i], "astro", 5)==0)//如果终止判断条件让我堵塞住了
//{
// astronum++;
//}
//if(strncmp(p[i],"astro",5))
//以上的解决方法竟然是for 循环
for (i = 0; i < SIZE; i++)
{
if (strncmp(p[i], key, 5) == 0)
{
printf("Found: %s\n", p[i]);
astronum++;
}
}
printf("和astro重叠的单词有%d个", astronum);
return 0;
}
char* get_s(char* s, int size)
{
char* p;
int i = 0;
p = fgets(s, size, stdin);
if (p)
{
while (p[i] != '\n' && p[i] != '\0')
i++;
if (p[i] == '\n')
p[i] = '\0';
else
while (getchar() != '\n')
continue;
return p;
}
}
9.strncpy()代替strcpy()函数
函数原型:
char* strcpy(char* restrict s1, const char* restrict s2);
该函数把s2指向的字符串(包括空字符)拷贝至s1指向的位置,返回值是s1。
char* strncpy(char* restrict s1, const char* restrict s2, size_t n);
该函数把s2指向的字符串拷贝至s1指向的位置,拷贝的字符数不超过n,其返回值是s1。该函数不会拷贝空字符后面的字符,如果源字符串的字符少于n个,目标字符串就以拷贝的空字符结尾;如果源字符串有n个或超过n个字符,就不拷贝空字符。xxxxxxxxxx char* strcpy(char* restrict s1, const char* restrict s2);该函数把s2指向的字符串(包括空字符)拷贝至s1指向的位置,返回值是s1。char* strncpy(char* restrict s1, const char* restrict s2, size_t n);该函数把s2指向的字符串拷贝至s1指向的位置,拷贝的字符数不超过n,其返回值是s1。该函数不会拷贝空字符后面的字符,如果源字符串的字符少于n个,目标字符串就以拷贝的空字符结尾;如果源字符串有n个或超过n个字符,就不拷贝空字符。数返回s1。
问题注意
strncpy(target, source, n)把source中的n个字符或空字符之前的字符(先满足哪个条件就拷贝到何处)拷贝至target中。因此,如果source中的字符数小于n,则拷贝整个字符串,包括空字符。但是,strncpy()拷贝字符串的长度不会超过n,如果拷贝到第n个字符时还未拷贝完整个源字符串,就不会拷贝空字符。所以,拷贝的副本中不一定有空字符。
鉴于此,该程序把n设置为比目标数组大小少1(TARGSIZE-1),然后把数组最后一个元素设置为空字符:
strncpy(qwords[i], temp, TARGSIZE - 1);
qwords[i][TARGSIZE - 1] = '\0';
参考代码:
8.sprintf()函数
// sprintf()函数声明在stdio.h中,而不是在string.h中。该函数和printf()类似,但是它是把数据写入字符串,而不是打印在显示器上。因此,该函数可以把多个元素组合成一个字符串。sprintf()的第1个参数是目标字符串的地址。其余参数和printf()相同,即格式字符串和待写入项的列表。
//
//
//
//闪光点:sprintf()函数,和printf()函数的用法一样,先格局自己设置的格式获取输入,然后把他们组合成一个字符串,最后输出到前一个变量。然后注意的是,sprintf的第一个参数应为目标字符串的地址。
//
//sprintf(formal, "%s,%-19s:&%6.2f\n", last, first, prize);这个sprintf()函数的意思就是将last按照%s的格式,first按照-19s的格式,prize按照%6.2f的格式输入到formal中,
#include <stdio.h>
#include <string.h>
#define MAX 20
char* get_s(char* ch, int size);
int main(void)
{
char first[MAX];
char last[MAX];
char formal[2 * MAX + 10];
double prize;
puts("enter your first name");
get_s(first, MAX);
puts("enter your last name");
get_s(last, MAX);
puts("enter your first name");
scanf("%lf",& prize);
sprintf(formal, "%s,%-19s:&%6.2f\n", last, first, prize);
puts(formal);
return 0;
}
char* get_s(char* ch, int size)
{
char* retu;
int i = 0;
retu = fgets(ch, size, stdin);
if (retu)
{
while (ch[i] != '\n' && ch[i] != '\0')
i++;
if (ch[i] == '\n')
ch[i] = '\0';
else
while (getchar() != '\n')
continue;
}
return retu;
}
8.其他库函数的函数原型
char* strcpy(char* restrict s1, const char* restrict s2);
该函数把s2指向的字符串(包括空字符)拷贝至s1指向的位置,返回值是s1。
char* strncpy(char* restrict s1, const char* restrict s2, size_t n);
该函数把s2指向的字符串拷贝至s1指向的位置,拷贝的字符数不超过n,其返回值是s1。该函数不会拷贝空字符后面的字符,如果源字符串的字符少于n个,目标字符串就以拷贝的空字符结尾;如果源字符串有n个或超过n个字符,就不拷贝空字符。
char* strcat(char* restrict s1, const char* restrict s2);
该函数把s2指向的字符串拷贝至s1指向的字符串末尾。s2字符串的第1个字符将覆盖s1字符串末尾的空字符。该函数返回s1。
char* strncat(char* restrict s1, const char* restrict s2, size_t n);
该函数把s2字符串中的n个字符拷贝至s1字符串末尾。s2字符串的第1个字符将覆盖s1字符串末尾的空字符。不会拷贝s2字符串中空字符和其后的字符,并在拷贝字符的末尾添加一个空字符。该函数返回s1。
int strcmp(const char* s1, const char* s2);
如果s1字符串在机器排序序列中位于s2字符串的后面,该函数返回一个正数;如果两个字符串相等,则返回0;如果s1字符串在机器排序序列中位于s2字符串的前面,则返回一个负数。
int strncmp(const char* s1, const char* s2, size_t n);
该函数的作用和strcmp()类似,不同的是,该函数在比较n个字符后或遇到第1个空字符时停止比较。
char* strchr(const char* s, int c);
如果s字符串中包含c字符,该函数返回指向s字符串首次出现的c字符的指针(末尾的空字符也是字符串的一部分,所以在查找范围内);如果在字符串s中未找到c字符,该函数则返回空指针。
char* strpbrk(const char* s1, const char* s2);
如果s1字符中包含s2字符串中的任意字符,该函数返回指向s1字符串首位置的指针;如果在s1字符串中未找到任何s2字符串中的字符,则返回空字符。
char* strrchr(const char* s, char c);
该函数返回s字符串中c字符的最后一次出现的位置(末尾的空字符也是字符串的一部分,所以在查找范围内)。如果未找到c字符,则返回空指针。
char* strstr(const char* s1, const char* s2);
该函数返回指向s1字符串中s2字符串出现的首位置。如果在s1中没有找到s2,则返回空指针。
size_t strlen(const char* s);
该函数返回s字符串中的字符数,不包括末尾的空字符
典型例题:
11.9该程序读取输入行,删除存储在字符串中的换行符,如果没有换行符,那么删除剩下的所有行,然后原样输出
闪光点:太多闪光点了,但是不知道怎么总结,见285页的有意思的那一行。
// 感觉挺厉害的
// 1.字符串结束的标志是'\0'.enter输出的是换行符号。fgets()函数却是保留换行符。
// 假设是一个数组要存储ch;fgets(ch,MAX,stdin), 并且此时从键盘输入的是dsfas.但是经过fgets()函数存到数组里面是dsfas\n\0.
#include <stdio.h>
#define MAX 10
int main(void)
{
int i;
char ch[MAX];
puts("请输入一串字符串");
while (fgets(ch, MAX, stdin) != NULL && ch[0] != '\n')//fgets()会保留'\n '\0
{
i = 0;
while (ch[i] != '\n' && ch[i] != '\0')
//判断
i++;//就这个就很优秀。
if (ch[i] == '\n')
ch[i] = '\0';
else
while (getchar() != '\n')
continue;
puts(ch);
//fputs(ch, stdout);//这个函数不会换行
}
puts("结束了 ");
return 0;
}
/*fgets(ch, MAX, stdin);*/
//for (i = 0; i < MAX; i++)
//{
// if (ch[i] == '\n')
// ch[i] = '\0';
// if (ch[MAX - 1] != '\n')//如果没有换行付,那就删除剩下的所有元素
// continue;
//}
2.字符串标准输入,模板
这一节引用最多的get_s()函数,所以写在这里以便于复制
char* get_s(char* ch, int size)
{
char* retu;
int i = 0;
retu = fgets(ch, size, stdin);
if (retu)
{
while (ch[i] != '\n' && ch[i] != '\0')
i++;
if (ch[i] == '\n')
ch[i] = '\0';
else
while (getchar() != '\n')
continue;
}
return retu;
}
3.典型例题
//11.29.c
//问题描述:按照字母表顺序进行排序字符串的实际问题
//一般做法:读取字符串函数,排序字符串,打印出来
// 具体问题:输入二维数组,用指针来表示出来,然后进行排序,最后输出
//
//闪光点:
//1.解决这个问题:
//while (chi < row && get_s(input[chi], line) != NULL && input[chi][0] != '\0')//卧槽尼玛,竟然是&&运算的,如果前一个运算表达式为假,后面就不用考虑。
// //但是为什么while (chi < row &&input[chi][0] != '\0'&& get_s(input[chi], line) != NULL )这样的话,必须要执行20次,很奇怪
//2.理解到了strcmp函数的两个参数的意义。不只是字符串,关键是指向字符串的指针。那为什么字符串也可以输入到strcmp函数里面呢?就是因为字符串也是指针。在一定方面来说。
//
//
#include <stdio.h>
#include <string.h>
#define row 20
#define line 81
#define HALT ""
char* get_s(char * ch, int size1);
void charsort(char* p[], int size);
//主函数
int main(void)
{
int chi=0;
int cho;
char input[row][line];
char *ptr[row];
printf("最多可以输入%d行的字符串\n",row);
printf(" 如果想结束的话,那就换一行结束\n");
//二维数组的输入,并且用指针表示出来
while (chi < row && get_s(input[chi], line) != NULL&&input[chi][0] != '\0' )//卧槽尼玛,竟然是&&运算的,如果前一个运算表达式为假,后面就不用考虑。
//但是为什么while (chi < row &&input[chi][0] != '\0'&& get_s(input[chi], line) != NULL )这样的话,必须要执行20次,很奇怪
{
ptr[chi] = input[chi];
chi++;
}
//二维数组的排序
charsort(ptr, chi);//这他妈的竟然合理了,我塔米牛逼
//二维数组的输出
puts("\n最后的结果是\n");
for (cho = 0; cho < chi; cho++)
puts(ptr[cho]);
return 0;
}
//字符串排序函数
void charsort(char* p[], int size)
{
//采用选择排序的方法。
int top, seek;
char* pt;
for (top = 0; top < size - 1;top++)//假设二维数组有20个一维数组,为什么排序的第一个元素是0-18.原因是最后一个一维数组可以不用排序
for (seek = top+1; seek < size; seek++)//seek=top+1.这里的思想很好,值得记住.因为如果还是从你开始的话,你重复了,虽然可以实行,但是效率太低.
//这里有点不理解为什么p来代表
if (strcmp(p[top], p[seek]) > 0)//按照字母表的顺序来排序
int strcmp(const char* s1, const char* s2);关键是这个函数的两个参数支持指针,而p[top]以及p[seek]都是char *ptr[row];中的元素,都是指向char的指针
{
pt = p[top];
p[top] = p[seek];
p[seek] = pt;
}
}
//字符串输入功能
char* get_s(char* chee, int size1)
{
char* retu;
int i = 0;
retu = fgets(chee, size1, stdin);
if (retu)
{
while (chee[i] != '\n' && chee[i] != '\0')
i++;
if (chee[i] == '\n')
chee[i] = '\0';
else
while (getchar() != '\n')
continue;
}
return retu;
}
4编译练习第三题
//3.设计并测试一个函数,从一行输入中把一个单词读入一个数组中,并丢弃输入行中的其余字符。该函数应该跳过第1个非空白字符前面的所有空白。将一个单词定义为没有空白、制表符或换行符的字符序列。
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define size 10
//1. 测试函数表明,当有子函数里面有多个return语句的时候,当输出一个return语句之后,子函数结束
char* getword(char* dest);
int main(void)
{
char* p;
char ch[size];
puts("请输入一个单词");
while (getword(ch) != NULL)
{
printf("result\n");
puts(ch);
printf("you can enter a word again\n");
}
printf("done\n");
puts(ch);
return 0;
}
char* getword(char* dest)
{
int ch;
int inputnum;//输入的数目个数
while ((ch = getchar()) != EOF && isspace(ch))
{
continue;
}//为了解决字符前面的空格
if (ch == EOF)
return NULL;
else//现在这个位置上面只能是字符
{
*dest++ = ch;
}
while ((ch = getchar()) != EOF && !isspace(ch))
{
*dest++ = ch;
}
//这是遇到空白,或者遇到eof的时候才退出。
*dest = '\0';
//if (ch == EOF)//这两个如果应该是在判断循环结束之后,的判断条件
//{
// return NULL;//我感觉这个写不写好像没有关系。是不是我的错觉
//}
//else
//{
while (getchar() != '\n')
continue;
return dest;
/*}*/
}
5.设计并测试一个函数,从输入中获取n
个字符(包括空白、制表符、换行符),把结果存储在一个数组里,它的地址被传递作为一个参数
//
// 闪光点:str[i] = '\0';这个很关键。当我知道当数组存储一个字符串的时候,他的后面必须是'\0'。
//所以思考puts,以及fpus以及gets和fgets函数后面是否也要加上符号
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define size 10
void getnchar(char str[], int n);
int main(void)
{
char p[size];
getnchar(p, size);
printf("下面是结果\n");
puts(p);
return 0;
}
void getnchar(char str[], int n)
{
int i;
for (i = 0; i < n - 1; i++)
{
str[i] = getchar();
if (isblank(str[i]))
break;
}
str[i] = '\0';
return;
}