• C++指针解读(6)-- 指针和字符串


    1、字符串的基本概念

    (1)字符串的存储

    字符串是存放在字符数组中的。比如字符串“hello”,在内存中是这么存放的:

    这里要注意,在字符串的最后会加上一个'\0',也被称为NUL字符,表示字符串的结束位。比如定义char str[] = "hello",则sizeof(str)的长度是6,不是5。

    (2)字符串的访问

    既然字符串是字符按数组的方式存储的,我们可以用访问一维数组的方式来访问字符串。

    1. char str[] = "hello";
    2. printf(" %c\n", str[1]);
    3. char* ch = &str[1];
    4. printf(" %c\n", *ch);

    (3)字符串常量池

    用char数组str[]的方式定义字符串,我们也可以用下面的方式定义一个字符串:

    char *str = "hello";

    这里的str其实是一个char*型的指针变量,它指向字符串"hello"的首地址。

    这种方式定义字符串时,"hello"是字符串常量,这个常量就放在字符串常量池中。

    我们可以用代码来验证下:

    1. const char* pstr1 = "hello";
    2. const char* pstr2 = "hello";
    3. printf(" p1 = %p, p2 = %p\n", pstr1, pstr2);

    运行后发现pstr1, pstr2这2个地址是相同的,且每次运行地址都一样。这说明这个字符串常量是编译时就确定好的。

    (4)sizeof()和strlen()

    sizeof是C语言中的一个单目运算符,用来计算数据类型所占空间的大小,单位为字节;而strlen是一个函数,用来计算字符串长度(不包括字符串结束位NUL)。

    1. char str1[] = "hello";
    2. char str2[10] = "hello";
    3. printf(" sizeof(str1) = %d, strlen(str1) = %d\n", sizeof(str1), strlen(str1));
    4. printf(" sizeof(str2) = %d, strlen(str2) = %d\n", sizeof(str2), strlen(str2));

    2、定义字符串的几种方式

    (1)char str[] = {'h', 'e', 'l', 'l', 'o', '\0'};

    定义了一个字符数组,长度为6,数组里的元素分布如下:{'h', 'e', 'l', 'l', 'o', '\0'}。

    假如末尾没有NUL字符:char str[] = {'h', 'e', 'l', 'l', 'o' };

    printf()输出str的值会是什么呢?sizeof()和strlen()的值分别是什么呢?

    1. char str1[] = { 'h', 'e', 'l', 'l', 'o' };
    2. printf(" %s\n", str1);
    3. printf(" sizeof(str1) = %d, strlen(str1) = %d\n", sizeof(str1), strlen(str1));

    这时,输出的str1是hello+一些乱码;sizeof(str1)是5,而strlen(str1)的值是个变数,每次的输出不一定相同。

    因为字符串是以NUL作为字符串结束位的,现在缺少这个结束位,程序就没有办法做出正确的处理了。

    (2)char str[] = "hello";

    定义了一个字符数组,长度为6,系统会在末尾自动加上字位结束符NUL。

    (3)char *pstr = "hello";

    定义了一个字符指针变量,指向常量池中的字符常量"hello"。

    4、字符指针变量和字符数组的比较

    char *str1定义的是一个字符指针变量;char str2[10]定义的是一个字符数组,数组名代表数组元素首地址。

    这是两个不同的概念,主要有以下区别:

    (1)字符指针变量存放的是地址,指向字符串的第一个元素;字符数组就是一个存放字符的数组,一个元素就是一个字符。

    (2)字符指针变量的值是可以改变的,但字符数组名的值不能改变(它只代表数组的首地址)。

    1. const char *str1 = "hello";
    2. printf(" %c\n", *str1);
    3. str1++; //可以更改
    4. printf(" %c\n", *str1);
    5. char str2[10] = "hello";
    6. str2++; //报错

    (3)字符指针指向的字符串的内容不能被更改,但字符数组中各元素的值可以被更改。

    1. const char *str1 = "hello";
    2. str1[2] = 'a'; //报错
    3. char str2[10] = "hello";
    4. str2[2] = 'a'; //可以

    (4)我们可以用str2[1], *(str2 + 1)这样的方式访问数组元素;对于字符指针变量,同样也可以用str1[1], *(str1 + 1)这样的方式访问。

    1. const char *str1 = "hello";
    2. printf(" %c, %c \n", str1[1], *(str1 + 1));
    3. char str2[10] = "hello";
    4. printf(" %c, %c \n", str2[1], *(str2 + 1));

    (5)初始化的含义

    char *str1 = "hello";

    等价于:

    1. char *str1;
    2. str1 = "hello"; //把字符串的首地址给str

    但字符数组的初始化:

    char str2[10] = "hello";

    不等价于:

    1. char str2[10];
    2. str2 = "hello"; //报错

    其实这里的str1是个char *类型的指针变量,即字符指针变量,把字符数组的首地址赋给这个指针变量是没问题的。

    而str2是数组的名字,代表字符数组首元素的地址,我们可以给这个地址赋值,但不能让地址去指向另外一个地址。

    1. char str1[10];
    2. *str1 = 'h'; //可以给地址赋值
    3. strcpy(str1, "hello"); //可以直接内存拷贝

    5、字符指针作函数参数

    前面我们知道可以用两种方式来定义字符串,一种是字符数组,另一种是字符串指针变量。当函数的形参是字符串时,我们可以传字符数组的首地址,也可以传字符串指针变量。

    (1)传字符数组的首地址

    1. void print_str(char str[]) {
    2. int i = 0;
    3. while (str[i] != '\0') {
    4. printf("%c", str[i]);
    5. i++;
    6. }
    7. }
    8. int main()
    9. {
    10. const char *str1 = "hello";
    11. print_str((char*)str1);
    12. char str2[10] = "hello";
    13. print_str(str2);
    14. return 0;
    15. }

    (2)传字符串指针变量

    1. void print_str(char* str) {
    2. int i = 0;
    3. while (str[i] != '\0') {
    4. printf("%c", str[i]);
    5. i++;
    6. }
    7. }

  • 相关阅读:
    【GUI】-- 11 贪吃蛇小游戏之绘制静态的小蛇
    gateway的GlobalFilter调用feign后,发现GlobalFilter不起作用
    贪吃蛇项目实践!(下)
    Dubbo 实战 - Mock 调用
    Vue.js 框架源码与进阶 - Virtual DOM 的实现原理
    是真是假,AI可根据声音检测是否感染新冠 准确率达89%
    JPA-Specification常用条件查询构造方式
    java设计模式之原型模式
    OpenCV类VideoCapture构造函数中参数apiPreference的可选值及意义
    十三、DataFrame表格型数据结构
  • 原文地址:https://blog.csdn.net/mars21/article/details/133847993