题目
# include<stdio.h>int main()
{
printf("%s, %p, %c\n", "We", "are", *"space farers");
return 0;
}
上面程序的输出是什么?
参考答案
jiaming@jiaming-System-Product-Name:/tmp$ ./strptr.oWe, 0x56518dbe4704, s
说明
printf() 根据 %s 转换说明打印 We,根据 p 转换说明打印一个地址, *"space farers" 表示该字符串所指向地址上储存的值,应该是字符串 space farers 的首字符。
文章目录
- 1. 在程序中定义字符串
- 1.1 字符串字面量(字符串常量)
- 1.2 字符串数组和初始化
- 1.3 数组和指针
- 1.4 数组和指针的区别
- 1.5 字符串数组
- 1.6 指针和字符串
- 2. 字符串输入
- 2.1 分配空间
- 3. 自定义输入/输出函数
- 字符串函数
- 0. scanf()函数
- 1. gets()函数
- 2. gets_s()函数
- 3. fgets() 函数
- 4. puts() 函数
- 5. fputs() 函数
- 6. printf() 函数
- 7. strlen() 函数
- 8. strcat() 函数
- 9. strncat() 函数
- 10. strcmp() 函数
- 11. strncmp() 函数
- 12. strcpy() 函数
- 13. strncpy() 函数
- 14. sprintf() 函数
- 其他字符串函数
1. 在程序中定义字符串
1.1 字符串字面量(字符串常量)
// 双引号中的字符会被编译器自动加入'\0', 存储在内存中,存储在字符常量区,静态存储char greeting1[50] = "Hello, and"" how are"" you" " today!"; // ANSI C 标准视为串联起来的字符串字面量
1.2 字符串数组和初始化
定义字符串数组时,需要让编译器知道需要多少空间:
const char m2[40] = {'L', 'i', 'm', 'i', 't', ' ', 'y', 'o', 'u', 'r', 's', 'e', 'l', 'f', ' ', 't', 'o', ' ', 'o', 'n', 'e', ' ', 'l', 'i', 'n', 'e', "'", 's', ' ', 'w', 'o', 'r', 't', 'h', '.'};
const char m3[] = "Limit yourself to one line's worth."; // 编译器会根据字符串末尾的空字符自动计算数组的大小,只能用在数组初始化时
int n = 8;
char crumbs[n]; // C99 标准之前无效,C99 标准之后这种数组是变长数组
1.3 数组和指针
const char * pt1 = "Something is pointing at me." // 字符串字面量被视为const数据,由于pt1指向这个const数据,所以应该把pt1声明为指向const数据的指针。不能改变指向的数据,但是可以改变指向。const char ar1[] = "Something is pointing at me." // 把字符串字面量拷贝给数组,如果没有 const 限定,可以随意更改数据。
- 指针形式(*pt1)也使得编译器为字符串在静态存储区预留 29 个元素的空间。一旦开始执行程序,它会为指针变量 pt1 留出一个储存位置,并把字符串的地址储存在指针变量中,该变量最初指向该字符串的首字符,但是它的值可以改变,因此,可以使用递增运算符。
- 数组形式在计算机内存中分配为一个内含 29 个元素的数组(每个元素对应一个字符,还加上一个末尾的空字符),每个元素被初始化为字符串字面量对应的字符。字符串作为可执行文件的一部分储存在数据段中。当把程序载入内存时,也载入了程序中的字符串。字符串储存在静态存储区,但是,当程序开始运行时才会为该数组分配内存。此时字符串有两个副本,一个是在静态内存中的字符串字面量,另一个是储存在 ar1 数组中的字符串。
char arr[] = MSG;
const char* pt = MSG; // pt 和 MSG的地址相同
1.4 数组和指针的区别
char * head = "Something is pointing at me." // 字符串变量char head[] = "Something is pointing at me." // 字符串常量
建议把指针初始化为字符串字面量时使用 const 限定符:
const char* p = "Klingon"; // 推荐用法1.5 字符串数组
// 含有5个指针的数组,40B,指针指向初始化时所用的字符串字面量的位置,这些字符串字面量被储存在静态内存中
const char *mytalents[LIM] = {
"Adding numbers swiftly",
"Multiplying accurately",
"Stashing data",
"Following instructions to the letter",
"Understanding the C language"
};
// 含有5个数组的数组 5x40=200B,数组则储存着字符串字面量的副本,所以每个字符串都被储存了两次
char yourtalents[LIM][SLEN] = {
"Walking in a strarght line",
"Sleeping",
"Watching television",
"Mailing letters",
"Reading email"
};
1.6 指针和字符串
字符串的绝大多数操作都是通过指针完成的。
2. 字符串输入
2.1 分配空间
char *name;scanf("%s", name); // 未初始化指针
char name[80]; // 初始化空间
3. 自定义输入/输出函数
不一定非要使用 C 库中的标准函数,如果无法使用这些函数或者不想用它们,完全可以在 getchar() 和 putchar()的基础上自定义所需的函数。
字符串函数
0. scanf()函数
scanf() 配合 %s 只能读取一个单词。
如果使用 %s 转换说明,以下一个空白字符(空行、空格、制表符和换行符)作为字符串的结束(字符串不包括空白字符),如果指定了字段宽度,如 %10s,那么 scanf() 将读取 10 个字符或读到第 1 个空白字符停止。
1. gets()函数
读取整行输入,直到遇到换行符,然后丢弃换行符,储存其余字符,自动添加空字符,但是它无法检查数组是否装得下输入行,gets() 函数只知道数组的开始处,并不知道数组中有多少个元素。
**如果输入的字符串过长,会导致缓冲区溢出,即多余的字符超出了指定的目标空间。**如果这些多余的字符只是占用了尚未使用的内存,就不会立即出现问题;如果它们擦写掉程序中的其他数据,会导致程序异常终止。过去,有些人通过系统编程,利用 gets() 插入和运行一些破坏系统安全的代码。制定 C99 标准的委员会将摒弃 gets() 的建议放入了标准,承认了 gets() 的问题并建议不使用它。
Segmentation fault. Unix 系统中,这条消息说明该程序试图访问未分配的内存。
gets() 替代品:
- fgets()
- gets_s()
- s_gets()
2. gets_s()函数
和 fgets() 的区别:
- gets_s() 只从标准输入中读取数据,没有第三个参数。
- gets_s() 读到换行符,会丢弃,非存储。
- 如果 gets_s() 读到最大字符数都没有读到换行符,会执行以下几步。首先把目标数组中的首字符设置为空字符,读取并丢弃随后的输入直至读到换行符或文件结尾,然后返回空指针,接着调用依赖实现的处理函数,以中止或退出程序。
3. fgets() 函数
通过第 2 个参数限制读入的字符数来解决溢出的问题,专门用于处理文件输入,和 gets() 的区别:
- fgets() 第 2 个参数指明了读入字符的最大数量,如果该参数的值为 n,那么 fgets() 将读入 n-1 个字符,或者读到遇到的第一个换行符为止。
- 如果 fgets() 读到一个换行符,会把它存储在字符串中,而非丢掉。
- 第 3 个参数指定了要读入的文件,如果读入从键盘输入的数据,则以 stdin 作为参数。
fgets(words, 10, stdin); // 输入 abc\n, words="abc\n\0";
puts(words);
空字符和空指针:
空字符是整数类型,可以用 0 来表示,一个字符,占 1 个字节。
空指针是指针类型,可以用 0 来表示,一个地址,通常 4 个字节。
4. puts() 函数
puts() 在显示字符串时会自动在其末尾添加一个换行符,该函数在遇到空字符时就停止输出。
char c[] = {'a', 'b', 'c', '\0'};puts(c); // puts("abc") 只需要把字符串的地址作为参数传递给它即可,确保有空字符,否则不知道何时结束
5. fputs() 函数
是 puts() 函数针对文件定制的版本:
- fputs 函数的第 2 个参数指明要写入数据的文件,如果要打印在显示器上,可以stdout作为参数。
- fputs() 不会在输出的末尾添加换行符。
- fgets() 保留输入中的换行符,fputs() 不在输出中添加换行符。
6. printf() 函数
与 puts() 不同的是,printf() 不会自动在每个字符串末尾加上一个换行符,计算机执行的时间更长。
7. strlen() 函数
统计字符串的长度。
8. strcat() 函数
用于拼接字符串,接受两个字符串作为参数。接受两个字符串作为参数,把第 2 个字符串的备份附加在第 1 个字符串末尾,并把拼接后形成的新字符串作为第 1 个字符串,第 2 个字符串不变。
9. strncat() 函数
strcat() 函数无法检查第 1 个数组能否容纳第 2 个字符串。
strncat(bugs, addon, 13) 把 addon 字符串的内容附加给 bugs,在加到第 13 个字符或遇到空字符时停止。
10. strcmp() 函数
比较的是字符串,非字符,可以比较储存在不同大小数组中的字符串。
11. strncmp() 函数
可以比较两个字符串的不同字符位置。
12. strcpy() 函数
strcpy() 接受两个字符串指针作为参数,可以把指向源字符串的第 2 个指针声明为指针、数组名或字符串常量;而指向源字符串副本的第 1 个指针指向一个数据对象,且该对象有足够的空间储存源字符串副本。
13. strncpy() 函数
更谨慎的选择 strncpy(),该函数的第 3 个参数指明可拷贝的最大字符数。strncpy(target, source, n),把 source 中的 n 个字符或空字符之前的字符拷贝至 target 中。
14. sprintf() 函数
该函数申明在 stdio.h 中,而不是在 string.h 中,该函数和 prinf() 函数类似,但是它是把数据写入字符串,而不是打印在屏幕上。
其他字符串函数
- char *strcpy(char * restrict s1, const char * restrict s2);
- char *strncpy(char * restrict s1, const char * strict s2, size_t n)
- char *strcat(char * restrict s1, const char * restrict s2);
- char *strncat(char * restrict s1, const char * restrict s2, size_t n);
- int strcmp(const char * s1, const char * s2);
- char *strchr(const char *s, int c);
- char *strpbrk(const char * s1, const char * s2);
- char *strrchr(const char * s, int c);
- char *strstr(const char * s1, const char * s2);
- size_t strlen(const char *s);