三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

指针(3):strlen的模拟实现和传址调用

指针(3):strlen的模拟实现和传址调用

指针(3):strlen的模拟实现和传址调用

  • 1.strlen的模拟实现
    • 1.1 streln的介绍
    • 1.2 strlen的模拟实现
    • 1.3 注意
    • 1.4 补充
  • 2.传址调用

1.strlen的模拟实现

1.1 streln的介绍

库函数strlen的功能是求字符串的长度,统计的是字符串中\0之前的字符的个数
函数原型如下:

size_tstrlen(constchar*str);

参数str接收⼀个字符串的起始地址,然后开始统计字符串中 \0 之前的字符个数,最终返回⻓度。

strlen的运用:

#include<stdio.h>#include<string.h>intmain(){intret=strlen("abcdef");printf("%d",ret);return0;}

字符串“abcdef”在内存中实际存储的是a b c d e f \0共7个字节,strlen只统计\0之前字符的数量,因此输出结果为6。

1.2 strlen的模拟实现

根据上面对strlen函数原型的分析我们可以得知,如果要模拟实现只要从起始地址开始向后逐个字符的遍历,只要不是 \0 字符,计数器就+1,这样直
到 \0 就停⽌。

接下来就让我们来完成strlen模拟实现的代码:

#include<stdio.h>size_tmy_strlen(constchar*str){size_tcount=0;while(*str){str++;count++;}returncount;}intmain(){size_tret=my_strlen("abcdef");printf("%zu\n",ret);return0;}
  1. 首先将 “abcdef” 传到函数my_strlen
  2. 设置一个计数器count用来统计该字符串\0之前的字符的个数
  3. 通过while循环遍历该字符串\0之前的字符,每循环一次指针向后移动一位,并且使得计数器count加一
  4. 遍历完成后,返回count,函数模拟完成

1.3 注意

通过观察我们可以发现模拟函数实现时,我们定义的用来接收my_strlenret的类型为size_t(无符号整数类型),而上面我们在运用strlen时,用来接收其返回值时用的ret的类型为int,但最后输出结果都正确,这是为什么呢。

这是因为字符串“abcdef”中字符数量太小,用int来接收也足够,当字符数量够大时,用int接收就会出现错误,所以最好是使用size_t 和 %zu来接收和打印,当然在字符串比较小的时候,也可以用int 和 %d来接收和打印。

此外还有一个需要注意的点,由于strlen的返回值是无符号整数,因此两个strlen相减是不能得到负数的,看下面这个例子:

if(strlen("hi")-strlen("hello")>0)

在这个例子中,看似strlen(“hi”)得到数是2,strlen(“hello”)得到的数是4,2 - 4 < 0,但是strlen返回值是size_t,不存在负数只用正数,因此strlen(“hi”) - strlen(“hello”)所得到的值是一个很大的无符号正数,因此这个式子永远大于0.

1.4 补充

根据之前章节里面所提到的指针-指针,我们可以用另一种方式来实现strlen的模拟实现,即首先记录指针的起始位置,然后遍历字符串,得到指针结束时的位置,用结束指针-起始指针即可得到字符的数量。

#include<stdio.h>size_tmy_strlen(constchar*str){constchar*start=str;while(*str){str++;}returnstr-start;}intmain(){size_tret=my_strlen("abcdef");printf("%zu\n",ret);return0;}

注意:指针相减得到的数字是有符号整数类型,但return时会将其隐式转化size_t类型,因此无需进行强制类型转化。

2.传址调用

我们学习指针是为了解决问题,那么有哪些问题是只有指针才能解决的呢?

我们来看这样一个问题:写一个函数,交换两个整型变量的值。

经过一番思考,我们可能会写出这样的代码:

#include<stdio.h>voidswap(intx,inty){intc=0;c=x;x=y;y=c;}intmain(){inta=5;intb=3;swap(a,b);printf("%d %d",a,b);return0;}

让我们运行起来看看是否能够达到目的

我们发现这并没有产生交换的后果,这是为什么呢?

让我们来通过调试观察一下。

我们发现,在main函数内部创建了变量a b,他们的地址分别为0x000000026dc6ff6c4 和 0x000000026dc6ff6e4,而在调用swap函数时,我们将 a 和 b 传递给swap函数,swap函数在函数内部创建了形参x 和 y用来接收a 和 b的值,但是x的地址是0x000000026dc6ff6a0y的地址是0x000000026dc6ff6a8x和y确实接收到了a和b的值,不过x的地址和a的地址不⼀样,y的地址和b的地址不⼀样,相当于xy是独⽴的空间,而我们又知道数据都是存储在地址所对应的内存单元当中,他们的地址不同,因此他们所对应的内存单元也就不同,那么在swap函数内部交换x和y的值,⾃然不会影响a和b,当swap函数调⽤结束后回到main函数,a和b的值没法交换。,这就是传值调⽤

因此我们可以得出结论:实参传递给形参的时候,形参会单独创建⼀份临时空间来接收实参,对形参的修改不影响实参。

因此swap失败了

那我们该怎么办呢?

其实只需要将a和b的地址传递给swap函数,让swap函数通过地址间接的操作a和b的值,这样就能够达到交换a和b的值的功能了。

#include<stdio.h>voidswap(int*x,int*y){intc=0;c=*x;*x=*y;*y=c;}intmain(){inta=5;intb=3;swap(&a,&b);printf("%d %d",a,b);return0;}


由此我们看到代码成功的完成了任务这⾥调⽤swap函数的时候是将变量的地址传递给了函数,这种函数调⽤⽅式叫:传址调⽤

传址调⽤,可以让函数和主调函数之间建⽴真正的联系,在函数内部可以修改主调函数中的变量;所以未来函数中只是需要主调函数中的变量值来实现计算,就可以采⽤传值调⽤。如果函数内部要修改主调函数中的变量的值,就需要传址调⽤。

← 返回列表