字符串的本质
字符串是有序字符的集合 ,C语言中通过特殊的字符数组模拟字符串,C语言中的字符串是以 '\0' 结尾的字符数组
在C语言中,双引号引用的单个或多个字符是一种特殊的字面量 ,本质为字符数组,编译器自动在结尾加上'\0'字符
字符串字面量的本质是—个数组,字符串字面量至少包含—个字符(\0)
字符串字面量可以看作指针常量,字符串字面量中的字符不可改变
#include <stdio.h>
char g_str[] = "AAAAAAA"; // 存储于.data段,全局数据区
int main()
{
char ca[] = { 'H', 'e', 'l', 'l', 'o' }; // 普通字符数组, 栈内, mov byte ptr [ca],48h
char sa[] = { 'W', 'o', 'r', 'l', 'd', '\0' }; // \0结尾的字符数组,表现为字符串,同上一致
const char* s1 = "12345678"; // 字符串存储于只读存储区
char s2[] = "Python"; // 字符串存储于只读存储区,但由于声明的是只读数组,所以栈内拷贝了一份
char* s3 = "Java"; // 字符串存储于只读存储区
sa[0] = 'A';
s2[0] = 'A'; // 被修改的是栈内的字符,数据段的没有被修改
// s3[0] = 'A'; // error
printf("%x\n%x\n\n", ca, &ca);
printf("%x\n%x\n\n", sa, &sa);
printf("%x\n%x\n\n", s1, &s1);
printf("%x\n%x\n\n", s2, &s2);
printf("%x\n%x\n\n", s3, &s3);
printf("%x\n%x\n\n", g_str, &g_str);
return 0;
}
"Hello World ! "是—个无名的字符数组
#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); //\0对应的ascii为0
printf("%s\n", "Hello");
printf("%p\n", "World");
return 0;
}
字符串的长度
字符串的长度就是字符串所包含字符的个数 ,字符串长度指的是第—个'\0'字符前出现的字符个数
通过'\0'结束符来确定字符串的长度 ,函数strlen用于返回字符串的长度, 字符串相关函数都依赖于结束符'\0'
#include <stdio.h>
#include <string.h>
int main()
{
char s[] = "Hello\0world"; // sizeof(s) = 12 需要加上编译器加上的\0
int i = 0;
for(i=0; i<sizeof(s)/sizeof(char); i++)
{
printf("%c\n", s[i]); //h e l l o \0 w o r l d \0
}
printf("%s\n", s); // Hello
printf( "%d\n", strlen(s) ); // 5
printf( "%d\n", strlen("123") ); // 3
return 0;
}
字符串误区
格式化信息必须与变参个数相匹配
#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;
}
snprintf函数本身是可变参数函数,原型:int snprintf(char* buffer, int buf_size, const char* fomart, ...)
当函数只有3个参数时,如果第三个参数没有包含格式化信息,函数调用没有问题;
相反,如果第三个参数包含了格式 化信息,但缺少后续对应参数,则程序行为不确定。
snprintf(buf, sizeof(buf), src, "wss");
字符串终止标记'\0'
#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);
printf("strlen(STR) = %d\n", strlen(STR)); //7
printf("sizeof(STR) = %d\n", sizeof(STR)); //22,即便是\0结尾也会编译器也会再加上\0
printf("strlen(src) = %d\n", strlen(src)); //7
printf("sizeof(src) = %d\n", sizeof(src)); //4
printf("strlen(buf) = %d\n", strlen(buf)); //7
printf("sizeof(buf) = %d\n", sizeof(buf)); //255
printf("src = %s\n", src); //Hello
printf("buf = %s\n", buf); //Hello
return 0;
}
字符串相关的函数均以第—个出现的 '\0'作为结束符 ,编译器总是会在字符串字面量的未尾添加'\0'
相同字符串字面量的比较
#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
BCC
字符串之间的相等比较需要用strcmp完成 ,不可直接用==进行字符串直接的比较 ,完全相同的字符串字面量的==比较结果为false
一些现代编译器能够将相同的字符串字面量映射到同—个无名字符数组,因此==比较 结果为true
字符串的相关操作
字符串循环右移
#include <stdio.h>
#include <string.h>
// 将字符串每个字符向右移动n位
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++) //O(n)
{
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;
}