前言

务必理解指针与内存模型,不要死记硬背。

内存里的字符串

C语言中的字符串一般是char *类型的,这是怎样存在内存中的呢?

cchar *s = "NIHAO";

| s:400 |

|---|---|---|---|

|'N'|'I'|'H'|'A'|'O'| 0 |

|---|---|---|---|---|---|

|400|401|402|403|404|405|

如是上图,假设字母A处于内存的第400号格子,那么后面几个字母也是紧跟着的。

变量s本身并没有储存字符串,而存的是字符串的首地址400。也即,s指向这个字符串。

为什么没有专门一个字符串的类型而是要靠一个指针指向它呢?因为字符串的长度是不固定的,所以一个字符串还包含着长度信息,基本类型是无法处理数据结构的。

我们都知道字符串是以0结尾的,而且这个更像是一种约定,C编译器本身并没有对此做任何保证。比如这样

cchar s[3] = "asd";

puts(s); /* prints "asd" or something longer */

这样做是危险的,因为s只有3个格子,字符串结尾的0并没有放进去。如果在它后面的内存格子并不是0,那打印这个字符串时就跟我们预期的不一样了。

字符串常量不可写

c"abc"[0] = 'z'; /* wrong */

char *s = "abc";

s[0] = 'z'; /* wrong */

char s[5] = "abc";

s[0] = 'z' /* right */

当指针s指向的是字符串常量(即直接写在程序里面的字符串时),要注意它是不可写的

为啥用数组就没问题呢,因为数组的初始化和指针有点区别

cchar s[5] = "abc";

/* 相当于 */

char s[5];

strcpy(s, "abc");

如果担心自己会不小心写错,可以加上const关键字,这样编译的时候就会报错

这是一个好习惯,接下来的示例程序中都会这么写。

cconst char *s = "abc";

s[0] = 'z'; /* causes a compiling error instead of runtime error */

指针是要初始化才能使用的

c/* wrong */

char *s;

s[0];

上面的程序编译是能过的(可能有warning),但运行是一定会出错的,因为编译器并不知道s指向哪些格子。

c/* right */

const char *s = "NIHAO";

s[0];

这样,其实是隐式的分配了6个格子(包括字符串结尾的0),并让s指向它们

c/* right */

char s[6];

s[0];

c/* right */

char s[6] = "NIHAO";

s[0];

数组其实跟指针没什么区别,主要的区别是它在声明的时候就分配好了格子(方括号里的6就是告诉编译器给我6个格子),而且数组不能改变它的指向(也不能再要更多的格子)。

为什么不能用等号来比较字符串?

比较字符串

cconst char *s = "abcd";

const char *t = "abcd";

/* wrong */

if (s == t) {

...

}

/* right */

if (!strcmp(s, t)) {

...

}

因为s和t都没有存字符串的内容,它们存的是字符串的地址,如果用==比较,比较的是两个字符串的地址是否相同。我们希望比较的是内容是否相同。

请使用C语言库函数中的strcmp比较字符串是否相等

复制字符串

c/* tries to copy a string */

char s[5] = "abcd";

char *t = s;

t[3] = 'z';

puts(s); /* puts "abcz" */

上面这种做法让t和s指向同一字符串,修改t指向的内容,会发现s指向的内容也被修改了。这种做法没有错,经常会用到,但不一定是你想要的。

c/* wrong */

char *s = "abcd";

char *t; /* not initialized */

strcpy(t, s);

c/* right */

char *s = "abcd";

char t[10] = {0}; /* or char *t = (char *) malloc(5*sizeof(char)); */

strcpy(t, s);

使用strcpy复制字符串的内容而不是指针,但也要注意初始化t这个指针

怎样让函数得到一个字符串结果

int,float之类的很简单直接return就好

但现在我想写一个函数,它能够得到一个字符串

三种错误的或者不太好的做法

c/* no problem, but meaningless */

const char *f()

{

const char *s = "abcd";

return s;

}

/* wrong */

char *f()

{

char s[100];

/* do something with s */

return s;

}

/* result correct but not good */

char *f()

{

int n = 10;

char *s = (char *) malloc(n*sizeof(char));

/* do something with s */

return s;

}

第一种情况就不说了,返回一个字符串常量并没有问题因为它不可修改,但是不可修改也就没什么意义了。

第二种情况是完全错误的,返回一个局部的数组。这个数组的内存会在函数调用完后被收回,因此返回的指针指向的时候没有意义的地方。现代编译器一般都会对这个有warning。

第三种情况是返回malloc的指针。这种情况你可以得到正确的答案,但是不推荐,调用这个函数的人很有可能

不知道函数里面分配过内存

不知道应该什么时候free这部分内存

忘了free这部分内存

一旦没有注意,多次调用这个函数,结果就是内存溢出,这样的错误还非常不好排查,所以不推荐

正确的做法

正确的做法是把分配内存这种事情放在函数外面做,正如strcpy一样

cchar *strcpy(char *dest, const char *src)

{

int i;

for (i = 0; i < strlen(src); i++) {

dest[i] = src[i];

}

return dest;

}

dest是我们想要返回的字符串,它是从外面传进来的原因是我们不想在函数内部为它分配内存,而是在外面分配好了,里面只对这个字符串进行修改。

