4·C语言---字符串类型 与 字符串函数 笔记(面试)


1·C语言中其实没有原生字符串类型,是通过字符指针来间接实现的。

2·C语言使用指针来管理字符串
2·1 C语言定义字符串的方法:

char *p = "hellow";

这里的p就叫做字符串,但是实际上p只是一个字符指针(本质就是一个指针变量,只是p指向了一个字符串的首地址)。

2·2 C语言的字符串的本质:
指针指向头、固定尾部 的 地址相连 的一段内存
我的理解:C语言中字符串的3个核心要义:第一是用一个指针指向字符串头,第二是固定尾部(字符串总是以’\0‘来结尾),第三是注册字符串的各个字符的地址连续。

所以,字符串就是多个字符在内存中连续分布组成的字符结构。字符串的特点是指定了开头(字符串的指针)和结尾(\0),而没有指定长度(长度由开头地址和结尾地址相减得到)。
在这里插入图片描述
’\0‘ 的asic码值为0,与十进制的0不同。十进制的0对应asic码值为48.
’\0‘ 表示字符串的结尾标志,所以字符串中无法包含’\0‘ 。这个数就叫“魔数”。

2·3 指向字符串的指针 与 字符串 本身是分开的两个东西
(1)char *p = “Linux”;
在这句代码中,p本质是是一个字符指针,占4字节;”Linux“分配在代码段,占6字节(还要算上一个 ’\0‘ )实际上一共消耗10字节,p叫做字符串指针,可以理解为是字符串的引子,但是p本身不是字符串,而5字节的Linux才是真正的字符串,最后以斜杠0结尾。(\0 是用来字符串的结尾标志,是不属于字符串的,它不属于字符串,但是计算字节时要算上它)

2·4 字符数组 与 字符串的差异
(1) 字符数组 char a[ ] = “Linux”;
定义了一个数组,数组a占6字节,右值”Linux“ 本身只存在于编译器中,编译器将它用来初始化字符数组a后丢掉(也就是说内存中是没有”Linux“这个字符串的);这句话相当于是:char a[ ] = {‘l’,‘i’,‘n’,‘u’,‘x’,’\0’};
(2)字符串char *p = “linux”;
定义了一个字符指针p占4字节,分配在栈上(前提是p定义在 一个函数里面,当作局部变量);同时还定义了一个字符串”Linux“,分配在代码段;然后把代码段中的字符串的首地址(也就是 ‘l’ 的地址)赋值给p。

3· 字符串处理函数(面试)
3·1 c库中字符串处理函数在string.h 中,这个文件在ubuntu系统中在/usr/include中。

一、memcpy

man 3 memcpy:

void *memcpy(void *dest, const void *src, size_t n);

总结:
1· memcpy() 会复制 src 所指的内存内容的前 n个字节到 dest 所指的内存地址上。memcpy() 并不关心被复制的数据类型,只是逐字节地进行复制,这给函数的使用带来了很大的灵活性,可以面向任何数据类型进行复制。
2. source和dest所指的内存区域可能重叠,但是如果source和dest所指的内存区域重叠,那么这个函数并不能够确保source所在重叠区域在拷贝之前不被覆盖。而使用memmove可以用来处理重叠区域。函数返回指向dest的指针.
3. 如果目标数组destin本身已有数据,执行memcpy()后,将覆盖原有数据(最多覆盖n)。如果要追加数据,则每次执行memcpy后,要将目标数组地址增加到你要追加数据的地址。
4· dest 指针要分配足够的空间,也即大于等于 n 字节的空间。如果没有分配空间,会出现断错误。

注意:
1· source和dest都不一定是数组,任意的可读写的空间均可。
与 strcpy() 不同的是,memcpy() 会完整的复制 n个字节,不会因为遇到“\0”而结束。

【返回值】返回指向 dest 的指针。注意返回的指针类型是 void,使用时一般要进行强制类型转换。

