字符串和数组指针传递详解

案例:把字符串传给函数

  • 代码

    #include <stdio.h>
    void fortune_cookie(char msg[]);
    int main(int argc, char const *argv[])
    {
        char quote[] = "Cookies make you fat";
        fortune_cookie(quote);
        return 0;
    }
    // 这里定义的msg[]数组是没有长度,为什么C会知道传过来的数组的长度,并且可以原样输出呢
    // 想都不用想,必然给你指针
    void fortune_cookie(char msg[])
    {
        printf("Message reads %s\n", msg);
        printf("char arry length %i\n",sizeof(msg));
    }
    
  • 输出结果

    Message reads Cookies make you fat
    char arry length 8
    

分析

数组变量好比指针,当你创建了一个数组,数组变量就可以当作指针使用,它指向数组在存储器中的起始地址。当C语言在函数中看到这样一行代码时:

char quote[]="Cookies make you fat";
//quote变量代表字符串中,第一个字符的地址

计算机会为字符串的每一个字符以及结束字符\0在栈上分配空间,并把首字符的地址和quote变量关联起来,代码中只要出现这个quote变量,计算机就会把它替换成字符串首字符的地址。其实,数组变量就好比一个指针:

// quote虽然是数组,但是可以当做指针变量使用
printf("The quote 字符串保存在: %p\n", quote);

执行结果输出的是一串16进制字符(指针),所以传递给函数的是指针,这就是为什么fortune_cookie()代码发生了奇怪的事情。看起来把字符串传给了fortune_cookie()函数,但实际上只传了一个指向字符串的指针:

msg其实是指针变量,sizeof(msg)不过是指针变量的大小罢了

这就是为什么sizeof运算符会返回奇怪结果,它只是返回了字符串指针的大小。指针在32位操作系统中占4字节,在64位操作系统中占8字节。

计算机运行代码时的操作

  1. 计算机看到函数:

    //此时计算机把msg当做一个指针进行处理(传参时)
    void fortune_cookie(char msg[])
    {
    	...
    }
    
  2. 紧接着,计算机看到了函数的内容:

    //消息的起始地址是msg。至于sizeof(msg),因为msg是指针变量,所以答案是8个字节,
    //保存指针时就用了这么多空间(结合电脑进行考虑32/64位)
    printf("Message reads: %s\n", msg);
    printf("msg occupies %i bytes\n", sizeof(msg));
    
  3. 计算调用函数:

    //quote是一个数组,然后要把quote变量传给fortune_cookie()。
    //最后将把参数msg设为quote数组在存储器中的起始地址。
    char quote[] = "Cookies make you fat";
    fortune_cookie(quote);
    

要点:

  1. 数组变量可以被用作指针。
  2. 数组变量指向数组中第一个元素。
  3. 如果把函数参数声明为数组,它会被当作指针处理。
  4. sizeof运算符返回某条数据占用空间的大小。
  5. 也可以对某种数据类型使用sizeof, 例如sizeof(int)。
  6. sizeof(指针)在32位操作系统中返回4, 在64位操作系统中返回8。
  7. sizeof()不是一个函数,它表示的是运算符

函数与运算符的区别

编译器会把运算符编译为一串指令;而当程序调用函数时,会跳到一段独立的代码中执行。所以程序是在编译时确定存储空间的大小。

为什么在不同的计算机上指针变量的大小不同

在32位操作系统中,存储器地址以32位数字的形式保存,所以它叫32位操作系统。 32位==4字节,所以64位操作系统要用8个字节来保存地址。

指针变量的相关概念

  1. 指针变量只不过是一个保存数字的变量罢了。
  2. 可以用&运算符找到它的地址。
  3. 可以把指针转化为一般的数字

数组变量与指针又不完全相同

虽然可以将数组变量用作指针,但两者还是有一些区别,为了区分它们,考虑下面这段代码:

#include <stdio.h>

int main() {
    int contestants[] = {1, 2, 3};
    int *choice = contestants;
    printf("%i\n", *choice);
    contestants[0] = 2;
    contestants[1] = contestants[2];
    contestants[2] = *choice;
    printf("%i\n", *choice);
    printf("choose %i index friend\n", contestants[2]);
    printf("choose %i index friend", contestants[1]);
    return 0;
}

输出结果:

1
2
choose 2 index friend
choose 3 index friend

分析:

#include <stdio.h>

int main() {
    int contestants[] = {1, 2, 3};
		//choice此时是contestants数组的地址
    int *choice = contestants;
    printf("%i\n", *choice);
    contestants[0] = 2;
    contestants[1] = contestants[2];
    contestants[2] = *choice;
		//contestants[2]==*choice==contestants[0]==2
    printf("%i\n", *choice);
    printf("choose %i index friend\n", contestants[2]);
    printf("choose %i index friend", contestants[1]);
    return 0;
}

虽然可以将数组变量用作指针,但两者还是有一些区别,为了区分它们,考虑下面这段代码:

#include <stdio.h>

int main() {
    char s[] = "How big is it?";
    char *t = s;
    printf("%p\n", s);
    printf("%i\n", s);//6422025
    printf("%i\n", sizeof(s));//15
    printf("%i\n", sizeof(t));//8
    printf("%i\n", sizeof(*t));//1
    printf("%i\n", sizeof(*s));//1
}

