《C和指针》读书笔记(第九章 字符串、字符和字节)(下)

简介: 《C和指针》读书笔记(第九章 字符串、字符和字节)(下)

8.2 字符转换

转换函数白大写字母转换为小写字母或者把小写字母转换为大写字母。有两个函数可供调用。toupper函数返回其参数对应的大写形式tolower函数返回其参数对应的小写形式

int tolower(int  ch);
int toupper(int  ch);

举个实际的例子:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main()
{
  char temp1[] = "To carry things with great virtue";
  char temp2[] = "To carry things with great virtue";
  //全转换为大写
  for (int i = 0; i < strlen(temp1); i++)
  {
    if (islower(temp1[i]))
      temp1[i] = toupper(temp1[i]);
    printf("%c",temp1[i]);
  }
  printf("\n-----------------------------\n");
  //全转换为小写
  for (int i = 0; i < strlen(temp2); i++)
  {
    if (isupper(temp2[i]))
      temp2[i] = tolower(temp2[i]);
    printf("%c", temp2[i]);
  }
  printf("\n");
  system("pause");
  return 0;
}

打印输出:

可以看到,我们可以按照自己的意愿去调整字符串中字母的大小写。

9 内存操作

字符串一般以NUL结尾,但是如果我们想要处理中间包含NUL的字符串,或者任意长度的字节序列的时候,前面的函数就显得比较乏力,或者说根本没法用。不过我们可以有另外一组函数,供我们使用,去完成实际开发中的一些需求。下面是它们的原型。

注意:这些函数能够处理的不仅仅是字符串,还可以是结构体数组等数据类型,具体可以处理的数据类型要根据具体函数来定。

void  *memcpy(void  *dst,  void  const  *src,  size_t  length);
void  *memmove(void  *dst,  void  const  *src,  size_t  length);
void  *memcmp(void  const  *a,  void  const  *b,  size_t  length);
void  *memchr(void  const  *a,  int  ch,  size_t  length);
void  *memset(void  *a,  int  ch,  size_t  length);

举个例子:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 10
int main()
{
  char temp1[] = "hello world";
  char temp2[] = "hello world";
  char temp3[] = "hello world";
  char temp4[] = "hello world";
  unsigned int int_array[SIZE];
  char *p = NULL;
  //复制字符串
  memcpy(temp1 + 2, temp1, 5);
  memmove(temp2 + 2, temp2, 5);
  printf("temp1 = %s\n", temp1);
  printf("temp2 = %s\n", temp2);
  printf("---------------------------------------\n");
  //比较字符串
  if(!memcmp(temp1, temp2, 6))
    printf("temp1 = temp2\n");
  else
    printf("temp1 != temp2\n");
  printf("---------------------------------------\n");
  //查找字符
  p = (char *)memchr(temp3, 'e', strlen(temp3));
  if(p != NULL)
    printf("字符e在temp3中的位置是:%d\n", p - &temp3[0]);
  printf("---------------------------------------\n");
  //初始化数组
  memset(int_array, 0, sizeof(int_array));
  for (int i = 0; i < SIZE; i++)
    printf("int_array[%d]的值为:%d\t", i, int_array[i]);
  printf("\n", sizeof(int));
  printf("---------------------------------------\n");
  //初始化数组
  memset(temp4, 'a', sizeof(temp4) - 1);
  printf("字符串temp4为:%s\n", temp4);
  system("pause");
  return 0;
}

打印输出:

9.1 memcpy和memmove真的不一样吗?

有一个很值得探讨的问题:

memcpy和memmove函数真的一样吗?《C和指针》以及网上的很多说法都是:两者不一样,如果src和dst出现了重叠,则memcpy会出现问题,而memmove总能按照理想的情况去运行,但我们的程序运行结果却是,这两个函数都可以按照理想情况运行,这是为什么呢?

唯一的解释就是,软件的运行环境不一样,程序的底层库出现了差异,所以会出现这样的情况。但这并不影响我们对以前版本的(也就是两者不同)的memcpy和memmove进行一番研究!

先来看看所谓的重叠是什么意思,为什么重叠的时候,字符串复制会出现问题。

以上就是我们程序中复制字符串操作的示意图。可以看到src子串和dst子串有三个字母出现了重叠,如果我们按照常规的操作方法,复制之后就会出现下面这样的结果。

若区域重叠,就会导致复制出错,也就是说,想要取的值被新值所覆盖,导致无法顺利取值,复制后temp1变成了hehehehorld。这就是之前memcpy字符串的复制方法。

