《C Primer Plus》 学习笔记系列之(二)
第11章 字符串和字符串函数
下面的程序使用了gets()和puts(), 以及字符指针的用法:
#include<stdio.h>
#define LIM 5
#define LINELEN 81 //最大字符串长度
#define MSG "You must have many talents.Tell me more." //定义字符串常量
int main()
{
char name[LINELEN];
char talents[LINELEN];
const char m2[] = "If you can't think of anything, fake it'";
const char *m3 = "\nEnough about me - what's your name?"; //初始化一个指针
const char * mytal[LIM] = { //mytal[1] mytal[2] mytal[3] mytal[4] mytal[5] 分别指向不同的字符串
"Multiplying accurately", "stashing data", "Adding numbers swiftly", "Following instructions to the letter",
"Understanding the c language"
};
int i;
for (i = 0; i < LIM; i++)
{
puts(mytal[i]); //使用puts()输出字符串
}
puts(MSG);
puts("Your name:");
gets(name);
puts("Yours more talents:");
gets(talents);
printf("%s\n%s\n", name, talents);
return 0;
}
#include<stdio.h>
int main()
{
char title[30];
while (gets(title) != NULL && title[0] != '\0')
{
puts(title);
}
return 0;
}
gets()试图超过文件结尾读取字符,这个表达式的值为NULL。如果用户在输入行的开始就按了回车键,将输入空字符串,以结束循环。
字符串常量属于静态存储(static storage)类。静态存储是指如果在一个函数中使用字符串常量,即使是多次调用了这个函数,该字符串在程序的整个运行过程中只存储一份。
整个引号中的内容作为指向该字符串存储位置的指针。这点与把数组名作为指向数组存储位置的指针类似。下面的程序可以证明这一点:
#include<stdio.h>
int main()
{
//整个字符串的内容,"Jack Chou"是作为指向该字符串存储位置的指针
printf("%s, %p, %c\n", "Hello", "Jack Chou", *"Jack Chou");
return 0;
}
//运行后的结果:
Hello, 0x80484fc, J
数组与指针的区别:
在指定字符数组大小的时候,一定要确保数组元素比字符串长度至少多1(多出来的一个元素用于容纳空字符)。未被使用的元素均被自动初始化为0。这里
的0是char形式的空字符,而不是数字0。
通常,被引用的的字符串存储在可执行文件的数据段部分;当程序被加载到内存中时,字符串也被加载到内存中。被引用的字符串被称为位于静态存储区。
但是在程序开始运行后才为数组分配存储空间。这时候,把被引用的字符串复制到数组中。
char m3[] = “Enough about me - what’s your name?”
char *m3 = “Enough about me - what’s your name?”
在数组形式中,m3是一个地址常量,不允许使用++m3,增量运算符只能用在变量名前,而不能用在常量前。
指针形式(*m3),这个变量初始时指向字符串的第一个字符,++m3则指向第二个字符。
总之,数组初始化是从静态存储区把一个字符串复制给数组,而指针初始化只是复制字符串的地址。
只有指针才可以使用增量运算符:
char * head = “I Love Dog!”;
while ( *(head) != ‘\0’)
{
putchar(*(head++));
}
字符串的输入:
gets()函数,如果输入顺利,它返回的是读入字符串的地址;如果出错或gets()遇到文件结尾,它就返回一个空(或0)地址。
这个地址被称为空指针, 并用stdio.h里定义的常量NULL来表示。所以:
while (gets(name) != NULL){}
对于getchar()
while ((ch = getchar()) != EOF){}
gets()的一个不足是它不检查预留存储区能够容纳实际输入的数据。多出来的字符简单的溢出到相邻的内存区。
fgets()函数改进了这个问题,它需要三个参数:
#include<stdio.h>
#define MAX 20
int main()
{
char name[MAX];
char * ptr;
ptr = fgets(name, MAX, stdin); //做多读取MAX-1个字符,或读完一个换行符为止,stdin说明读取的是键盘数据。
printf("%s, %s hello", name, ptr);
return 0;
}
fgets()读取到换行符,会把它存到字符串里,不会像gets()函数那样把它丢掉。
对于scanf()和gets(),scanf()使用两种方法决定输入结束:
输入的字符串都是以遇到的第一个非空白字符开始。如果使用%s格式,字符串读到(但不包括)下一个空白字符(比如空格、制表符或换行符)。
如果指定了字段宽度,比如%10s,scanf()会读入10个字符或直到遇到第一个空白字符。scanf()返回一个成功读取项目的一个数值,或当遇到文件结束时返回一个EOF。
#include<stdio.h>
int main()
{
float cost;
scanf("%f", &cost);
return 0;
}
/*
运行这段代码,在心显示器的终端输入
12.50[enter]
会传送下面的字符序列:
12.50\n
scanf()函数读入了1 2 . 5 0,把\n留在了输入流中,等待下一个读入语句处理。
按下回车键后,12.50\n在缓冲区中,scanf()函数将缓冲区中的12.50给读走了,剩下'\n'
于是可以使用下面这段代码来清空缓冲区:
while (getchar() != '\n')
{
continue;
}
*/
字符串的输出:
c有三个用于输出字符串的标准库函数:puts()、fputs()、printf()
对于puts()函数,只要给出字符串参数的地址,就可以输出字符串。
fput()需要第二个参数说明要写的文件。可以使用stdout作为参数进行输出。
char line[81];
while(fgets(line, 81, stdin))
{
fputs(line, stdout);
}
puts()和gets()一起使用而设计的,
fputs()和fgets()一起使用而设计。
自定义字符串的输出:
#include<stdio.h>
void put1(const char * string) //这个函数不用并不改变字符串,所以使用const修饰符
{
while(*string != '\0') //while(*string)
{
putchar(*string++); //++比*的优先级高
}
}
字符串函数
c库提供了许多处理字符串的函数,ANSI C用头文件string.h给出这些函数的原型。
strlen()
得到字符串的长度
strcat()
接收两个字符串参数,它将第二个字符串的一份拷贝添加到第一个字符串的结尾,从而使第一个字符串成为一个新的组合字符串,
而第二个字符串没有改变。
char flower[50] = “Rose”;
char addon[] = “s smell like old shoes”;
strcat(flower, addon);
puts(flower); //将输出连接后的字符串
strncat()
strncat(flower, addon, 10); //把addon字符串中的内容添加到flower上,直到加到10个字符或空字符为止。
strcmp()
接收两个字符串参数,如果两个字符串参数相同,就返回0。
strcmp(flower, addon);
strncmp()
可以比较到字符串的不同处,也可以比较完由第三个参数指定的字符数。
如下面的程序如果想搜索以”astro”开头的字符串,可以限定搜索前5个字符
char * list[5] = {"astronomy", "astrounding", "astrophysics", "ostracize", "asterism"};
int count = 0;
int i;
for (i = 0; i < 5; i++)
{
if (strncmp(list[i], "astro", 5) == 0) //指定比较前5个字符
{
printf("Found:%s\n", list[i]);
count++;
}
}
printf("The list contained %d words beginning with astro.\n", count);
strcpy()
复制字符串,在字符串运算中的作用等价于赋值运算符
char qwords[] = “this is a test.”;
char temp[30];
strcpy(temp, qwords); //strcpy(temp, “Hi, jack”); //根据赋值语句的顺序,左边目标字符串在左边
声明一个数组将为数据分配存储空间,而声明一个指针只为一个地址分配存储空间。
strcpy()是char *类型,它返回的是第一个参数的值,及一个字符的地址。
见下面的程序:
#include<stdio.h>
#include<string.h>
#define WORDS "beast"
#define SIZE 40
int main()
{
char * orig = WORDS;
char copy[SIZE] = "Be the best that you can be.";
char * ps;
puts(orig);
puts(copy);
ps = strcpy(copy + 7, orig); //ps指向copy的第8个元素,索引为7.orig中的'\0'正好把that中的't'给覆盖
puts(copy); //遇到了前面被复制进去的空字符'\0',空字符就标志着字符串的真正结束。所以就不输出copy后面的内容了
puts(ps); //ps指向copy的第8个元素
return 0;
}
strncpy()
需要使用第三个参数来指明最大可复制的字符数
头文件stdio.h支持的sprintf()函数
写到字符串里,而不是输出到屏幕显示。
选择法排序(字符串排序):
使用选择法进行排序,下面是一个程序实例:
#include<stdio.h>
int main()
{
//选择法排序
int arr[10] = {10, 9, 3, 5, 8, 1, 2, 4, 0, 6};
int i, j, temp;
for (i = 0; i < 9; i++)
{
for (j = i + 1; j < 10; j++)
{
if (arr[i] > arr[j]) //以从小到大的顺序进行排序
{
temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
}
}
//输出那个数组
int k;
for (k = 0; k < 10; k++)
{
printf("%d\t", arr[k]);
}
putchar('\n');
return 0;
}
命令行参数
看一个例子:
#include<stdio.h>
int main(int argc, char *argv)
{
int count;
printf("The commnd line has %d arguments.\n", argc - 1);
printf("The command is:\n");
for (count = 1; count < argc; count++)
{
printf("%s\n", argv[count]); //%s需要提供字符串的地址作为参数
}
return 0;
}
argc是整型数,其值是命令行的单词个数;argv是一个指针,指向一个char指针数组。
还没有评论,来说两句吧...