注意这里返回了char *但其实返回的正是原本传进来的dest,这里只是为了方便而已。

c语言 指针到字符串,C语言中的指针和字符串相关推荐

  1. c语言实现函数给主函数中的指针赋值的方法

    //利用二维指针.自从学了c之后,还没怎么用过二维指针,这么算是记住了 /* c语言实现函数给主函数中的指针赋值的方法*/#include<stdio.h>void f (int **p) ...

  2. pandas使用replace函数和正则表达式移除dataframe字符串数据列中头部指定模式字符串(Removing leading substring in dataframe)

    pandas使用replace函数和正则表达式移除dataframe字符串数据列中头部指定模式字符串(Removing leading substring in dataframe) 目录

  3. pandas使用replace函数和正则表达式移除dataframe字符串数据列中尾部指定模式字符串(Removing trailing substring in dataframe)

    pandas使用replace函数和正则表达式移除dataframe字符串数据列中尾部指定模式字符串(Removing trailing substring in dataframe) 目录

  4. c语言给结构体指针申请空间,结构体中的指针变量申请空间问题

    本人声明了一个结构体包含id,age,name三个属性 struct studentInfo { int id; int age; char *name[20]; }; 本人又创建了一个双向循环链表, ...

  5. php this指针的用法,C#_C#中this指针的用法示例,本文实例展示了C#中this指针的 - phpStudy...

    C#中this指针的用法示例 本文实例展示了C#中this指针的用法,对于初学者进一步牢固掌握C#有很大帮助,具体内容如下: 一.this指针是什么: 这里有一些面向对象编程的概念需要说明:类(Cla ...

  6. java int 指针_如何在Java中使用指针?

    小编典典 Java中的所有对象都是引用,你可以像使用指针一样使用它们. abstract class Animal {... } class Lion extends Animal {... } cl ...

  7. 共享内存中使用指针_详解c++中字符指针数组的使用

    之前有写过一篇叫c++中动态数组的使用,今来看看c++中的字符指针数组的使用. 涛哥:c++中的动态数组使用​zhuanlan.zhihu.com 指针数组,就指向指针的指针,很早以前在说指针的时候说 ...

  8. 字符串在Java中_字符和字符串在Java中的旅程

    以下是个人对java中字符和字符串的见解,如有疏漏之处,还请不吝赐教. 下面通过一个简单的程序来说明字符和字符串在Java中的旅程. 以字符 ' 中 '为例, 它的GBK编码是2个字节:0xd6d0, ...

  9. 用find在html中找字符串,Windows CMD中 find命令(字符串查找)

    在cmd窗口中敲下find /? 这条命令,然后重重地按下回车键. find /? 的执行结果很快,唰的一下,帮助信息全出来了,与findstr命令10多个开关比起来,find仅有5个开关,算得上小巫 ...

  10. java中正则匹配字符串长度_Java中使用正则表达式校验字符串

    Java中使用正则表达式校验字符串 正则表达式是某一位伟大的数学家发明的,现在已经形成了一个ISO标准,这个标准和编程语言没有关系.至于具体谁发明的,怎么发明的,我也忘记了:). 正则表达式简单理解就 ...

最新文章

  1. 数据结构-图-邻接矩阵-试在邻接矩阵存储结构上实现图的基本操作 matrix_insert_vertex 和matrix_insert_arc-icoding
  2. Almost Sorted Array HDU - 5532
  3. 数据结构—链表-链式存储
  4. 使用邻接矩阵实现有向图最短路径Dijkstra算法
  5. 莫陷入点击和评论陷阱
  6. keil5代码可以下载但无法进行串口通信
  7. 贪心科技机器学习训练营(七)
  8. Machine Learning Regression-Case Study
  9. Window和WindowManager--《Android开发艺术探索》阅读笔记——第八章
  10. 手机WiFi和热点为何不能同时开
  11. linux添加开机启动项
  12. 区块链学习笔记:区块链浏览器
  13. process has died
  14. 人之间的尊重是相互的_人与人之间彼此尊重是相互的,你若敬我一尺,我必敬你一丈...
  15. php网店系统与java网店系统的区别
  16. 4.导出UnityPackage(AssetDatabase.ExportPackage(assetPathName ,fileName ,ExportPackageOptions.Recurse )
  17. 【Android】使用LiveData KTX Builder让代码更简洁
  18. Rocky Linux 国内镜像源列表
  19. 机械臂长度的设计计算_Ubuntu(14.04-16.04-18.04)Openrave 配置安装用于计算机械臂可达空间...
  20. EXCEL中如何按字体做筛选

热门文章

  1. 静茹docker容器的几种方法_1-容器和docker基础知识
  2. 【MFC系列-第32天】控件自绘技术
  3. 循环自相关函数和谱相关密度(一)——公式推导
  4. 让你的数据和对象有源有出路,一文打尽,Java常用IO流处理流(处理字节流文件流)缓冲流、转换流、对象流等
  5. 微信开发修改button里的字体大小_微信小程序全栈开发课程【视频版】2.2 index页面完善...
  6. 从客户的角度看网站涉及的第一要素
  7. Qt 程序打包发布总结
  8. eclipse复制代码连接数据库404_再见,Eclipse ...
  9. Java Thread 总结
  10. c++编译时候fatal error C1075: end of file found before the left brace '{' at