12. 字符串
一、字符串字面量
字符串字面量(string literal)或 字符串常量(string constant)是一个或多个字符的序列,它使用一对双引号包裹起来。编译器会自动在双引号的后面添加 '\0' 作为字符串的结束标志。
printf("hello world\n");
从 ANSI C 标准起,如果字符串字面量之间没有间隔,或者使用空白字符分隔,C 会将其视为串联起来的字符串字面量。例如:
printf("Hello, and"" how are" "you"
" today!\n");
与下面的代码等价:
printf("Hello, and how are you today!\n");
字符串常量属于 静态存储类别(static storage class),这说明如果在函数中使用字符串常量,该字符串只会被储存一次,在整个程序的声明周期内存在,及时函数被调用多次。用双引号括起来的内容被视为指向该字符串储存位置的指针。
#include <stdio.h>
int main(void)
{
printf("%s, %p, %c\n", "We", "are", *"space farets");
return 0;
}
printf() 根据 %s 转换说明打印 We,根据 %p 转换说明打印一个地址。因此,如果 "are" 代表一个地址,printf() 将打印该字符串首地址的地址。最后,*"space farers" 表示该字符串指向地址上存储的值,应该是字符串 *"space farers" 的首字符。
二、字符串的初始化
在 C 语言中,我们可以使用 字符数组 来保存字符串,也就是使用一个一维字符数组保存字符串中的每一个字符。此时,编译器会自动为其添加 '\0' 作为结束符。在定义字符数组时,必须让编译器知道需要多少空间。在指定字符数组大小时,要确保数组的元素个数至少比字符串长度多 1(为了容纳空字符)。所有未被使用的元素都被自动初始化为 0(这里的 0 指的是 '\0',而不是数字字符 0)
char str[15] = "hello world!";
我们还可以省略数组初始化声明中的大小,编译器会自动计算字符数组的大小。
char str[] = "hello world!";
让编译器计算数组的大小只能用在初始化数组时。如果创建一个稍后再填充的数组,就必须在声明时指定大小。声明数组时,数组大小必须是可求值的整数。在 C99 新增变长数组之前,数组的大小必须是整型常量,包括有整型常量组成的表达式。
通常,字符串都作为可执行文件的一部分储存在数据段中。当把程序载入内存是,也载入程序中的字符串。字符串储存在 静态内存区(static memory)。但是,程序在开始运行时才会为该数组分配内存。此时,才将字符串拷贝到数组中。注意,此时字符串有两个副本。一个是静态内存中的字符串字面量,另一个是储存在字符数组中的字符串。
此后,编译器便将数组名识别为该数组首元素地址的别名。在数组形式中,数组名是 地址常量,不能更改数组名所对应的值,如果更改了数组名所对应的值,则意味着改变了数组的存储位置(即地址)。我们可以进行类似数组名 + 1 的操作,表示数组的下一个元素。
我们也可以使用字符指针的方式来定义字符串;C 语言对字符串常量是按字符数组处理的,在内存中开辟了一个字节数组用来存放字符串常量,程序在定义字符串指针变量时只是把字符串首地址(即存放字符串的字节数组的首地址)赋给指针变量。该变量最初指向该字符串的首字符,但是它的值可以改变看。因为,可以使用递增运算符。
char * str = "hello world";
字符串字面量被视为 const 数组。由于指针变量指向这个 const 数据,所以应该把指针变量声明为指向 const 数组的指针。这意味着不能用指针变量改变它所指向的数据,但是仍然可以改变指针变量的值(即,指针变量指向的位置)。如果把一个字符串字面量拷贝给一个数组,就可以随意改变数据,除非把这个数组声明为 const。
字符数组是若干个元素组成的,每个元素放一个字符,而字符指针变量中存放的是地址(字符串/字符数组的首地址),绝不是将字符串放到字符指针变量中。对字符数组除了定义时可以使用字符串整体赋值,其它情况下只能单个元素赋值。对字符指针可以使用字符串整体赋值;
如果定义了一个字符数组,那么它有确定的内存地址(即字符数组名是一个常量),而定义一个字符指针变量时,它并未指向某个确定的字符数据,并且可以多次赋值。
#include <stdio.h>
#define MSG "I'm special"
int main(void)
{
char ar[] = MSG;
const char * pt = MSG;
printf("address of \"I'm special\": %p\n", "I'm special");
printf("address ar: %p\n", ar);
printf("address pt: %p\n", pt);
printf("address NSG: %p\n", MSG);
printf("address of \"I'm special\": %p\n", "I'm special");
return 0;
}
pt 和 MSG 的地址相同,而 ar 的地址不同。虽然字符串字面量 "I'm special" 在程序的两个 printf() 函数中出现了两次,但是编译器只使用一个储存位置,而且与 MSG 的地址相同。编译器可以把多次使用的相同字面量储存在一处或者多处。静态数据使用的内存与 ar 使用的动态内存不同。
三、字符串数组
我们可以使用二维数组的方式或指针数组的方式创建字符串数组。
#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 和 yourtalents 非常相似。两者都代表 5 个字符串。使用一个下标时都分别表示一个字符串,使用两个下标时都分别表示一个字符。而且,两者的初始化方式也相同。
但是,它们也有区别。mytalents 数组是一个内含 5 个指针的数组,在我们的系统中共占用 40 个字节。而 yourtalents 是一个内含 5 个数组的数组,每个数组内含 40 个 char 类型的值,共占用 200 字节。所以,mytalents[0] 和 yourtalents[0] 都分别表示一个字符串,但 mytalents 和 yourtalents 的类型并不相同。mytalents 中的指针指向初始化时所用的字符串字面量的位置,这些字符串字面量被储存在静态内存中,而 yourtalents 中的数组则储存着字符串字面量的副本,所以每个字符串都被储存了两次。yourtalents 中的每个元素的大小必须相同,而且必须是能储存最长字符串的大小。
如果要用数组表示一系列待显示的字符串,请使用指针数组。因为它比二维字符数组的效率高;
四、字符串的输入输出
4.1、scanf()函数与printf()函数
scanf() 函数的函数原型如下:
int scanf( const char * restrict format, ... );
在 C 语言中,格式输入使用 scanf() 函数,scanf() 函数通过 % 转义的方式可以获取用户通过标准输入设备输入的数据 。用户从键盘输入的是文本数据,scanf() 函数把输入的字符串转换成指定的格式,并将转后成指定格式后的数据保存在指定变量中。
通过 scanf() 函数的一般格式可以看出, 格式字符串 是用双引号括起来的字符串,其中包括 转换说明 (conversion specification)和 字面字符 两种 。而在地址列表中,此处应该给出用来接收数据变量的地址 。我们可以使用 %s 格式说明输入一个字符串。
scanf() 函数有两种方法确定输入结束。无论使用哪种方法,都从第 1 个非空白字符作为字符串的开始,如果使用 %s 格式说明,以一下一个空白字符(空行、空格、制表符或换行符)作为字符串的结束(字符串不包括空白字符)。如果指定字符宽度,那么 scanf() 将读取指定的字符个数或读到第 1 个空白字符停止(先满足的条件即是结束输入的条件)。
scanf() 函数会返回一个整数,该值等于 scanf() 成功读取的项数或 EOF(读到文件结尾时返回 EOF)。
printf() 函数的函数原型如下:
int printf( const char * restrict format, ... );
printf() 函数的作用是向终端(输出设备)输出若干任意类型的数据。printf() 语句把输出发送到一个叫做 缓冲区 (buffer)的中间存储区域,然后缓冲区的内容再不断发送到屏幕上。C 标准明确规定了何时把缓冲区中的发送到屏幕:当缓冲区满、遇到换行字符或组要输入的时候(从缓冲区把数据发送到屏幕或文件被称为 刷新缓冲区 )。
通过 printf() 函数的一般格式可以看出,格式字符串 是用双引号括起来的字符串,其中包括 转换说明 (conversion specification)和 字面字符 两种。printf() 函数把字符串的地址作为参数。我们可以使用 %s 格式说明输入一个字符串。printf() 函数不会自动在每个字符串末尾添加一个换行符。因此,必须在参数中指明应该在哪里使用换行符。
#include <stdio.h>
int main(void)
{
char name1[11], name2[11];
int count;
printf("Please enter 2 names.\n");
count = scanf("%5s %10s", name1, name2);
printf("I read the %d names %s and %s.\n", count, name1, name2);
return 0;
}
由于 scanf() 函数无法知道要输入字符串的大小,必须遇到换行符或读到文件结尾位置才接收输入,因此容易导致字符数组越界的情况。不过,在 %s 格式说明中使用字段宽度可防止溢出;
4.2、gets()函数与puts()函数
gets() 函数的函数原型如下:
char * gets(char * str);
gets() 函数读取整行输入,直至遇到换行符,然后丢弃换行符,储存其余字符,并在这些字符的末尾添加一个空字符 '\0' 使其成为一个 C 字符串。
gets() 函数无法检查实参数组是否能装下输入行。这是因为数组名会转换成该数组首元素的地址。因此,gets() 函数只知道数组的开始处,并不知道数组中有多少个元素。如果输入的字符串过长,会导致缓冲区溢出(buffer overflow),即多余的字符超出了指定的目标空间。如果这些多余的字符只是占用了尚未使用的内存,就不会立即出问题;如果它们擦写了程序中的其它数据,会导致程序异常终止,或者还有其它情况。
puts() 函数的函数原型如下:
int puts(char * str);
字符串输出就是将字符串输出到控制台上。在 C 语言中,字符串输入使用的是 puts() 函数,它的作用是输出字符串并显示在屏幕上。puts() 函数只需要把字符串的地址作为参数传递给它即可。puts() 函数在显示字符串时会自动在其末尾添加一个换行符,该函数在遇到空字符 '\0' 时就停止输出,所以必须保证有空字符。
#include <stdio.h>
#define SIZE 81
int main(void)
{
char words[SIZE];
puts("Enter a string, please.");
gets(words);
puts("Your string:");
puts(words);
puts("Done.");
return 0;
}
C11 标准委员会采取了更强硬的态度,直接从标准中废除了 gets() 函数;
4.3、fgets()函数与fputs()函数
fgets() 函数的函数原型如下:
char * fgets( char * restrict str, int count, FILE * restrict stream );
过去通常用 fgets() 来代替 gets()。fgets() 函数的第 2 个参数限制读入的字符数来解决溢出问题。该函数专门设计用于处理文件输入。fgets() 和 gets() 的区别如下:
- fgets() 函数的第 2 个参数指明读入字符的最大数量。如果该参数的值是 n,那么 fgets() 将读入 n-1 个字符,或者读到遇到的第一个换行符为止;
- 如果 fgets() 读到一个换行符,会把它储存在字符串中。这点与 gets() 不同,gets() 会丢弃换行符;
- fgets() 函数的第 3 个参数指明要读入的文件。如果读入从键盘输入的数据,则以 stdin(标准输入)作为参数,该标识符定义在 stdio.h 中。
fgets() 函数返回指向 char 的指针。如果一切进行顺利,该函数返回的地址与传入的第 1 个参数相同。但是,如果函数读到文件结尾,它将返回一个特殊的指针:空指针(null pointer)。该指针保证不糊指向有效的数据。
fputs() 函数的函数原型如下:
int fputs( const char *restrict str, FILE *restrict stream );
因为 fgets() 函数把换行符放在字符串的末尾(假设输入行不溢出),通常要与 fputs() 函数配对使用,除非该函数不在字符串末尾添加换行符。fputs() 和 puts() 的区别如下:
- fputs() 函数的第 2 个参数执行要写入数据的文件。如果要打印在显示器上,可以用定义在 stdio.h 中的 stdout(标准输出)作为该参数;
- 与 puts() 函数不同,fputs() 函数不会在输出的末尾添加换行符。
系统使用缓冲的 I/O。这意味着用户按下 Return 键之前,输入都被储存在临时储存区(即,缓冲区)中。按下 Return 键就在输入中增加了一个换行符,并把整行输入发送给 fgets()。对于输出,fputs() 把字符发送给另一个缓冲区,当发送换行符时,缓冲区的内容发送至屏幕上。
#include <stdio.h>
#define SIZE 14
int main(void)
{
char words[SIZE];
fputs("Enter a string, please.\n", stdout);
fgets(words, SIZE, stdin);
fputs("Your string:\n", stdout);
fputs(words, stdout);
fputs("Done.", stdout);
return 0;
}
fgets() 获取字符串少于元素个数会有 '\n',大于等于没有 '\n'。
4.4、gets_s()函数
gets_s() 函数的函数原型如下:
char * gets_s( char * str, rsize_t n );
C11 标准新增的 gets_s() 函数也可代替 gets() 函数。该函数与 gets() 函数更接近,而且可以替换现有代码中的 gets() 函数。但是,它是 stdio.h 输入/输出函数系列中的可选拓展,所以支持 C11 的编译器也不一定支持它。gets_s() 函数与 fgets() 函数的区别:
- gets_s() 函数只从标准输入中读取数据,所以不需要第 3 个参数;
- 如果 gets_s() 读到换行符,会丢弃它而不是储存它;
- 如果gets_s() 读到最大字符数都没有读到换行符,会执行以下几步。首先把目标数组中的首字符设置为空字符,读取并丢弃然后的输入直至读到换行或者文件结尾,返回返回空指针。接着,调用依赖实现的“处理函数”(或你选择的其它函数),可能会中止或退出程序;
如果目标存储区装得下输入行,gets()、fgets() 和 gets_s() 3 个函数都没有问题。但是,fgets() 会保留输入末尾的换行符作为字符串的一部分,要编写额外的代码将其替换成空字符。
如果输入行太长,使用 gets() 不安全,它会擦写现有数据看,存在安全隐患。gets_s() 函数很安全,但是如果并不希望程序中止或者退出,就要知道如何编写特殊的“处理函数”。另外,如果打算让程序继续运行,gets_s() 函数会丢弃输入行的其它字符,无论你是否需要。
五、字符串处理函数
C库 提供了多个处理字符串的函数,ANSI C 把这些函数的原型放在 string.h 头文件中。其中最常用的函数有 strlen()、strcpy()、strncpy()、strcat()、 strncat()、strcmp()、strncmp() 等等。另外,还有 sprintf() 函数,其原型在 stdio.h 头文件中。
5.1、获取字符串长度函数
strlen() 函数的函数原型如下:
size_t strlen( const char * str ); // 获取源字符串的有效长度
strlen() 函数返回的是字符串中 '\0' 前面出现的字符个数(不包含 '\0')。str 参数指向的字符串必须要以 '\0' 结束。strlen() 函数的返回值是 size_t,是无符号的整数。
#include <stdio.h>
#include <string.h>
int main(void)
{
char str[15] = "hello world!" ;
int length = 0;
length = strlen(str);
printf("字符串的长度为:%d\n", length);
return 0;
}
模拟实现 strlen() 函数:
#include <stdio.h>
#include <assert.h>
size_t my_strlen(const char * str);
int main(void)
{
char str[] = "hello world!";
int length = my_strlen(str);
printf("%u\n", length);
return 0;
}
size_t my_strlen(const char * str)
{
size_t count = 0;
assert(str); // 断言,判断指针不能为空
while(*str++ != '\0')
{
count++;
}
return count;
}
5.2、字符串复制函数
strcpy() 函数 和 strncpy() 函数的原型如下:
char * strcpy( char * dest, const char * src ); // 复制源字符串数组到目标字符串中
char * strncpy( char * dest, const char * src, size_t count ); // 复制源字串的n个字符复制到目标字符串中
strcpy() 函数会将源字符串拷贝到目标字符串中,源字符串中必须以 '\0' 结束,它会将源字符串的 '\0' 拷贝到目标空间。并且,目标空间必须足够大,以确保能存放源字符串,目标空间还必须是可变的。
#include <stdio.h>
#include <string.h>
int main(void)
{
char src[15] = "hello world!" ;
char dest1[15] = {0};
char dest2[15] = {0};
char * str = "hello"; // 目标区域不可修改
strcpy(dest1,src);
printf("%s\n",dest1);
strncpy(dest2,str,5);
printf("%s\n",dest2);
return 0;
}
模拟实现 strcpy() 函数:
#include <stdio.h>
#include <assert.h>
char * my_strcpy(char * desc, const char * src);
int main(void)
{
char src[15] = "hello world!" ;
char dest[15] = {0};
my_strcpy(dest,src);
printf("%s\n",dest);
return 0;
}
char * my_strcpy(char * dest, const char * src)
{
char * ret_val = dest; // 记录目标空间原地址
assert(src); // 断言,判断指针不能为空
assert(dest); // 断言,判断指针不能为空
while(*dest++ = *src++);
return ret_val; // 返回目标空间原地址
}
5.3、字符串连接函数
strcat() 函数 和 strncat() 函数的原型如下:
char * strcat( char * dest, const char * src ); // 将源数组的字符串连接到目标数组后
char * strncat( char * dest, const char * src, size_t count ); // 将源数组的指定个数字符连接到目标数组后
strcat() 函数可以将源字符串拼接到目标字符串后,源字符串必须以 '\0' 结束。并且,目标空间必须足够大,以确保能存放源字符串,目标空间还必须是可变的。
#include <stdio.h>
#include <string.h>
int main(void)
{
char dest1[15] = "hello " ;
char dest2[15] = "hello " ;
char src[15] = "world!";
strcat(dest1,src);
printf("%s\n",dest1);
strncat(dest2,src,5);
printf("%s\n",dest2);
return 0;
}
模拟实现 strcat() 函数:
#include <stdio.h>
#include <assert.h>
char * my_strcat(char * dest, const char * src);
int main(void)
{
char dest[15] = "hello " ;
char src[15] = "world!";
my_strcat(dest,src);
printf("%s\n",dest);
return 0;
}
char * my_strcat(char * dest, const char * src)
{
char * ret_val = dest; // 记录目标空间原地址
assert(src); // 断言,判断指针不能为空
assert(dest); // 断言,判断指针不能为空
while(*dest != '\0')
{
dest++;
}
while(*dest++ = *src++);
return ret_val; // 返回目标空间原地址
}
5.4、字符串比较函数
在 C 语言中,不能使用 == 来比较两个字符串,== 符号是比较两个字符串的首地址是否相等。
字符串比较就是将一个字符串与另一个字符串从首字母开始,按照 ASCII 码的顺序进行逐个比较。在 C 语言中,通过 strcmp() 函数 或者 strncmp() 函数进行字符串的比较。
strcmp() 函数 和 strncmp() 函数的原型如下:
int strcmp( const char * lhs, const char * rhs ); // 将两个字符串从首字符按ASCII进行比较,相同返回0,lhs大返回整数,rhs大返回负数
int strncmp( const char * lhs, const char * rhs, size_t count ); // 将两个字符串前count字符按ASCII进行比较,相同返回0,lhs大返回整数,rhs大返回负数
strcmp() 函数 和 strcmp() 函数的功能就是按照 ASCII 码的顺序比较两个数组中的字符串,并由函数返回值返回函数结果:
- 字符串 1 = 字符串 2,返回值为 0
- 字符串 1 > 字符串 2,返回值为 正数
- 字符串 1 < 字符串 2,返回值为 负数
#include <stdio.h>
#include <string.h>
int main(void)
{
char array1[15] = "hello world!" ;
char array2[15] = "hello sakura!" ;
int result = 0;
result = strcmp(array1,array2);
if(result < 0)
{
printf("第一个字符串大\n");
}
else if(result > 0){
printf("第二个字符大\n");
}
else{
printf("两个字符串相等\n");
}
result = strncmp(array1,array2,5);
if(result < 0)
{
printf("第一个字符串大\n");
}
else if(result > 0){
printf("第二个字符大\n");
}
else{
printf("两个字符串前5个字符相等\n");
}
return 0;
}
模拟实现 strcmp() 函数:
#include <stdio.h>
#include <assert.h>
int my_strcmp(const char * str1, const char * str2);
int main(void)
{
char array1[15] = "abc" ;
char array2[15] = "abc" ;
int result = 0;
result = my_strcmp(array1,array2);
if(result < 0)
{
printf("第一个字符串大\n");
}
else if(result > 0){
printf("第二个字符大\n");
}
else{
printf("两个字符串相等\n");
}
return 0;
}
int my_strcmp(const char * str1, const char * str2)
{
assert(str1); // 断言,判断指针不能为空
assert(str2); // 断言,判断指针不能为空
while(*str1 == *str2)
{
if(*str1 == '\0')
{
return 0;
}
str1++;
str2++;
}
return *str1 - *str2;
}
与其说 strcmp() 函数按字母顺序进行比较,不如说按 机器排序序列(machine aollationg sequence)进行比较,即根据字符的数值进行比较(通常都使用 ANSCII 码);
5.5、查找子串函数
strstr() 函数的原型如下:
char * strstr( const char * str, const char * substr );
查找字符串 str 中是否包含子串 substr,如果找到则返回子串所在母串中的地址,找不到则返回 NULL。
#include <stdio.h>
#include <string.h>
int main(void)
{
char str[] = "hello world!";
char substr[] = "world";
char * result = strstr(str, substr);
result ? printf("%s\n", result):printf("子串不存在\n");
return 0;
}
模拟实现 strstr() 函数:
#include <stdio.h>
#include <assert.h>
char * my_strstr(const char * str, const char * substr);
int main(void)
{
char str[] = "abbbcdef";
char substr[] = "bbc";
char * result = my_strstr(str, substr);
result ? printf("%s\n", result):printf("子串不存在\n");
return 0;
}
char * my_strstr(const char * str, const char * substr)
{
const char * s1 = str;
const char * s2 = substr;
const char * p = str;
assert(str); // 断言,判断指针不能为空
assert(substr); // 断言,判断指针不能为空
while(*p)
{
s1 = p;
s2 = substr;
while(*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
{
s1++;
s2++;
}
if(*s2 == '\0')
{
return (char *)p;
}
p++;
}
return NULL;
}
5.6、切割字符串函数
strtok() 函数的原型如下:
char * strtok( char * str, const char * delim );
该函数可以切割字符串,第一个参数 str 指定一个字符串,它包含了 0 个或多个由 delim 字符串中一个或者多个分隔符分割的标记。delim 参数是个字符串,定义了用作分隔符的字符集合。
该函数找到 str 中的下一个标记,并将其用 '\0' 结尾,返回一个指向这个标记的指针。(注意:strtok 函数会改变被操作的字符串,所以在使用 strtok() 函数切分的字符串一般都是临时拷贝的内容中的位置。)strtok() 函数的第一个参数不为 NULL,函数将找到 str 中的第一个标记,strtok() 函数将保存它在字符串中的位置。strtok() 函数的第一个参数为 NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记。如果字符串不存在更多的标记,则返回 NULL 指针。
#include <stdio.h>
#include <string.h>
int main(void)
{
char email[] = "sakura27185@petalmail.com";
const char * delim = "@.";
char * result = NULL;
char temp[30];
strcpy(temp, email);
for(result = strtok(temp,delim); result != NULL; result = strtok(NULL, delim))
{
printf("%s\n", result);
}
return 0;
}
5.7、sscanf()函数与sprintf()函数
sscanf() 函数的原型如下:
int sscanf( const char* buffer, const char* format, ... );
sscanf() 函数声明在 stdio.h 头文件中。该函数与 scanf() 函数类似,但是它是从一个字符串中提取出格式化数据,并保留在对象的变量中。sscanf() 函数的第 1 个参数是字符串的地址。其余参数和 scanf() 函数相同,即格式说明符和待写入项的列表。
sprintf() 函数的原型如下:
int sprintf( char *restrict buffer, const char *restrict format, ... );
sprintf() 函数声明在 stdio.h 头文件中。该函数与 printf() 函数类似,但是它是把数据写入字符串,而不是打印在显示器上。因此,该函数可以把多个元素组合成一个字符串。sprintf() 函数的第 1 个参数是目标字符串的地址。其余参数和 printf() 函数相同,即格式字符串和待写入项的列表。
#include <stdio.h>
int main(void)
{
char date[15] = {0};
int year = 0,month = 0,day = 0;
printf("请输入年月日,中间用-分割:\n");
scanf("%d-%d-%d", &year, &month, &day);
sprintf(date,"%4d-%02d-%02d", year, month, day);
printf("字符串:%s\n", date);
year = 0,month = 0,day = 0;
printf("%04d-%02d-%02d\n", year, month, day);
sscanf(date, "%d-%d-%d", &year,&month, &day);
printf("%4d-%02d-%02d\n", year, month, day);
return 0;
}
sprintf() 函数获取输入,并将其格式化为标准形式,然后把格式化的字符串储存在 format 中;