文章目录
C程序中,字符串是用双引号括起来的字符序列。
表示字符串
字符串字面量
程序中用双引号括起来的内容称为字符串字面量(string literal),也叫做字符串常量。
字符串常量属于静态存储类别(static storage class)。字符串作为可执行文件的一部分储存在数据段中,当把程序载入内存时,字符串被载入静态存储区。在程序中使用一个字符串常量,该字符串常量就会被存储到静态存储区中:
- 可以使用多次
- 只会被储存一次
- 被视为const数据不可修改
- 在程序的生命期内存在。
指针表示法创建字符串
用双引号括起来的内容被视为指向该字符串储存首字符位置的指针。类似于把数组名作为指向数组首元素的指针。
int main(void) {
printf("%s, %p, %c", "We", "are", *"space farers");
return 0;
}
输出
We, 00AE7B44, s
const char * pt1 = "Something is pointing at me.";
/* 该字符串被存储到静态存储区, 且在末尾存储了一个 \0 来标记字符串结束
然后把字符串首字符位置赋给指针 */
数组表示法创建字符串
// 不指定数组长度
const char m1[] = "If you can't think of anything, fake it.";
/* 编译器自动确定字符串数组大小 */
/* 当把程序载入内存时,也将字符串载入静态存储区。程序在开始运行时才会为数组分配内存,
此时才将储存在静态存储区(static memory)中的字符串拷贝到数组中 。
程序应确保字符数组长度比字符串长度多1(为了存储\0)
此时字符串有两个副本。一个是在静态内存中的字符串常量,另一个是储存在 m1 数组中的字符串 */
// 指定数组长度
const char m2[40] = "Limit yourself to one line's worth.";
/* 要确保数组长度比字符串长度多1,用于存储 \0
所有未被使用的元素都被自动初始化为 \0 */
//创建一个稍后填充的字符数组
char m3[40];
/* 字符数组名和其他数组名一样,是该数组首元素的地址 */
char car[10] = "Tata";
car == &car[0]
*car == 'T'
*(car+1) == car[1] == 'a'
从ANSI C标准起,如果字符串字面量之间没有间隔,或者用空白字符分隔,C会将其视为串联起来的字符串字面量。
char greeting[50] = "Hello, and"" how are" " you"
" today!";
//等价
char greeting[50] = "Hello, and how are you today!";
//如果要在括号内使用引号,需要使用转义序列 \"
printf("\"Run,Spot, run!\" exclaimed Dick.\n");
数组和指针创建字符串的区别
首先,用双引号括起来的字符串字面量都会存储在静态存储区中。
初始化数组把静态存储区的字符串拷贝到数组中,
初始化指针只把静态存储区中·字符串的地址拷贝给指针。
/* 数组表示法创建字符串 */
const char ar1[] = "A copy of the static store.";
/* 数组存储的是静态存储区中字符串常量的副本, 可随意修改 */
/* 指针表示法创建字符串 */
const char * pt1 = "Something is pointing at me.";
/* 指针存储的是静态存储区中字符串常量的首字符地址, 不可通过指针修改字该符串常量 */
字符串数组
#include<stdio.h>
#define SLEN 40
#define LIM 5
int main(void) {
const char *mytalents[LIM] = {
"Adding numbers swiftly",
"Multiplying accurately", "Stashing data",
"Following instructions to the letter",
"Understanding the C language"
};
char yourtalents[LIM][SLEN] = {
"Walking in a straight line",
"Sleeping", "Watching television",
"Mailing letters", "Reading email"
};
int i;
puts("Let's compare talents.");
printf("%-36s %-25s\n", "My Talents", "Your Talents");
for(i = 0; i < LIM; i++)
printf("%-36s %-25s\n", mytalents[i], yourtalents[i]);
printf("\nsizeof mytalents: %zd, sizeof yourtalents: %zd\n",
sizeof(mytalents), sizeof(yourtalents));
return 0;
}
/*
使用一个下标时都分别表示一个字符串, 如 mytalents[0]和yourtalents[0]
使用两个下标时都分别表示一个字符, 如 mytalents[1][2]和yourtalents[1][2]
mytalents 是一个内含5个指针的数组, 在系统中共占用40字节。
yourtalents 是一个内含5个数组的数组, 每个数组内含40个char类型的值,共占用200字节。
mytalents 中的指针指向静态存储区中的字符串字常量的位置
yourtalents 中的数组则储存着静态存储区中字符串字常量的副本 */
字符串输入输出
如果想把一个字符串读入程序,首先必须预留储存该字符串的空间, 然后用输入函数获取该字符串。 可以定义一个字符数组或使用C库函数分配内存空间。
gets() 和 puts()
gets() 和 puts() 常常搭配使用。由于gets()函数无法检查数组是否装得下输入行,因此使用它会造成安全隐患,C11标准委员会已经将其废除,尽量不要使用。
char *gets(char *str);
/* 从标准输入流(stdin)读取一行输入, 直到遇到换行符或文件结尾停止, 读取并丢弃遇到的换行符。
然后将读取到的字符存入str指向的内存中, 并在末尾添加一个 \0 使其成为一个 C 字符串。
成功读取则返回str, 读取出错或读取时文件为空, 则返回一个空指针(null pointer) */
int puts(const char *str);
/* 将str指向的字符串输出到标准输出流(stdout), 并额外输出一个换行符
执行成功返回非负数。发生错误返回EOF */
char str[100];
printf("%d\n",puts(gets(str)));
#include <stdio.h>
#define STLEN 81
int main(void) {
char words[STLEN];
puts("Enter a string, please.");
gets(words); //读取一行字符,丢弃换行符
printf("Your string twice:\n");
printf("%s\n", words);
puts(words); // 输出字符串并额外输出一个换行符
puts("Done.");
return 0;
}
fgets() 和 fputs()
char *fgets(char *str, int n, FILE *stream)
/* 从指定的流 stream 读取一行
当读取(n-1)个字符时, 或者读取到换行符时(读取并保留), 或者到达文件末尾时, 停止读取。
然后将读取到的字符存入str指向的内存中, 并在末尾添加一个 \0 使其成为一个 C 字符串
成功读取返回 str, 读取出错或读取时文件为空,则返回一个空指针 null pointer
fgets()会读取并保留换行符, 而gets()会读取并丢掉换行符 */
int fputs(const char * str, FILE * stream);
/* 将 str 指向的字符串输出到 stream 流
成功执行返回一个非负值, 发生错误则返回 EOF(-1)
fputs()不会额外输出一个换行符,
puts()会额外输出一个换行符 */
fgets()函数的第3个参数指明要读入的文件。 如果要读入从键盘输入的数据, 应以stdin(标准输入)作为参数。
fputs()函数的第2个参数指明要写入的文件。 如果要显示在显示器上, 应使用 stdout(标准输出) 作为该参数。
空字符和空指针
- 都可以用数值0来表示
- 空字符是整数类型, 空指针是指针类型
- 空字符是一个字符, 占1字节
空指针是一个地址, 占4字节 - 空字符用于标记字符串结尾
- 函数返回空指针表示某些特殊情况发生,如遇到文件结尾或未能按预期执行。
//读取多行输入行, 删除储存在字符串中的换行符, 如果没有换行符, 则丢弃一行内多余字符
#include <stdio.h>
#define STLEN 10
int main(void) {
char words[STLEN];
int i;
puts("Enter strings (empty line to quit):");
while(fgets(words, STLEN, stdin) != NULL && words[0] != '\n') {
i = 0;
while(words[i] != '\n' && words[i] != '\0')
i++;
if (words[i] == '\n')
words[i] = '\0';
else // 如果word[i] == '\0'则执行这部分代码
while (getchar() != '\n')
continue;
puts(words);
}
puts("done");
return 0;
}
/* s_gets.c --
读取一行输入行, 删除储存在字符串中的换行符, 如果没有换行符, 则丢弃一行内多余字符 */
char * s_gets(char * st, int n) {
char * ret_val;
int i = 0;
ret_val = fgets(st, n, stdin);
if (ret_val) // 即,ret_val != NULL
{
while(st[i] != '\n' && st[i] != '\0')
i++;
if(st[i] == '\n')
st[i] = '\0';
else
while(getchar() != '\n')
continue;
}
return ret_val;
}
gets_s()
char *gets_s( char *str, rsize_t n );
https://cloud.tencent.com/developer/section/1009156
自定义输入输出函数
void put1(const char * string) {
while (*string)
putchar(*string++);
}
char *get1(char *str, int length){ }
string.h 函数
C库提供了多个处理字符串的函数, ANSI C把这些函数的原型放在 string.h头文件中。
strlen() 求字符串长度
size_t strlen(const char *str);
/* 返回字符串的长度, 不包括空字符 */
strcat() 拼接字符串
char *strcat(char *dest, const char *src);
/* 把 src 指向的字符串追加到 dest 指向的字符串的末尾, 直到遇到空字符, 并在末尾添加 \0
从dest 的 \0 的位置开始追加
返回dest
strcat()无法检查 dest 剩余内存是否能容纳src,有安全隐患 */
char *strncat(char *dest, const char *src, size_t n);
/* 把 src 指向的字符串追加到 dest 指向的字符串的末尾, 直到 n 个字符或遇到空字符为止, 并在末尾添加 \0
从dest 的 \0 的位置开始追加
返回dest */
strcmp() 比较字符串
int strcmp(const char *str1, const char *str2);
/* 把 str1 所指向的字符串和 str2 所指向的字符串进行比较
1. 如果返回值小于 0, 则表示 str1 小于 str2
2. 如果返回值大于 0, 则表示 str1 大于 str2
3. 如果返回值等于 0, 则表示 str1 等于 str2 */
int strncmp(const char *str1, const char *str2, size_t n);
/* 把 str1 和 str2 进行比较,最多比较前 n 个字符 */
strcpy() 拷贝字符串
char *strcpy(char *dest, const char *src);
/* 把 src 指向的字符串复制到 dest, 并在末尾添加一个 \0, src 的内容将覆盖 dest 的内容
返回 dest
不能检查 dest 指向的内存能否容纳 src , 有安全隐患 */
char *strncpy(char *dest, const char *src, size_t n)
/* 把 src 指向的字符串复制到 dest, 复制 n 个字符或遇到 \0 停止, 并在末尾添加 \0
返回dest */
strchr() 查找字符首次出现的位置
char *strchr(const char * str, int c);
/* 在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置。
返回在字符串 str 中第一次出现字符 c 的位置,如果未找到该字符则返回 NULL */
strrchr() 查找字符最后一次出现的位置
char *strrchr(const char *str, int c);
/* 在 str 所指向的字符串中搜索最后一次出现字符 c 的位置
返回 str 中最后一次出现字符 c 的位置, 如果未找到该值,则函数返回 NULL */
strpbrk() 公共字符第一次出现的位置
char *strpbrk(const char *str1, const char *str2);
/* 检索字符串 str1 中第一个匹配字符串 str2 中任意字符的字符, 不包含 \0
返回 str1 中第一个匹配字符串 str2 中任意字符的字符位置, 如果未找到字符则返回 NULL */
strstr() 子字符串匹配
char *strstr(const char *haystack, const char *needle);
/* 在字符串 haystack 中查找第一次出现字符串 needle 的位置, 不包含 \0
返回在 haystack 中第一次出现 needle 字符串的位置, 如果未找到则返回 NULL */
字符串比较
1 两个字符串自左向右逐个按 ASCII 值大小相比
2 直到出现差异,ASCII 码大的字符串大
3 或者其中一个字符串先结束,先结束的字符串小
"A"<"B"
"A"<"AB"
"Apple"<"Banana"
"A"<"a"
"compare"<"computer"
字符串排序
#include <stdio.h>
#include <string.h>
#define SIZE 81 /* 限制字符串长度,包括 \0 */
#define LIM 20 /* 可读入的最多行数 */
#define HALT "" /* 空字符串停止输入 */
void stsrt(char *strings[], int num); /* 字符串排序函数 */
char * s_gets(char * st, int n);
int main(void)
{
char input[LIM][SIZE]; /* 储存输入的数组 */
char *ptstr[LIM]; /* 内含指针变量的数组 */
int ct = 0; /* 输入计数 */
int k; /* 输出计数 */
printf("Input up to %d lines, and I will sort them.\n", LIM);
printf("To stop, press the Enter key at a line's start.\n");
while (ct < LIM && s_gets(input[ct], SIZE) != NULL && input[ct][0] != '\0')
{
ptstr[ct] = input[ct]; /* 设置指针指向字符串 */
ct++;
}
stsrt(ptstr, ct); /* 字符串排序函数 */
puts("\nHere's the sorted list:\n");
for (k = 0; k < ct; k++)
puts(ptstr[k]); /* 排序后的指针 */
return 0;
}
/* 字符串-指针-排序函数 */
void stsrt(char *strings[], int num)
{
char *temp;
int top, seek;
for (top = 0; top < num - 1; top++)
for (seek = top + 1; seek < num; seek++)
if (strcmp(strings[top], strings[seek]) > 0)
{
temp = strings[top];
strings[top] = strings[seek];
strings[seek] = temp;
}
}
/* 读取字符串 */
char * s_gets(char * st, int n)
{
char * ret_val;
int i = 0;
ret_val = fgets(st, n, stdin);
if (ret_val)
{
while (st[i] != '\n' && st[i] != '\0')
i++;
if (st[i] == '\n')
st[i] = '\0';
else
while (getchar() != '\n')
continue;
}
return ret_val;
}
/* 输入:
O that I was where I would be,
Then would I be where I am not;
But where I am I must be,
And where I would be I can not.
*/
ctype.h 函数
命令行参数
/* repeat.c -- 把命令行参数转换为数字 */
#include <stdio.h>
#define STLEN 81
int main(int argc, char *argv []) {
int count;
printf("The command line has %d arguments:\n", argc - 1);
for (count = 1; count < argc; count++)
printf("%d: %s\n", count, argv[count]);
printf("\n");
return 0;
}
//将程序编译成可执行文件 repeat.exe
命令行(command line) 是在命令行环境中, 用户为运行程序输入命令的行。Win10运行 repeat. exe 的命令行:
C:\Users\liwei\Desktop>repeat
命令行参数(command-line argument) 是同一行的附加项。 一个C程序可以读取并使用这些附加项:
C:\Users\liwei\Desktop>repeat Resistance is futile
The command line has 3 arguments:
1: Resistance
2: is
3: futile
C编译器允许main()没有参数或者有两个参数(一些实现允许main()有更多参数, 属于对标准的扩展) 。
main()有两个参数时, 第1个参数是命令行中的字符串数量。 这个int类型的参数被称为argc(表示参数计数 argument count)。
系统用空格分隔字符串。 该程序把命令行字符串储存在内存中, 并把每个字符串的地址储存在指针数组中。 而该数组的地址则被储存在 main()的第 2 个参数中。 这个指向指针的指针称为argv(表示参数值 argument value)。 如果系统允许(一些操作系统不允许这样), 就把程序本身的名称赋给 argv[0], 然后把随后的第1个字符串赋给argv[1]。
许多环境(包括UNIX和DOS) 都允许用双引号把多个单词 括起来形成一个参数。 C:\Users\liwei\Desktop>repeat " I am hungry " now
这行命令把字符串 “I am hungry” 赋给argv[1],把 “now” 赋给argv[2]。
将字符串转换为数字
printf()通过转换说明, 把数字从数值形式转换为字符串形式
scanf()通过转换说明,把字符串转换为数值形式
sprintf() 和 sscanf()
int sprintf(char *str, const char *format, ...);
/* 格式化输出到字符串 str, 格式说明与printf() 一样 */
int sscanf(const char *str, const char *format, ...);
/* 从字符串 str 读取格式化输入, 格式说明与scanf()一样 */
// 位于<stdio.h>
#include<stdio.h>
int main(void) {
char str1[50]="";
sprintf(str1, "PI 的值 = %f", 3.1415926);
puts(str1);
int n;
char str2[50]="9527";
sscanf(str2,"%d",&n);
printf("n = %d",n);
return 0;
}
atoi() aotl() aotf()
int atoi(const char *str);
/* 把参数 str 所指向的字符串转换为一个整数(类型为 int 型)
返回转换后的双精度浮点数, 如果没有执行有效的转换, 则返回零 */
long int atol(const char *str);
/* 把参数 str 所指向的字符串转换为一个长整数(类型为 long int 型)
返回转换后的双精度浮点数, 如果没有执行有效的转换,则返回零 */
double atof(const char *str);
/* 把参数 str 所指向的字符串转换为一个浮点数(类型为 double 型)
返回转换后的双精度浮点数, 如果没有执行有效的转换,则返回零(0.0) */
// 这三个函数处理以数字或小数点(strtod().)的字符串, 如果字符串以其它字符开头, 返回0/0.0
// 位于<stdlib.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
puts("atoi() -----------------------------------");
int val1;
char str1[20]="93489";
val1 = atoi(str1);
printf("字符串值 = %s, 整型值 = %d\n",str1, val1);
puts("atol() -----------------------------------");
long val2;
char str2[20]="93489ABCD";
val2 = atol(str2);
printf("字符串值 = %s, 长整型值 = %ld\n", str2, val2);
puts("atof() -----------------------------------");
float val3;
char str3[20]="93.489=&-";
val3 = atof(str3);
printf("字符串值 = %s, 浮点值 = %f\n", str3, val3);
return 0;
}
strtol() strtoul() strtod()
long int strtol(const char *str, char **endptr, int base);
/* 把参数 str 所指向的字符串根据给定的 base 转换为一个长整数(类型为 long int 型)
endptr -- 对类型为 char* 的对象的引用,其值由函数设置为 str 中读取完数值后的下一个字符
base -- 基数, 必须介于 2 和 36(包含)之间,或者是特殊值 0。
返回转换后的长整数, 如果没有执行有效的转换,则返回零 */
unsigned long int strtoul(const char *str, char **endptr, int base);
/* 把参数 str 所指向的字符串根据给定的 base 转换为一个无符号长整数(类型为 unsigned long int 型)
endptr -- 对类型为 char* 的对象的引用, 其值由函数设置为 str 中读取完数值后的下一个字符
base 必须介于 2 和 36(包含)之间, 或者是特殊值 0
返回转换后的无符号长整数, 如果没有执行有效的转换,则返回一个零 */
double strtod(const char *str, char **endptr);
/* 把参数 str 所指向的字符串转换为一个浮点数(类型为 double 型)
endptr -- 对类型为 char* 的对象的引用, 其值由函数设置为 str 中读取完数值后的下一个字符
返回转换后的双精度浮点数,如果没有执行有效的转换,则返回零(0.0) */
// 位于<stdlib.h>
#include<stdio.h>
#include <stdlib.h>
int main(void) {
puts("strtol() -----------------------");
char str1[30] = "20221130 This is test";
char *ptr1;
long ret1;
ret1 = strtol(str1, &ptr1, 10);
printf("数字(无符号长整数)是 %ld\n", ret1);
printf("字符串部分是 |%s|\n", ptr1);
puts("strtoul() ----------------------");
char str2[30] = "20221130 This is test";
char *ptr2;
long ret2;
ret2 = strtoul(str2, &ptr2, 10);
printf("数字(无符号长整数)是 %lu\n", ret2);
printf("字符串部分是 |%s|\n", ptr2);
puts("strtod() -----------------------");
char str3[30] = "20.221130 This is test";
char *ptr3;
double ret3;
ret3 = strtod(str3, &ptr3);
printf("数字(double)是 %f\n", ret3);
printf("字符串部分是 |%s|\n", ptr3);
return(0);
}
把命令行参数转换为数字
/* repeat.c -- 把命令行参数转换为数字 */
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv []) {
int i, times;
if (argc < 2 || (times = atoi(argv[1])) < 1)
printf("Usage: %s positive-number\n", argv[0]);
else for (i = 0; i < times; i++)
puts("Hello, good looking!");
return 0;
}
C:\Users\liwei\Desktop>repeat 3
itoa() ftoa()
许多实现使用 itoa()和 ftoa()函数分别把整数和浮点数转换成字符串。 但是这两个函数并不是 C标准库的成员, 可以用sprintf()函数代替它们, 因为sprintf()的兼容性更好。