然后让我们来看看memmove(以及优化后的memcpy)是如何巧妙地解决这个问题的。

可以看到,复制的顺序出现了变化,这次是从后往前复制的,很好地避免了这个问题。

那么问题来了,这次是要复制的目的位置在后面,如果在前面又该如何处理呢?答案是复制顺序也反过来。看看执行过程:

这个时候,也不会出现将要取的值被原来的值覆盖的情况,会按照预想的结果去执行,结果是:llo w world

9.2 memcmp:简单的比较

memcmp比较内存区域a和的前length个字节。比较方法,返回值都和strcmp基本一致,详情可以参考本文的strcmp部分。

9.3 memchr:简单的查找

memchr从a的起始位置开始查找字符ch的第一次出现的位置,并返回一个指向该位置的指针。查找方法,返回值都和strchr基本一致,详情可以参考本文的strchr部分。

9.4 memset:初始化的值只能是0和-1?

从该函数的介绍来看,该函数可以对某连续的内存区域设置相同的任何值(理论上),但在实际的开发中,基本都设置为0或者-1,这是为什么呢?

这是因为,这个函数对于内存的赋值,是以字节为单位的,一般用来对字符串进行赋值没有任何问题,因为字符串的元素只占一个字节,而数组则不同,常见的short,int,long类型的数组元素都不止一个字节,所以初始化才不会出现我们预想的结果。当我们设置为0的时候,各个字节都为0(若为-1,则各个位都为1),所以每个元素无论几个字节都会初始化为0,但是为其他值,结果就不一样了。参考以下程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 10
int main()
{
  unsigned int int_array[SIZE];
  //初始化数组
  printf("--------------初始化值设为0-------------------------\n");
  memset(int_array, 0, sizeof(int_array));
  for (int i = 0; i < SIZE; i++)
    printf("int_array[%d]的值为:%d\t", i, int_array[i]);
  printf("\n");
  printf("--------------初始化值设为1-------------------------\n");
  memset(int_array, 1, sizeof(int_array));
  for (int i = 0; i < SIZE; i++)
    printf("int_array[%d]的值为:%d\t", i, int_array[i]);
  printf("\n");
  system("pause");
  return 0;
}

打印输出:

这是为什么呢?

这是因为,memset是按字节赋值的,而int类型的数据在内存中占了4个字节,所以该值应该以4个字节为单位,即:0x01010101,转换成十进制正好是16843009

所以,一般如果想用memse对一段内存区域设定相同的值,初始化为0或者-1是最好的选择。

10 总结

字符串本身并不是很复杂,为了方便开发,提供了很多的库函数,所以我们只需要掌握C语言的那些库函数即可。尤其是要注意,有的函数返回的并不是数值,而是指针;有的函数使用起来比较特殊,比方说strtok等。

------------------------------------------------------------------------end-------------------------------------------------------------------------

相关文章
|
2月前
|
存储 C++
使用字符指针变量和字符数组的比较
使用字符指针变量和字符数组的比较
14 0
C4.
|
2月前
|
存储 程序员 C语言
C语言中如何通过指针引用字符串
C语言中如何通过指针引用字符串
C4.
17 0
|
2月前
|
C语言
C语言----字符数组&&指针
C语言----字符数组&&指针
24 0
|
7月前
|
存储 编译器 C语言
C语言之字符指针
C语言之字符指针
|
2月前
|
安全 C语言 C++
字符指针做函数参数
字符指针做函数参数
10 1
|
2月前
|
存储 程序员 C++
使用字符指针变量和字符数组的比较
使用字符指针变量和字符数组的比较
15 1
|
2月前
|
安全 C语言
字符指针作函数参数的深入探索
在C语言编程中,字符指针是一个重要的概念,尤其在处理字符串和文本数据时。当我们将字符指针作为函数参数时,可以实现多种灵活和高效的操作。本文将深入探讨字符指针作为函数参数的应用,并通过代码示例加以说明。
15 1
|
7月前
|
C语言
C语言之字符串的连接使用指针和调用函数两种方法
C语言之字符串的连接使用指针和调用函数两种方法
209 0
|
4月前
利用两个指针的差值求字符串长度
利用两个指针的差值求字符串长度
19 0
|
5月前
|
存储 并行计算 C++
指针详解(二级指针、指针数组和数组指针、字符指针、二维数组传参、函数指针变量)(二)
指针详解(二级指针、指针数组和数组指针、字符指针、二维数组传参、函数指针变量)(二)