分析:

  • sizeof(数组)是数组的大小

    sizeof(指针)返回了4或8,因为4和8分别是32位和64位操作系统上指针的大小。但如果对数组变量使用sizeof, C语言就开窍了,它知道你想得到数组在存储器中的长度

  • 数组的地址是数组的地址,即为指针变量

    指针变量是一个用来保存存储器地址的变量,那数组变量呢?如果对数组变量使用&运算符,结果是数组变量本身。

    当程序员写下&s时,表示“数组s的地址是?”,数组s的地址就是s;但如果他写的是&t,则表

    示“变量t的地址是?”。

  • 数组变量不能指向其他地方

    当创建指针变量时,计算机会为它分配4或8字节的存储空间。

    但如果创建的是数组呢?计算机会为数组分配存储空间,但不会为数组变量分配任何空间,编译器仅在出现它的地方把它替换成数组的起始地址。(解指针操作)

    但是由于计算机没有为数组变量分配空间,也就不能把它指向其他地方。

&以及*运算符说明

指针退化说明:

  1. 数组变量和指针变量有一点小小的区别,所以把数组赋给指针时千万要小心。 假如把数组赋给指针变量, 指针变量只会包含数组的地址信息, 而对数组的长度一无所知, 相当于指针丢失了一些信息。 我们把这种信息的丢失称为退化。
  2. 只要把数组传递给函数, 数组免不了退化为指针, 但需要记清楚代码中有哪些地方发生过数组退化, 因为它们会引发一些不易察觉的错误

为什么数组的指正从0开始

数组变量可以用作指针,这个指针指向数组的第一个元素,也就是说除了方括号表示法,还可以用*运算符读取数组的第一个元素,像这样:

int drinks[] = {4, 2, 3};
//下列两行代码时等价的
printf("第一单: %i 杯\n", drinks[0]);
printf("第一单: %i 杯\n", *drinks);

地址只是一个数字,所以可以进行指针算术运算,比如为了找到存储器中的下一个地址,可以增加指针的值。既可以用方括号加上索引值2来读取元素,也可以对第一个元素的地址加2:

printf("第三单: %i 杯\n", drinks[2]);
printf("第三单: %i 杯\n", *(drinks + 2));

总之,表达式drinks[i]*(drinks + i)是等价的,这解释了为什么数组要从索引0开始,所谓索引,其实就是为了找到元素的地址单元,指针需要加上的那个数字。

为什么指针有类型

既然指针只是地址,为什么指针变量有类型?为什么不能用一种通用类型的变量保存所有的指针?

  1. 因为指针算术运算会暗渡陈仓。如果对char指针加1,指针会指向存储器中下一个地址,那是因为char就占1字节。
  2. 如果是int指针呢? int通常占4字节,如果对int指针加1,编译后的代码就会对存储器地址加4
//@author:five-five
#include <stdio.h>

int main() {
    int nums[] = {1, 2, 3};
    printf("nums address %p\n", nums);
    printf("nums + 1 address %p\n", nums + 1);
}

如果运行上面这段代码,就会发现两个地址相隔不止一个字节。指针之所以有类型,是因为编译器在指针算术运算时需要知道加几。

要点

  1. 数组变量可以用作指针,但数组变量和指针又不完全相同。
  2. 对数组变量和指针变量使用sizeof, 效果不同。
  3. 数组变量不能指向其他地方。
  4. 把数组变量传给指针, 会发生退化。
  5. 索引的本质是指针算术运算,所以数组从0开始。
  6. 指针变量具有类型, 这样就能调整指针算术运算
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
JavaScript中有许多用于字符串数组处理的方法。下面是一些常用的方法: 字符串方法: 1. `length`:返回字符串的长度。 2. `charAt(index)`:返回指定位置的字符。 3. `charCodeAt(index)`:返回指定位置字符的Unicode值。 4. `toLowerCase()`:将字符串转换为小写。 5. `toUpperCase()`:将字符串转换为大写。 6. `trim()`:去除字符串两端的空格。 7. `slice(start, end)`:提取字符串的一部分,包括起始索引但不包括结束索引。 8. `split(separator)`:将字符串按指定分隔符分割为数组数组方法: 1. `length`:返回数组的长度。 2. `concat(arr)`:将数组与其他数组或值连接在一起,返回一个新数组。 3. `join(separator)`:将数组的所有元素转换为字符串,通过指定的分隔符连接起来。 4. `pop()`:移除并返回数组中的最后一个元素。 5. `push(item1, item2, ...)`:向数组末尾添加一个或多个元素,并返回新数组的长度。 6. `shift()`:移除并返回数组中的第一个元素。 7. `unshift(item1, item2, ...)`:向数组开头添加一个或多个元素,并返回新数组的长度。 8. `slice(start, end)`:提取数组的一部分,包括起始索引但不包括结束索引。 9. `splice(start, count, item1, item2, ...)`:从指定位置开始修改数组,删除/替换/添加元素。 这只是一部分常用的方法,JavaScript还有很多其他处理字符串数组的方法。你可以查阅相关文档来了解更多细节。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

five-five

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值