C语言自学完备手册(30)——指针(4)

自定义View系列教程00–推翻自己和过往,重学自定义View
自定义View系列教程01–常用工具介绍
自定义View系列教程02–onMeasure源码详尽分析
自定义View系列教程03–onLayout源码详尽分析
自定义View系列教程04–Draw源码分析及其实践
自定义View系列教程05–示例分析
自定义View系列教程06–详解View的Touch事件处理
自定义View系列教程07–详解ViewGroup分发Touch事件
自定义View系列教程08–滑动冲突的产生及其处理


探索Android软键盘的疑难杂症
深入探讨Android异步精髓Handler
详解Android主流框架不可或缺的基石
站在源码的肩膀上全解Scroller工作机制


Android多分辨率适配框架(1)— 核心基础
Android多分辨率适配框架(2)— 原理剖析
Android多分辨率适配框架(3)— 使用指南


讲给Android程序员看的前端系列教程(图文版)
讲给Android程序员看的前端系列教程(视频版)
Android程序员C语言自学完备手册


版权声明


在本篇博客中介绍字符串和指针的密切关系。

字符串的两种实现方式

字符串常见的实现方式有如下两种:

  • 方式1:利用数组实现字符串
  • 方式2:利用指针实现字符串

请看如下示例:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    //方式1:利用数组实现字符串
    char str[]="ABC";
    //方式2:利用指针实现字符串
    char *ptr="123";
    printf("str=\"%s\",sizeof(str)=%d\n",str,sizeof(str));
    printf("ptr=\"%s\",sizeof(ptr)=%d,sizeof(\"123\")=%d\n",ptr,sizeof(ptr),sizeof("123"));

    return 0;
}

在本示例中用两种方式实现了字符串。

结果如下:

str="ABC",sizeof(str)=4
ptr="123",sizeof(ptr)=4,sizeof("123")=4

图示如下:
在这里插入图片描述

在示例代码和图示之后,我们再来分别深入地学习这两种字符串的实现方式。

利用数组实现字符串

在方式中,str是char[4]型的数组,各元素从头开始依次使用’A’、‘B’、‘C’、’\0’进行初始化。在该方式中,数组str占用4个字节的内存空间。
既然是数组,那么可通过下标的方式访问元素,例如:

printf("str[0]=%c\n",str[0]);//结果为:str[0]=A

请看如下示例:请遍历字符串中的每个字符

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i=0;
    char str[]="123";
    while(str[i]){
        printf("%c\n",str[i++]);
    }
    return 0;
}

结果如下:

1
2
3

在此思考一个问题:*p的含义是什么?或者说*p的值是多少?请看如代码:

char str[]="123";
char *p=str;
printf("*p=%c\n",*p);//*p=1

*p指向了字符数组中的第一个元素,它的值是1。所以,还有较刚才更为简单巧妙的方法:利用指针的自增或自减来访问当前元素的下一个或者上一个元素,代码修改如下:

int main()
{
    char str[]="123";
    char *p=str;
    while(*p){
        printf("%c\n",*p++);
    }
    return 0;
}

了解了元素的访问,再来看元素的修改。当利用数组实现字符串时可通过下标修改元素;请看如下示例:

int main()
{

    char str[]="123";
    str[0]='4';
    printf("str[0]=%c\n",str[0]);//结果为:str[0]=4
    return 0;
}

但是,一定要注意:在该方式中不可以再次为str赋值,例如:str="DEF";,这一点在之前我们也反复强调过:赋值表达式的左侧不可为数组名! 虽然说左侧的数组名会被解释为数组起始元素的地址,但是依然不可以改写其值。其实,也可以反向思考一下:如果可以赋值,那岂不是就改变了原数组在内存中的地址了?这是不可以的。

利用指针实现字符串

