字符串与字符数组、sizeof与strlen

一、C语言的字符串类型

C语言没有原生的字符串类型,不像C#等高级语言中具有string类型来表示字符串,C语言中的字符串是通过字符指针来间接实现的,如:

char *p = "WHUT2018";

此时p就叫做字符串,但是实际上p只是一个字符指针(本质上就是一个指针变量,只是p指向了一个字符串的起始地址而已)

1.1 C语言中字符串的本质

  1. 字符串就是指针指向的一段连续的内存空间,在内存中就是多个字节连续分布构成的
  2. 字符串就是一串字符,字符在编程语言中就是字符类型的变量。C语言用ASCII编码对字符进行编程,编码后可以用char型变量来表示一个字符。字符串就是多个字符打包在一起构成的。

1.2 C语言中字符串的三个核心要点

  1. 用一个指针指向字符串头
  2. 固定尾部:以’\0’结尾
  3. 组成字符串的各字符彼此地址相连

1.3 关于’\0’

  1. 0'\0'是等价的,48'0'是等价的
  2. '\0'是字符串中的一个魔数(一些具有特殊含义的内容,在这里起结尾的标识作用),因此C语言中无法定义出中间内容含有'\0'的字符串

1.4 指向字符串的指针和字符串本身

指向字符串的指针和字符串本身是分开的两个东西:在下述代码段中

char *p = "WHUT2018";
  1. p是一个字符指针,分配在栈上,占4字节,用来指向字符串,是字符串的引子,但其本身并不是字符串的内容;
  2. "WHUT2018"分配在代码段,占9个字节,其中:
  • 8字节的空间用来存放"WHUT2018"8个字符组成的字符串
  • 1个字节的内存空间存放'\0'这个字符串结束标志(本质上也不是字符串的内容)

1.5 存储多个连续字符的两种方式:

  1. 字符串
  2. 字符数组

二、字符串和字符数组

2.1 sizeof

  1. sizeofC语言中的一个关键字,也是一个运算符,不是函数。
  2. sizeof运算符用来返回一个类型或者变量所占用的内存直接数。
  3. sizeof存在的意义:
  • ADT占几个字节和平台有关
  • UDT无法一眼看出占几个字节

2.2 strlen

  1. strlen是一个C语言库函数,其原型是:size_t strlen(const char *s);
  2. 这个函数接收一个字符串的指针,返回这个字符串的长度(以字节为单位)
  3. strlen返回的字符串长度不包含字符串结尾的'\0'

2.3 字符数组的初始化

字符数组有以下三种初始化的方式,其中方式1是最为方便常用

1. char str1[] = "WHUT2018";
2. char str2[] = {"WHUT2018"};
3. char str3[] = {'W','H','U','T','2','0','1','8','\0'};

测试代码

#include <stdio.h>
#include <string.h>

int main(void)
{
    char str0[9];
    char str1[]   = "WHUT2018";
    char str2[8]  = "WHUT2018";
    char str3[4]  = "WHUT2018";
    char str4[15] = "WHUT2018";

    printf("sizeof(str0) = %ld\n", sizeof(str0));
    printf("strlen(str0) = %ld\n", strlen(str0));
    printf("sizeof(str1) = %ld\n", sizeof(str1));
    printf("strlen(str1) = %ld\n", strlen(str1));
    printf("sizeof(str2) = %ld\n", sizeof(str2));
    printf("strlen(str2) = %ld\n", strlen(str2));
    printf("sizeof(str3) = %ld\n", sizeof(str3));
    printf("strlen(str3) = %ld\n", strlen(str3));
    printf("sizeof(str4) = %ld\n", sizeof(str4));
    printf("strlen(str4) = %ld\n", strlen(str4));

    return 0;
}

结果分析

arr_str.png

  1. 我们在编译的过程中会看到这样一个警告:
warning: initializer-string for array of chars is too long
     char str3[4] = "WHUT2018";

也就是说字符数组太短,后面的字符太长了。遇到这种情况,编译器会自动截断后面的部分。sizeof(str3) = 4是好理解的,那为什么strlen(str3) = 12 呢?其实这个值是随机的,strlen计算长度的时候是从首地址的字符开始,一直计数到'\0'的位置,所以这个值具体为多少得看栈的情况(栈内存是脏的),同理strlen(0)和strlen(str2)的值也是随机的。

  1. sizeof(arr)得到的永远是字符数组中字符元素的个数(也就是数组的大小),和数组是否初始化、初始化多少是没有关系的。

2.4 字符串初始化

就只有一种方法,直接看测试代码:

测试代码

#include <stdio.h>
#include <string.h>

int main(void)
{
    char *p = "WHUT2018";

    printf("sizeof(p) = %ld\n", sizeof(p));
    printf("strlen(p) = %ld\n", strlen(p));

    return 0;
}

测试结果

string.png

  • 64位处理器上64位操作系统上的64位编译器的环境下,sizeof(p)得到的永远是8(其他环境永远是4),因为sizeof(p)测的是字符指针p的字节数;
  • strlen才是真正测的字符串的长度(不包括'\0'

2.5 字符串与字符数组的比较

字符数组与字符串的本质差异

  1. 对字符数组char a[] = "WHUT2018";来说,就相当于定义了一个数组aa9个字节,分配在栈上。右值"WHUT2018"本身只存在于编译器中,编译器将他用来初始化字符数组a后丢弃掉(也就是说内存中并没有"WHUT2018"这个字符串的)。就相当于是:
char a[] = {'W', 'H', 'U', 'T', '2', '0', '1', '8', '\0'};
  1. 字符串char *p = "WHUT2018";定义了一个分配在栈上占4(或8)个字节的字符指针p和一个分配在代码段的占9个字节的字符串常量"WHUT2018",然后把代码段中的字符串的首地址赋值给p

字符串与字符数组的选用

通过上述对比我们可以看出字符串和字符数组有本质区别:

  • 字符数组本身自带内存空间,可以用来存储字符。
  • 字符串本身是指针,占4个字节的内存空间,这4个字节用来存储有效数据的首地址。

在工程领域上广泛使用的是字符串,因为字符串的存储地方非常灵活,如下所示:

#include <stdio.h>
#include <stdlib.h>
char str0[10];
int main(void)
{
    /*储存在bss段*/
    char *p1 = str0;
    /*存储在.text段*/
    char *str1 = "WHUT2018";
    /*存储在stack上*/
    char str2[10];
    char *p2 = str2;
    /*储存在heap上*/
    char *str3 = (char *)malloc(16);
    if(!str3)
    {
        printf("MALLOC ERROR!\n");
        return -1;
    }
    free(str3);
    str3 = NULL;
    return 0;
}

测试结果从略

发布了16 篇原创文章 · 获赞 1 · 访问量 4443

猜你喜欢

转载自blog.csdn.net/weixin_43955214/article/details/104161413