前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C/C++中的数组和数组的memset函数

C/C++中的数组和数组的memset函数

作者头像
触摸壹缕阳光
发布2019-11-13 10:23:06
1.6K0
发布2019-11-13 10:23:06
举报
01

数组介绍

什么是数组?

数组就是把相同数据类型的变量组合在一起而产生的数据集合。从数组定义中可以看出数组主要有两个方面:

  1. 相同数据类型的变量;
  2. 数据集合;

对于第一点比较好理解,对于第二点简单来说就是把这些相同数据类型的变量按某种关系联系起来,这也是数据结构的定义。因此数组可以说是一个存储数据的数据结构,这种关系就是这些相同数据类型的变量在内存中必须是连续存储的。

数组的定义

前面说数组是相同数据类型变量连续存储的集合,因此在定义数组的时候需要给定数组的数据类型以及数组存放的变量个数。因此数组的定义格式如下:

代码语言:javascript
复制
数据类型  数组名[数组大小];

这里简单介绍定义数组的三个部分:

  1. 数据类型:数据类型可以是四种基本数据类型,例如int、float、double、char以及bool等;
  2. 数组名:定义数组的名称,当然数组名除了表示该数组之外,还表示该数组的首地址;
  3. 数组大小:当定义数组的时候需要指定数组中相同数据类型变量的个数,因为定义变量的时候,会在内存中开辟一块空间,当定义单个变量的话很好理解,如果定义的是数组变量的话,如果不指定相同数据类型变量的个数,就不知道开辟多少块内存空间。需要注意这里的数组大小必须是常量,绝对不能是变量,因为通常情况下C语言是不允许对数组长度进行动态定义的;

例如定义一个数组:

代码语言:javascript
复制
int a[3];

上面定义了一个整型数组,数组名为a,此时数组a中有3个整型变量,由于一个整型变量占4个字节的内存空间,那么3个整型变量占3 * 4 = 12个字节的内存空间,并且这12个字节的空间是连续的。习惯把数组中的变量称为元素。

实际上面数组的定义是一维数组的定义。当然也会有二维数组、三维数组等多维数组,其本质都是一样的。二维数组可以理解成一维数组中的元素还是一维数组,同理三维等多维数组的理解。

二维数组定义格式如下:

代码语言:javascript
复制
数据类型  数组名[第一维大小][第二维大小];
代码语言:javascript
复制

数组的初始化

为了方便接介绍,接下来以一维数组为例,当然对于二维三维等更高维度的数组同样适用。数组的初始化可以使用下面三种方法实现:

  1. 定义数组时给所有元素赋初始值,这被称为"完全初始化";
  2. 只给一部分元素赋值,这被称为"不完全初始化";
  3. 只定义数组不对数组中的元素进行赋值,这被称为"完全不初始化"。

接下来分别介绍:

  • 定义数组时给所有元素赋初始值,这被称为"完全初始化"。简单理解:定义数组元素个数 = 赋予初值的元素个数。
代码语言:javascript
复制
#include <stdio.h>
int main(){
  int a[3] = {1, 2, 3}; // 完全初始化
  int i;
  // 打印输出数组
  for(i = 0; i < 3; i++){
    printf("a[%d] = %d\n", i, a[i]);
  }
  return 0;}

a[0] = 1 a[1] = 2 a[2] = 3

  • 只给一部分元素赋值,这被称为"不完全初始化"。简单理解:定义数组元素个数 < 赋予初值的元素个数。
代码语言:javascript
复制
#include <stdio.h>
int main(){
  int a[3] = {1, 2}; // 不完全初始化
  int i;
  // 打印输出数组
  for(i = 0; i < 3; i++){
    printf("a[%d] = %d\n", i, a[i]);
  }
  return 0;}

a[0] = 1 a[1] = 2 a[2] = 0

定义数组a中的3个元素时,只对第一个以及第二个元素值进行初始化,最后一个元素即a[2]没有被初始化。在"不完全初始化",没有被初始化的元素自动为0。

  • 只定义数组不对数组中的元素进行赋值,这被称为"完全不初始化"。
代码语言:javascript
复制
#include <stdio.h>
int main(){
  int a[3]; // 完全不初始化 
  int i;
  // 打印输出数组
  for(i = 0; i < 3; i++){
    printf("a[%d] = %d\n", i, a[i]);
  }
  return 0;}

a[0] = 21909 a[1] = 1871924176 a[2] = 32765

同样没有对数组中的元素初始化,但是最终和"不完全初始化"只对部分初始化的结果不同:

  1. 在对部分元素不进行初始化的"不完全初始化"中,未被初始化的元素被编译器自动赋值为0;
  2. 在对全部元素都不进行初始化的"完全不初始化"中,未被初始化的元素编译器自动赋值为比较大的随机数;

02

对数组中每个元素赋相同值的memset函数

在实际使用中可能需要对数组中的每一个元素赋以相同的值。当想要把整个数组元素都赋初值为0的话,可以使用"不完全初始化"的方式:

代码语言:javascript
复制
int a[3] = {0}; // 后面再写一些0也是可以的
int a[3] = {}; // 必须加上大括号,这样就和完全不初始化区分开