在该方式中,ptr是指向char型变量的指针,它的初始值为字符串字面量"123"并指向该字面量的第一个字符。从图示中,我们也可以看出来指针ptr和字符串字面量“123”都会占用内存空间(即sizeof(ptr)=4,sizeof("123")=4)。也就是说:对于同一个字符串而言,用指针实现的字符串比用数组实现的字符串需要更多的内存空间。

接下来,我们来看看数据的访问。
在利用指针实现字符串的方式中依然可以采用下标的方式访问字符串。

请看如下示例:请遍历字符串中的每个字符

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i=0;
    char *ptr="123";
    while(ptr[i]){
        printf("%c\n",ptr[i++]);
    }
    return 0;
}

结果如下:

1
2
3

按照之前的思路继续优化代码,修改如下:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    char *ptr="123";
    while(*ptr){
        printf("%c\n",*ptr++);
    }
    return 0;
}

在该示例中直接使用了*ptr++让指针不断指向下一个元素。

好嘞,既然之前可以使用下标的方式访问元素,那么就继续通过该方式修改元素,例如:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    char *ptr="123";
    ptr[0]='9';
    printf("ptr[0]=%c\n",ptr[0]);
    return 0;
}

运行代码可以发现:程序不能够正常执行!这是为什么呢?在利用指针实现的字符串时,该字符串常量被定义为只读;如果试图通过指针修改这个字符串的值程序会出现未定义的异常行为!也就是说:在利用指针实现字符串时可通过指针访问字符串字面量中的内容,但是不能通过指针修改字符串字面量中的内容!

另外,在利用指针实现字符串时指针(例如ptr)不可进行如下声明:

char *ptr={'1','2','3','\0'};

因为数组采用的{'1','2','3','\0'}形式的初始值,它不能够被赋值给单一的变量(例如指针ptr),应该被赋值给arrayName[ ];这点是大家容易犯错的。

在利用指针实现字符串时可以再次为指针(例如指针p)赋值么?答案是肯定的。例如:

char *p="123";
p="456";

指针p原本指向字符串字面量“123”之后被再次赋值,指向了新的字符串字面量“456”,此时原来的“123”并不会消失,但变成了无用的内存垃圾;图示如下:
在这里插入图片描述

练习1:求字符串长度

代码如下:

#include <stdio.h>
#include <stdlib.h>

int getStringLength(const char *p){
    int len=0;
    while(*p++){
        len++;
    }
    return len;
}

int main()
{
    char str[128];
    printf("请您输入字符串:");
    scanf("%s",str);
    printf("字符串%s的长度是%d\n",str,getStringLength(str));
    return 0;
}

结果如下:

请您输入字符串:asdf
字符串asdf的长度是4
练习2:复制键盘输入的字符串

代码如下:

#include <stdio.h>
#include <stdlib.h>

char* copyString(char *destination,char *source){
    char *temp=destination;
    while(*destination++ = *source++){

    }
    return temp;
}

int main()
{
    char source[128];
    char destination[128];
    printf("请您输入需要复制的字符串:");
    scanf("%s",source);
    printf("需要复制的字符串source=%s\n",source);
    puts("开始复制");
    puts("复制结束");
    printf("复制得到的字符串destination=%s",copyString(destination,source));
    return 0;
}

结果如下:

请您输入需要复制的字符串:qwert
需要复制的字符串source=qwert
开始复制
复制结束
复制得到的字符串destination=qwert

在本示例中充分运用了之前所讲的知识点——利用指针的自增或自减来访问当前元素的下一个或者上一个元素。


字符串处理库函数

