1 字符串的概念
- 字符串是有序字符的集合。
- 字符串是程序中的基本元素之一。
- C语言中没有字符串的概念。
- C语言中通过特殊的字符数组模拟字符串。
- C语言中的字符串是以‘\0’结尾的字符数组。
2 字符数组与字符串
- 在C语言中,双引号引用的单个或多个字符是一种特殊的字面量。
- 存储于程序的全局只读存储区。
- 本质为字符数组,编译器自动在结尾加上‘\0’字符。
下面哪些是字符串的定义?
char ca[] = {‘H’, ‘e’, ‘l’, ‘l’, ‘o’}; // error
char sa[] = {‘W’, ‘o’, ‘r’, ‘l’, ‘d’, ‘\0’}; // ok
char ss[] = “Hello world!”; // ok
char* str = “Hello world!”; // ok
实例分析:字符数组与字符串
#include <stdio.h>
int main()
{
char ca[] = {'H', 'e', 'l', 'l', 'o'};
char sa[] = {'W', 'o', 'r', 'l', 'd', '\0'};
char ss[] = "Hello world!";
char* str = "Hello world!";
printf("%s\n", ca);
printf("%s\n", sa);
printf("%s\n", ss);
printf("%s\n", str);
return 0;
}
3 鲜为人知的小秘密
- 字符串字面量本质是一个数组。
- 字符串字面量可以看作常量指针。
- 字符串字面量中的字符不可改变。
- 字符串字面量至少包含一个字符。
4 字符串字面量
- “hello world !”是一个无名的字符数组。
下面的表达式正确吗?
char b = “abc”[0]; // ok
char c = *(“123” + 1); // ok
char t = *”“; // ok,这里是空字符不是空格
实例分析:字符串字面量的本质
#include <stdio.h>
int main()
{
char b = "abc"[0];
char c = *("123" + 1);
char t = *"";
printf("%c\n", b);
printf("%c\n", c);
printf("%d\n", t);
printf("%s\n", "Hello");
printf("%p\n", "World");
return 0;
}
5 字符串的长度
- 字符串的长度就是字符串所包含字符的个数。
- 字符串长度指的是第一个‘\0’字符前出现的字符个数。
- 通过‘\0’结束符来确定字符串的长度。
- 函数strlen用于返回字符串的长度。
编程实验:strlen的使用
#include <stdio.h>
#include <string.h>
int main()
{
char s[] = "Hello\0world";
int i = 0;
for(i=0; i<sizeof(s)/sizeof(char); i++)
{
printf("%c\n", s[i]);
}
printf("%s\n", s);
printf( "%d\n", strlen(s) );
printf( "%d\n", strlen("123") );
return 0;
}
小结
- C语言中通过字符数组模拟字符串。
- C语言中的字符串使用 ‘\0’作为结束符。
- 字符串字面量的本质为字符数组。
- 字符串相关函数都依赖于结束符 ‘\0’。
6 字符串典型问题分析
6.1 典型问题一
下面的程序输出什么?为什么?
#include <stdio.h>
int main()
{
char buf[10] = {0};
char src[] = "hello %s";
snprintf(buf, sizeof(buf), src);
printf("buf = %s\n", buf);
return 0;
}
// 运行结果:
// buf = hello ��
分析:
snprintf函数本身是可变参数函数,原型如下:int snprintf(char *buffer, int buf_size, const char *fomart, …)。
当函数只有3个参数时,如果第三个参数没有包含格式化信息,函数调用没有问题;相反,如果第三个参数包含了格式化信息,但缺少后续对应参数,则程序行为不确定(与变参的实现原理有关)。格式化信息必须与变参个数相匹配。
6.2 典型问题二
下面的程序输出什么?为什么?
#include <stdio.h>
#include <string.h>
int main()
{
#define STR "Hello, \0D.T.Software\0"
char* src = STR;
char buf[255] = {0};
snprintf(buf, sizeof(buf), src);//总共拷贝了7个字符
printf("strlen(STR) = %d\n", strlen(STR));
printf("sizeof(STR) = %d\n", sizeof(STR));
printf("strlen(src) = %d\n", strlen(src));
printf("sizeof(src) = %d\n", sizeof(src));
printf("strlen(buf) = %d\n", strlen(buf));
printf("sizeof(buf) = %d\n", sizeof(buf));
printf("src = %s\n", src);
printf("buf = %s\n", buf);
return 0;
}
/*
运行结果:
strlen(STR) = 7
sizeof(STR) = 22
strlen(src) = 7
sizeof(src) = 4
strlen(buf) = 7
sizeof(buf) = 255
src = Hello,
buf = Hello,
*/
分析:
- 字符串相关的函数均以第一个出现的‘\0’作为结束符。
- 编译器总是会在字符串字面量的末尾添加 ‘\0’。
- 字符串字面量的本质为数组。
- 字符串、字符数组、字符指针不同。
6.3 典型问题三
下面的程序输出什么?为什么?
#include <stdio.h>
#include <string.h>
int main()
{
#define S1 "D.T.Software"
#define S2 "D.T.Software"
if( S1 == S2 )
{
printf("Equal\n");
}
else
{
printf("Non Equal\n");
}
if( strcmp(S1, S2) == 0 )
{
printf("Equal\n");
}
else
{
printf("Non Equal\n");
}
return 0;
}
/*
gcc运行结果:
Equal
Equal
*/
分析:
- 字符串之间的相等比较需要使用strcmp完成。
- 不可直接用= =进行字符串直接的比较。
- 完全相同的字符串字面量的 = = 比较结果为false。
- 一些现代的编译器能够将相同的字符串字面量映射到同一个无名字符数组,因此 = = 比较结果为true。所以,不编写依赖特殊编译器的代码!!!
6.4 典型问题四
函数原型:void right_shift_r(const char *src, char *result, unsinged int n);
函数功能:将输入字符串src循环右移n位,result为输出结果。
要求:以效率最高实现。
示例:
“abcde” –2—- “deabc”
“abcde” –8—- “cdeab”
#include <stdio.h>
#include <string.h>
void right_shift_r(const char* src, char* result, unsigned int n)
{
const unsigned int LEN = strlen(src);
int i = 0;
for(i=0; i < LEN; i++)
{
result[(n + i) % LEN] = src[i];
}
result[LEN] = '\0';
}
int main()
{
char result[255] = {0};
right_shift_r("abcde", result, 2);
printf("%s\n", result);
right_shift_r("abcde", result, 5);
printf("%s\n", result);
right_shift_r("abcde", result, 8);
printf("%s\n", result);
return 0;
}