《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-------------------------------------------------------------------------

相关文章
|
6月前
|
存储 编译器 C语言
函数指针&&数组指针&&数组传参的本质&&字符指针(进阶篇)
函数指针&&数组指针&&数组传参的本质&&字符指针(进阶篇)
126 0
|
6月前
|
存储 C++
使用字符指针变量和字符数组的比较
使用字符指针变量和字符数组的比较
62 0
C4.
|
6月前
|
存储 程序员 C语言
C语言中如何通过指针引用字符串
C语言中如何通过指针引用字符串
C4.
70 0
|
6月前
|
C语言
C语言----字符数组&&指针
C语言----字符数组&&指针
49 0
|
6月前
【每日一题Day301】LC2337移动片段得到字符串 | 双指针 计分
【每日一题Day301】LC2337移动片段得到字符串 | 双指针 计分
51 0
|
6月前
|
算法 C语言
通过指针引用字符串
通过指针引用字符串
71 1
|
2月前
|
存储 人工智能 C语言
C语言程序设计核心详解 第八章 指针超详细讲解_指针变量_二维数组指针_指向字符串指针
本文详细讲解了C语言中的指针,包括指针变量的定义与引用、指向数组及字符串的指针变量等。首先介绍了指针变量的基本概念和定义格式,随后通过多个示例展示了如何使用指针变量来操作普通变量、数组和字符串。文章还深入探讨了指向函数的指针变量以及指针数组的概念,并解释了空指针的意义和使用场景。通过丰富的代码示例和图形化展示,帮助读者更好地理解和掌握C语言中的指针知识。
|
6月前
|
存储 C语言
字符指针变量与字符数组的比较
字符指针变量与字符数组的比较
51 3
|
6月前
|
存储 C语言
字符指针作为函数参数
字符指针作为函数参数
61 2
|
6月前
DAY-2 | 哈希表、指针与区间划分:字符种数统计问题
```markdown ## 题干 [牛客网链接](https://www.nowcoder.com/practice/eb94f6a5b2ba49c6ac72d40b5ce95f50) ## 题解 1. **查表法(哈希表)**:利用数组标记出现过的 ASCII 值小于127的字符,首次出现计数,重复则忽略。 2. **指针与区间划分(回头法)**:遍历字符串,对每个字符检查其前所有字符是否重复,重复则不计数。 ## 方法总结 - 哈希表在去重问题中非常实用,可多做相关练习。 - 使用`continue`时注意避免死循环,确保循环变量会改变。 - 多回顾此类问题以巩固理解。 ```
44 2
下一篇
无影云桌面