在此介绍几个常用的由系统提供用于处理字符串的函数。

  • strlen(char *str) 获取字符串长度;该函数返回不包含null字符在内的字符串长度
  • strcpy(char *destination,char *source) 复制字符串,即将source复制至destination
  • strncpy(char *destination,char *source,int n) 复制字符串,并控制复制的字符个数。
  • strcat(char *str1,char *str2) 连接字符串(cat为concatenate的缩写),即将str2连接至str1的末尾。注意:str1须有足够的空间容纳str2
  • strncat(char *str1,char *str2,int n) 连接字符串,并控制连接的字符个数。
  • strcmp(char *str1,char *str2) 比较字符串的大小(cmp为compare的缩写)。当str1等于str2时返回0,str1大于str2时返回正整数,str1小于str2时返回负整数
  • strncmp(char *str1,char *str2,int n) 比较字符串的大小,并控制比较的字符个数。
  • atoi(char *str) 将字符串类型的数字转换成int类型数字,atoi为ascii to integer的缩写
  • atol(char *str) 将字符串类型的数字转换成long类型数字,atol为ascii to long的缩写
  • atof(char *str) 将字符串类型的数字转换成double类型数字,atof为ascii to floating point numbers的缩写

函数小结

  1. strlen( )、strcpy( )、strncpy( )、strcat( )、strncat( )、strcmp( )、strncmp均由<string.h>头文件提供
  2. 在使用strcpy( )、strncpy( )、strcat( )、strncat( )时要注意内存重叠的问题
  3. atoi( )、atol( )、atof( )均由<stdlib.h>头文件提供。其中,stdlib是standard library的缩写

示例如下:

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

//获取字符串的长度--strlen()
int getStringLen(char *str){
    int len=strlen(str);
    return len;
}

//复制字符串--strcpy(destination,source);
char* copyString(char *destination,char *source){
    strcpy(destination,source);
    return destination;
}

//复制字符串--strncpy(destination,source,n);
//将source复制到destination。若source的长度大于等于n,则复制到第n个字符为止;否则用null字符填充剩余部分。
char* copyNString(char *destination,char *source,int n){
    strncpy(destination,source,n);
    return destination;
}

//连接字符串--strcat(str1,str2);
//将str2连接到str1的末尾
char* concatenateString(char *str1,char *str2){
    strcat(str1,str2);
    return str1;
}

//连接字符串--strncat(str1,str2,n);
//将str2连接到str1的末尾,若str2的长度大于n则截断
char* concatenateNString(char *str1,char *str2,int n){
    strncat(str1,str2,n);
    return str1;
}

//比较字符串大小--strcmp(str1,str2);
//str1等于str2时返回0,str1大于str2时返回正整数,str1小于str2时返回负整数
int compareString(char *str1,char *str2){
    int result=strcmp(str1,str2);
    return result;
}

//比较字符串前n个字符大小--strncmp(str1,str2,n);
//str1等于str2时返回0,str1大于str2时返回正整数,str1小于str2时返回负整数
int compareNString(char *str1,char *str2,int n){
    int result=strncmp(str1,str2,n);
    return result;
}

//将字符串类型的数字转换成int类型数字
int asciiToInteger(char *str){
    int result=atoi(str);
    return result;
}

//将字符串类型的数字转换成long类型数字
long asciiToLong(char *str){
    long result=atol(str);
    return result;
}

//将字符串类型的数字转换成double类型数字
double asciiToDouble(char *str){
    double result=atof(str);
    return result;
}

int main()
{
    char *str01="helloWorld";
    int len=getStringLen(str01);
    printf("len=%d\n",len);

    char str02[10];
    char *str03="everyone";
    char *copyResult1=copyString(str02,str03);
    printf("copyResult1=%s\n",copyResult1);

    char str04[10];
    char *str05="include";
    int n=5;
    char *copyResult2=copyNString(str04,str05,n);
    str04[n]='\0';
    printf("copyResult2=%s\n",copyResult2);

    char strArray06[20]="come";
    char *str07=" on";
    char *catResult1=concatenateString(strArray06,str07);
    printf("catResult1=%s\n",catResult1);

    char strArray08[20]="come";
    char *str09=" on,you are the best";
    char *catResult2=concatenateNString(strArray08,str09,7);
    printf("catResult2=%s\n",catResult2);

    char *str10="cba";
    char *str11="nba";
    int cmpResult1=compareString(str10,str11);
    printf("cmpResult1=%d\n",cmpResult1);

    char *str12="zba";
    char *str13="nba";
    int cmpResult2=compareNString(str12,str13,1);
    printf("cmpResult2=%d\n",cmpResult2);

    char *str14="9527";
    int asciiToResult1=asciiToInteger(str14);
    printf("asciiToResult1=%d\n",asciiToResult1);

    char *str15="234567890";
    long asciiToResult2=asciiToLong(str15);
    printf("asciiToResult2=%d\n",asciiToResult2);

    char *str16="3.14";
    double asciiToResult3=asciiToDouble(str16);
    printf("asciiToResult3=%.2f\n",asciiToResult3);

}

