字符串
字符数组
在C语言中,并没有像Java一样有一个关键字String来直接表示字符串,它都是以字符数组的形式来表示。
// 该变量存在于内存中的栈区,在函数执行结束后会被释放掉
char str[] = "Hello";
str这个字符串的长度是多少呢?可以使用库函数strlen来算出。
printf("%zd\n", strlen(str)); // 5
那这个字符数组的长度是多少呢(也就是数组元素个数)?可以使用关键字sizeof来算出。它是用来计算数据类型在内存中所占的字节数。
printf("%zd\n", sizeof(str) / sizeof(char)); // 6
为什么是6?再来看看另外一种形式的字符数组
char str1[] = {'H', 'e', 'l', 'l', 'o'};
printf("%zd\n", sizeof(str1) / sizeof(char)); // 5
str和str1都是字符数组,但长度却不一样,究其原因是str比str1多了一个\0。在C语言中,字符串都是以\0结尾的。
字符串常量
虽然没有直接的关键字来表示字符串,但我们也可以直接使用指针来表示。此种方式创建的字符串是一个常量,存在于内存中的常量区,只可读。
char *str = "Hello World";
指针
定义与使用
指针是一种数据类型。在内存中,每一个变量都有其特定的地址,指针变量就是用来指向这个地址的。 通过这个地址就可以找到这个变量所对应的值。
int a = 10;
// 指针变量p为int*类型,指向变量a的地址
int* p = &a;
// 通过指针变量获取值
printf("%d\n", *p); // 10
// 通过指针修改变量的值
*p = 100;
printf("%d\n", *p); // 100
内存大小
无论何种类型的指针变量,在32位的操作系统中所占的大小为4个字节;而在64位的操作系统中则为8个字节。
void pointerSize() {
printf("%zd\n", sizeof(int *)); // 8
printf("%zd\n", sizeof(char *)); // 8
printf("%zd\n", sizeof(long *)); // 8
printf("%zd\n", sizeof(double *)); // 8
printf("%zd\n", sizeof(float *)); // 8
}
分类
-
野指针
指向一个未知的内存空间,可能在读写的时候出现错误
void unknownPointer() {
int a = 10;
int *p = &a;
// 0 - 255都是系统保留,不可读写
p = 100; // 野指针
printf("%d\n", *p);
}
-
空指针
不指向任何地址。其最终是指向内存编号为0的地址。操作该内存空间会报错,一般情况空指针用于程序条件判断。
int *p = NULL;
- 万能指针
void*:可指向任意变量的内存空间。
void voidPointer() {
int a = 10;
void *p = &a;
printf("%d\n", *(int *) p);
}
const修饰指针
const修饰指针类型,可以改变指针指向的地址,但不能改变指针指向的地址的值
int a = 10;
int b = 5;
const int* p1 = &a;
p1 = &b;
// 不允许
*p1 = 100;
const修饰指针变量,可以改变指针指向的地址的值,但不能改变指针指向的地址
int a = 10;
int b = 5;
int *const p2 = &a;
*p2 = 20;
// 不允许
p2 = &b;
const同时修饰指针类型和指针变量,都不能改变
int a = 10;
const int *const p3 = &a;
字符串常用库函数
- 字符串拷贝
void strCopy() {
char src[] = "Hello";
char dst[100];
// 会将src中的\0拷贝过去
if (strcpy(dst, src) != NULL) {
printf("%s\n", dst); // Hello
}
//拷贝指定长度的字符串
char dst1[100];
// 不会将src中的\0拷贝过去
if (strncpy(dst1, src, 2) != NULL) {
// 手动添加字符串结束标志
dst1[2] = '\0';
printf("%s\n", dst1); // He
}
}
- 字符串追加
void strConcat() {
char dst[100] = "Hello";
char blank[] = " ";
strcat(dst, blank);
char src[] = "World";
strcat(dst, src);
printf("%s\n", dst);
// 追加指定长度的字符串
char dst1[100] = "Hello";
strcat(dst1, blank);
strncat(dst1, src, 2);
printf("%s\n", dst1);
// 1. 被追加的字符串需有足够的空间
// 2. 追加顺序:先去掉目标字符串中的\0再追加源字符串所有字符包括\0
}
- 字符串比较
void strCompare() {
char str1[] = "Hello";
char str2[] = "Hell";
char str3[] = "He\0ll";
int result = strcmp(str1, str2);
printf("%d\n", result); // 111
// 只比较\0前面的字符
int result2 = strcmp(str2, str3);
printf("%d\n", result2); // 108
// 比较指定字符串
int res = strncmp(str1, str2, 4);
printf("%d\n", res); // 0
// 相等返回0,若不相等,在不同的操作系统中,返回结果可能不同。
// 有的返回-1或者1,有的返回ASCII差值
}
- 字符串输入输出
void strFormat() {
char dst[100] = "1+2=";
int a, b;
// 获取字符串中的数据
sscanf(dst, "%d+%d=", &a, &b);// 从dst中获取1和2,输出到a和b
// 按格式写入数据到字符串中
sprintf(dst, "%d+%d=%d", a, b, a + b);// 将a + b = (a+b)写入到dst中
printf("%s\n", dst); // 1 + 2 = 3
}
- 在字符串中查找字符或者字符串
void charAndStrInStr() {
char str[] = "Hello哈";
char *res = strchr(str, 'l');
if (res != NULL) {
printf("%s\n", res); // ello
}
char *ret = strstr(str, "哈");
if (ret != NULL) {
printf("%s\n", ret); //哈
}
}
- 字符串分割
void strSplit() {
char str[] = "345@qq.com";
char *res = strtok(str, "@");
while (res != NULL) {
printf("%s\n", res); // 345 qq com
res = strtok(NULL, ".");
}
// 切割完之后会破坏原先字符串样式,
// 切割符会被\0代替: 345\0qq\0com
// 所以打印时才会分别打出345 qq com
}
- 字符串转数字
void str2Num() {
char str[] = " 100hello 123";
char str1[] = " hello 123";
int ret = atoi(str);
int res = atoi(str1);
printf("%d\n", ret); // 100
printf("%d\n", res); // 0
// 1. 跳过前面的空格字符,直到遇到正负数字才开始转化
// 遇到非数字或\0才结速转换
// 2. 还有 atof(), atol()等函数
}