//memcpy.c  作用:将s中的字符串复制到字符数组d中。
#include <stdio.h>
#include <string.h>
int main()
{
    char* s="GoldenGlobalView";
    chard[20];
    clrscr();
    memcpy(d,s,(strlen(s)+1));
    printf("%s",d);
    getchar();
    return 0;
}
运行结果:Golden Global View
作用:将s中第13个字符开始的4个连续字符复制到d中。(0开始)
#include<string.h>
int main(
{
    char* s="GoldenGlobalView";
    char d[20];
    memcpy(d,s+12,4);//从第13个字符(V)开始复制,连续复制4个字符(View)
    d[4]='\0';//memcpy(d,s+14*sizeof(char),4*sizeof(char));也可
    printf("%s",d);
   getchar();
   return 0;
}
运行结果:View
作用:复制后覆盖原有部分数据
#include<stdio.h>
#include<string.h>
intmain(void)
{
    char src[]="******************************";
    char dest[]="abcdefghijlkmnopqrstuvwxyz0123as6";
    printf("destination before memcpy:%s\n",dest);
    memcpy(dest,src,strlen(src));
    printf("destination after memcpy:%s\n",dest);
    return 0;
}
结果:
destination before memcpy:abcdefghijlkmnopqrstuvwxyz0123as6
destination after memcpy: ******************************as6

二、memccpy

void *memccpy(void *dest, const void *src, int c, size_t n);

函数说明:memccpy()用来拷贝src 所指的内存内容前n 个字节到dest 所指的地址上。与memcpy()不同的是,memccpy()会在复制时检查src中参数c 是否出现,若出现则返回dest 中值为c 的下一个字节地址。

返回值:返回指向dest 中值为c 的下一个字节指针。返回值为0 表示在src 所指内存前n 个字节中没有值为c的字节。

#include <string.h>
main(){
  char a[] = "string[a]";
  char b[] = "string(b)";
  memccpy(a, b, 'B', sizeof(b));
  printf("memccpy():%s\n", a);
}

结果:memccpy():string(b)

三、memset

void *memset(void *s, int c, size_t n);

函数的功能是:将指针变量 s 所指向的前 n 字节的内存单元用一个“整数” c 替换,注意 c 是 int 型。s 是 void* 型的指针变量,所以它可以为任何类型的数据进行初始化。

memset() 的作用是在一段内存块中填充某个给定的值。因为它只能填充一个值,所以该函数的初始化为原始初始化,无法将变量初始化为程序中需要的数据。用memset初始化完后,后面程序中再向该内存空间中存放需要的数据。

memset 一般使用“0”初始化内存单元,而且通常是给数组或结构体进行初始化。一般的变量如 char、int、float、double 等类型的变量直接初始化即可,没有必要用 memset。如果用 memset 的话反而显得麻烦。

当然,数组也可以直接进行初始化,但 memset 是对较大的数组或结构体进行清零初始化的最快方法,因为它是直接对内存进行操作的。

这时有人会问:“字符串数组不是最好用’\0’进行初始化吗?那么可以用 memset 给字符串数组进行初始化吗?也就是说参数 c 可以赋值为’\0’吗?”

可以的。虽然参数 c 要求是一个整数,但是整型和字符型是互通的。但是赋值为 ‘\0’ 和 0 是等价的,因为字符 ‘\0’ 在内存中就是 0。所以在 memset 中初始化为 0 也具有结束标志符 ‘\0’ 的作用,所以通常我们就写“0”。

memset 函数的第三个参数 n 的值一般用 sizeof() 获取,这样比较专业。注意,如果是对指针变量所指向的内存单元进行清零初始化,那么一定要先对这个指针变量进行初始化,即一定要先让它指向某个有效的地址。而且用memset给指针变量如p所指向的内存单元进行初始化时,n 千万别写成 sizeof§,这是新手经常会犯的错误。因为 p 是指针变量,不管 p 指向什么类型的变量,sizeof§ 的值都是 4。

# include <stdio.h>
# include <string.h>
int main(void)
{
    int i;  //循环变量
    char str[10];
    char *p = str;
    memset(str, 0, sizeof(str));  //只能写sizeof(str), 不能写sizeof(p)
    for (i=0; i<10; ++i)
    {
        printf("%d\x20", str[i]);
    }
    printf("\n");
    return 0;
}
根据memset函数的不同,输出结果也不同,分为以下几种情况:
memset(p, 0, sizeof(p));  //地址的大小都是4字节
0 0 0 0 -52 -52 -52 -52 -52 -52

memset(p, 0, sizeof(*p));  //*p表示的是一个字符变量, 只有一字节
0 -52 -52 -52 -52 -52 -52 -52 -52 -52

memset(p, 0, sizeof(str));
0 0 0 0 0 0 0 0 0 0

memset(str, 0, sizeof(str));
0 0 0 0 0 0 0 0 0 0

memset(p, 0, 10);  //直接写10也行, 但不专业
0 0 0 0 0 0 0 0 0 0

四、memcmp

int memcmp(const void *s1, const void *s2, size_t n);

函数说明:memcmp()用来比较s1和s2所指的内存区间前n个字符(也可以说成是字节)。字符串大小的比较是以ASCII码表上的顺序来决定,次顺序亦为字符的值。memcmp()首先将s1第一个字符值减去s2第一个字符的值,若差为0则再继续比较下个字符,若差值不为0则将差值返回。例如,字符串"Ac"和"ba"比较则会返回字符’A’(65)和’b’(98)的差值(-33)。
返回值:若参数s1和s2所指的内存内容都完全相同则返回0值。s1若大于s2则返回大于0的值。s1若小于s2则返回小于0的值。

#include <stdio.h>
 #include <string.h>
  
  int main(void)
  {
      char *a = "bcd";
      char *s = "abc";
      int iRet = 0;
  
      iRet = memcmp((void *)a, (void *)s, 3);
      printf("iRet[%d]\n", iRet);
      return 0;
  }

结果:iRet[65793]

五、memchr

void *memchr(const void *s, int c, size_t n);

功能:从buf所指内存区域的前count个字节查找字符ch。
说明:当第一次遇到字符ch时停止查找。如果成功,返回指向字符ch的指针;否则返回NULL。

// memchr.c
#include <syslib.h>
#include <string.h>
main()
{
char *s="Hello, Programmers!";
void *p;//因为memchr(,,);return void*p;
clrscr();
p=memchr(s,'P',strlen(s));
//p=(char *)memchr(s,'P',sizeof(s)); //s是一个指向char的指针,而任何指针都是个一个4字节的数,在这里应//该是要说明搜索整个字符串的长度,所以应该用strlen(s)
if(p)
printf("%s",p);
else
printf("Not Found!");
getchar();
return 0;
}

结果(应该是:):Programmers

六、strcpy

/* Copy N characters of SRC to DEST. */

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

strcpy() 会把 strSource 指向的字符串复制到 strDest。
必须保证 strDest 足够大,能够容纳下 strSource,否则会导致溢出错误。

返回值:目的字符串,也即 strDestination。

#include <stdio.h>
#include <string.h>
int main(){
    char dest[50] = { 0 };
    char src[50] = { "http://c.biancheng.net" };
    strcpy(dest, src);
    puts(dest);
    return 0;
}

结果:http://c.biancheng.net

七、strncpy

/* Copy no more than N characters of SRC to DEST. */

char *strncpy(char *dest, const char *src, size_t n);

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

八、strcat

/* Append SRC onto DEST. */

char *strcat(char *dest, const char *src);

C语言 strcat() 函数用来将两个字符串连接(拼接)起来。
strcat() 函数把 strSource 所指向的字符串追加到 strDest 所指向的字符串的结尾,所以必须要保证 strDest有足够的内存空间来容纳两个字符串,否则会导致溢出错误。
注意:strDest 末尾的\0会被覆盖,strSource 末尾的\0会一起被复制过去,最终的字符串只有一个\0。
返回值:指向 strDest 的指针。

使用C语言 strcat() 函数将用户输入的两个字符串拼接在一起。
 #include <stdio.h>
#include <string.h>
int main()
{
	int i =0 ;
    char str1[10] = "abcd";
    char str2[6] = "haha";
    
    strcat(str1, str2);
    printf("src:%s\n",str2 );
    printf("dest:%s\n",str1 );

    return 0;
}
结果:
cjj@cjj-virtual-machine:/mnt/hgfs/sharefile/C/4.6$ ./a.out 
src:haha
dest:abcdhaha

九、strncat

/* Append no more than N characters from SRC onto DEST. */

char *strncat(char *dest, const char *src, size_t n);

此函数从src指向的字符串到dest指向的字符串的末尾附加不超过n个字符,并附加一个终止的Null-character(\0)。字符串(src)的初始字符将覆盖字符串(dest)末尾的Null-character。因此,字符串(目标)的长度变为strlen(目标)+ n。但是,如果字符串(src)的长度小于n,则仅复制直到终止null-character的内容,并且字符串(dest)的长度变为strlen(src)+ strlen(dest)。
该行为是不确定的,如果:
1·字符串重叠。
2`dest数组的大小不足以附加src的内容。

// C,C++ program demonstrate functionality of strncat() 
#include <stdio.h> 
#include <string.h> 
  
int main() 
{ 
    
   // Take any two strings 
   char src[50] = "efghijkl"; 
   char dest[50]= "abcd"; 
   
   // Appends 5 character from src to dest 
   strncat(dest, src, 5); 
      
   // Prints the string 
   printf("Source string:%s\n", src); 
   printf("Destination string:%s", dest); 
     
   return 0; 
}
结果:
Source string:efghijkl
Destination string:abcdefgh

十、strcmp

int strcmp(const char *s1, const char *s2);

C语言 strcmp() 函数用于对两个字符串进行比较(区分大小写)。
参数 s1 和 s2 是参与比较的两个字符串。

strcmp() 会根据 ASCII 编码依次比较 s1 和 s2 的每一个字符,直到出现不到的字符,或者到达字符串末尾(遇见\0)。

返回值:
如果返回值 < 0,则表示 str1 小于 str2。
如果返回值 > 0,则表示 str2 小于 str1。
如果返回值 = 0,则表示 str1 等于 str2。

#include <stdio.h>
#include <string.h>
int main(){
    char str1[50] = { 0 };
    char str2[50] = { 0 };
    int i = 1;
    do {
        printf("******第%d次输入******\n", i);
        gets(str1);
        gets(str2);
        i++;
    } while ( strcmp(str1, str2) );
    return 0;
}

结果:
******1次输入******
123abc↙
456edf↙
******2次输入******
Java Linux C++ Python C# MySQL↙
java linux c++ python c# mysql↙
******3次输入******
Golang is great!↙
Golang is great!

十一、strncmp

int strncmp(const char *s1, const char *s2, size_t n);

说明:此函数功能即比较字符串str1和str2的前maxlen个字符。如果前maxlen字节完全相等,返回值就=0;在前maxlen字节比较过程中,如果出现str1[n]与str2[n]不等,则返回(str1[n]-str2[n])。

#include <string.h>
  #include <stdio.h>
  int main(void)
  {
  char *buf1 = "aaabbb", *buf2 = "bbbccc", *buf3 = "ccc";
  int ptr;
  ptr = strncmp(buf2,buf1,3);
  if (ptr > 0)
  printf("buffer 2 is greater than buffer 1\n");
  else if(ptr<0)
  printf("buffer 2 is less than buffer 1\n");
  ptr = strncmp(buf2,buf3,3);
  if (ptr > 0)
  printf("buffer 2 is greater than buffer 3\n");
  else if(ptr<0)
  printf("buffer 2 is less than buffer 3\n");
  return(0);
  }

结果:
打印结果为
buffer 2 is greater than buffer 1
buffer 2 is less than buffer 3

注意该函数判断 buffer 2和buffer 1大小的是根据子串aaa和bbb的Asc值的大小,而不是其长度。
注意该函数判断 buffer 3和buffer 2大小的是根据子bbb和ccc的Asc值的大小,而不是其长度。所以会出现buffer 3 > buffer2

另外,C里面非零的数值都为true.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值