结果如下:

len=10
copyResult1=everyone
copyResult2=inclu
catResult1=come on
catResult2=come on,you
cmpResult1=-1
cmpResult2=12
asciiToResult1=9527
asciiToResult2=234567890
asciiToResult3=3.14

Process returned 0 (0x0)   execution time : 1.049 s
Press any key to continue.


字符串数组

之前介绍了实现字符串的两种方式,现在在该基础上学习实现字符串数组的两种方式。

字符串常见的实现方式有如下两种:

  • 方式1:利用二维字符数组实现字符串数组
  • 方式2:利用char*型指针数组实现字符串数组

请看如下示例:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i;
    //方式1:利用二维字符数组实现字符串数组
    char a[][5]={"LISP","C","Ada"};
    //方式2:利用char*型指针数组实现字符串数组
    char *p[]={"PAUL","X","MAC"};
    printf("字符串数组a所占内存大小为=%d\n",sizeof(a));
    for(i=0;i<3;i++){
        printf("a[%d]=%s\n",i,a[i]);
    }
    printf("字符串数组p所占内存大小为=%d\n",(sizeof(p)+sizeof("PAUL")+sizeof("X")+sizeof("MAC")));
    for(i=0;i<3;i++){
        printf("p[%d]=%s\n",i,p[i]);
    }
    return 0;
}

结果如下:

字符串数组a所占内存大小为=15
a[0]=LISP
a[1]=C
a[2]=Ada
字符串数组p所占内存大小为=23
p[0]=PAUL
p[1]=X
p[2]=MAC

图示如下:
在这里插入图片描述

在示例代码和图示之后,我们再来分别深入地学习这两种字符串数组的实现方式。

利用二维字符数组实现字符串数组

在该示例中,数组a是3行5列的二维字符数组,所占内存为15字节。因为并非所有字符串的长度是一致的,所以该数组中会存在一些未使用的部分,例如a[1][2]–a[1][4]。
很多书中都会讲到二维数组,但是都缺乏一个形象的示图。庆幸的是《明解C语言(第3版)》的321页给出了一张非常形象的插图,如上所示。由此可见:在利用二维字符数组实现字符串数组时,所有元素是连续排列的! 即:

  • 字符串数组中的“LISP”里的每个字符以及‘\0’分别存放于a[0][0]、a[0][1]、a[0][2]、a[0][3]、a[0][4]
  • 字符串数组中的“C”里的每个字符以及‘\0’分别存放于a[1][0]、a[1][1]、a[1][2]、a[1][3]、a[1][4]
  • 字符串数组中的“Ada”里的每个字符以及‘\0’分别存放于a[2][0]、a[2][1]、a[2][2]、a[2][3]、a[2][4]
利用char*型指针数组实现字符串数组

在该示例中指针p是元素类型为char*、元素个数为3的数组。数组中各元素p[0]、p[1]、p[2]的初始值是分别指向各字符串字面量的首字符“P”、“X”、“M”的指针。因此,除了字符串占用的11字节的内存以外;数组还p占用即12(3*4)个字节的内存。至于,字符串数组在内存中分配请详细查看上图即可。从这里我们也能发现:利用char*型指针数组实现字符串数组时无法保证字符串排列的顺序和连续性!

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

谷哥的小弟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值