虽然上面的为数组每一个元素赋0初始值很方便,但是如果想要赋除0以外的其他初始值就需要使用其他方式。一般来说,给数组中每一个元素赋相同初始值的方法有两种:

  1. memset函数,这也是接下来重点介绍的方法;
  2. fill函数;

memset函数的格式为:

代码语言:javascript
复制
memset(数组名, 值, sizeof(数组名));

如果想要使用memset函数,需要在程序的开头添加string.h头文件。介绍memset函数是因为这个函数不是按照常规赋予一个初始值即可,memset函数使用的是按字节赋值,即对每个字节赋同样的值。

在计算机所有数值都是以二进制的方式进行存储的,这种二进制叫做机器数,这是计算机内部的数据表示形式,而在计算机中就是通过这些二进制来进行运算的。为了方便进行运算,机器数有三种常用的表示方法:

  1. 原码,人类比较容易理解和计算的机器数表示方式;
  2. 反码,人类不容易理解,可以看成是原码和补码之间进行转换的中间过程,如果想要知道对应的数值可以转换成人类容易理解的原码;
  3. 补码,人类不容易理解,计算机中所有运算都是采用补码来完成的,如果想要知道对应的数值也可以转换成人类容易理解的原码;

在计算机中原码、反码以及补码的转换规则如下:

  1. 正数的原码、反码和补码都是一样的;
  2. 负数的原码、反码和补码都是不一样的;
  3. 负数原码 --> 反码,符号位不变,数值位按位取反;
  4. 负数反码 --> 补码,符号位不变,数值位加1;

接下来看一看memset函数是如何按字节赋值:

代码语言:javascript
复制
#include <stdio.h>
#include <string.h> // 想要使用memset函数,必须加上string.h头文件
int main(){
  int i;
  int a[3] = {1, 2, 3}; 
  // 赋值为0
  memset(a, 0, sizeof(a));
  for(i = 0; i < 3; i++){
    printf("a[%d] = %d\n", i, a[i]);
  }
  // 赋值为-1
  memset(a, -1, sizeof(a));
  for(i = 0; i < 3; i++){
    printf("a[%d] = %d\n", i, a[i]);
  }
  return 0;}

a[0] = 0 a[1] = 0 a[2] = 0 a[0] = -1 a[1] = -1 a[2] = -1

在C/C++中int数据类型占4个字节,memset函数按字节赋值,memset函数中的值即为对字节赋值的数值。将字节赋值为0,0为正数因此原码、反码以及补码都是一样的,1个字节的0补码表示如下:

00000000

int有4个字节,每个字节都是0的补码:

00000000 00000000 00000000 00000000

上面得到的是4个字节的补码,由于符号位为0为正值,正数反码补码以及原码都是一样的,转换成原码结果为0。即0就是数组需要为每个元素赋的值。

而对于-1而言,-1在1个字节中的原码表示为:

10000001 -1在1个字节中的原码表示方式

在计算机中参与运算的都是补码,因此还需将上面的原码转换成补码:

10000001 -1在1个字节中的原码表示方式 11111110 -1在1个字节中的反码表示方式 11111111 -1在1个字节中的补码表示方式

int有4个字节,每个字节都是-1的补码:

11111111 11111111 11111111 11111111

上面得到的是4个字节的补码,由于符号位为-为负值,正数反码补码以及原码是不一样的,补码转换成原码:

  1. 补码 --> 反码,符号位不变,减1;
  2. 反码 --> 原码,符号位不变,按位取反;

11111111 11111111 11111111 11111111 补码 11111111 11111111 11111111 11111110 反码(减1) 10000000 00000000 00000000 00000001 原码(按位取反),结果为-1

最终将数组a中的每一个元素都赋值为-1。需要注意,转换为4个字节的时候,最左边的最高位依然是符号位。

接下来,为一个字节赋值为-121进行分析(只要数值能够在一个字节范围中即可,超出会抛出异常):

由于-121是负数,因此需要计算转换成补码:

11111001 -121原码 10000110 -121反码 10000111 -121补码

上面只是1个字节,对于int数据类型的4个字节结果为:

10000111 10000111 10000111 10000111 补码形式

现在并不知道上面的补码具体是什么,接下来可以将上面的补码转换成原码来看看具体的数值。

10000111 10000111 10000111 10000111 补码形式 10000111 10000111 10000111 10000110 反码形式(减1) 11111000 01111000 01111000 01111001 原码形式(按位取反,最左边是符号位,符号位不要变)

通过计算,上面原码值为-2021161081‬。有了结果,通过代码来验证。

代码语言:javascript
复制
#include <stdio.h>
#include <string.h> // 想要使用memset函数,必须加上string.h头文件
int main(){
  int i;
  int a[3] = {1, 2, 3}; 
  // 赋值为-1
  memset(a, -121, sizeof(a));
  for(i = 0; i < 3; i++){
    printf("a[%d] = %d\n", i, a[i]);
  }
  return 0;}

a[0] = -2021161081 a[1] = -2021161081 a[2] = -2021161081

实验的结果和我们分析结果一致。这种按字节赋值的方式还是比较复杂的,因此还有一种fill方法,这里不详细介绍。这种方式赋值虽然比较复杂,但是效率比较高。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-10-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 AI机器学习与深度学习算法 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档