C语言之字符串与指针

1,089 阅读5分钟

字符串

字符数组

在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

strstr1都是字符数组,但长度却不一样,究其原因是strstr1多了一个\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()等函数
}