数组(C语言)

一、数组的定义

1、定义

数组:指针的特殊化
它也是内存分配(空间申请)的一种形式,而不是一种全新的数据结构。

其目的就是为了定义一个连续的空间。

2、2个属性:

  • 大小

  • 读取方式

    数据类型 数组名[m]:
    
    []:把数组名升级为一个连续空间
    m:连续空间的个数。
    数据类型:表示内存以何方式切割。
    
    int a[100];   // 100个int这么大的连续空间
    
    a[10]   // 使用方式与指针是一样的
    

(1)m 就是一个建议符,只是在申请的时候用一下。后面使用的时候,编译器无法知道[]里面的数是否越界。

int a[100];   // 理论上,a的空间只有100个,但是只是建议;还是可以越界的。

(2)经典错误1:数组名是一个常量符号,只是一个标签常量,是无法改变的,一定不要放到=的左边

char buf[100];
buf = "hello world";    // 错误!
buf++;    // 错误!

数组名是一个常量,指针是一个变量。


二、数组空间的初始化(数组空间的赋值问题)

原理:按照标签逐一赋值。(空间的赋值没有投机取巧的办法,只能这样做)

int a[10];
a[0] = x;
a[1] = y;
...

问题:
但如果这样赋值,程序员的工作量很大。
偷懒办法:让编译器进行一些自动处理,帮助程序员写如上的逐一赋值的程序。

方法:空间在定义时,就告知编译器初始化情况,这叫空间的第一次赋值。

int a[10] = 空间;

C语言本身/CPU内部本身,一般不支持空间和空间的拷贝,只支持int、int(4B)等之间小的拷贝。

int a[10] = {10, 20, 30};   -- > 学习arm汇编后,可以进行反汇编。
其内部相当于执行一个拷贝的过程。只是不同编译器调用不同的函数来做拷贝。

这句定义,其实CPU还是做了:
a[0] = 10;
a[1] = 20;
a[2] = 30;
a[3] = 0 / 随机值;  (不同编译器可能有所不同)
...
所以,一定不要这样认为:数组初始化一步就可以完成了!

数组空间的初始化和变量的初始化,本质上是不同的。
尤其在嵌入式的裸机开发中,数组空间的初始化往往需要一些库函数的辅助,或者程序员的人为设计。

1、字符空间(软件的最小空间:char)

char buf[10] = {'a','b','c'};
// 1. buf如果当成普通内存来看,这样写没有问题
// 2. buf如果当成一个字符串来看,最后一定加上一个'\0'
字符串的重要属性:结尾一定有个'\0'

(1)buf当成字符串的写法:

char buf[10] = {'a','b','c', '\0'};

char buf[10] = {"abc"};    
// buf当成字符串最合理的写法
// C语言编译器看到双引号,就会自动在末尾加上'\0'

char buf[10] = "abc";

char buf[] = "abc";

(2)经典错误2

char buf[10] = "abc";
// buf有空间,再把abc逐一地拷贝到buf中
// 通过初始化方法完成了常量区向变量区的拷贝。
// 因此,对buf变量区的值进行修改是可以的;但无法对“abc”常量区进行改动。
buf[2] = 'e';   //对变量区进行修改,没有问题
char *p = "abc";
// p直接指向abc的地址
p[2] = 'e';    // 错误!不能对常量区进行修改

(3)经典错误1:数组名是一个常量符号,只是一个标签常量,是无法改变的,一定不要放到=的左边。

char buf[100] = "abc";    // 数组空间的初始化,是空间的第一次赋值,可以直接这样写
buf = "hello world";    // 错误!
// 想把空间的值变为其它,这是进行空间的第二次赋值,就不能直接这样写了,只能逐一处理:
buf[0] = 'h';   
buf[1] = 'e'; 
... 
buf[n] = 'd';
buf[n+1] = 0;

C语言编译器只支持第一次空间赋值(初始化)时,直接=来一次赋值;第二次开始,必须进行逐一赋值。

问题:
第二次赋值,是很多人都有的需求,而逐一赋值工作量太大啦!

鉴于此,C语言提供了一套字符拷贝函数

2、字符拷贝函数(strcpy、strncpy)

字符拷贝函数:内存空间和内存空间的逐一赋值功能的一个封装体。

注意:一旦空间中出现了0这个特殊值,字符拷贝函数就将结束。
也就是说,只有遇到\0,strcpy和strncpy才会结束拷贝;否则会一直拷贝。

(1)strcpy

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

使用strcpy实现第二次空间赋值:

char buf[10] = "abc";
// buf = "hello world";
strcpy(buf, "hello world");   // 使用strcpy实现了第二次空间赋值!

该函数存在严重的内存泄漏问题,在工程中绝对不能使用!

char buf[10] = "abc";
strcpy(buf, "hello world, i am lixingzhi. How are you ? ...");
// buf只有10个连续空间,但是strcpy时字符串的字符多于10个,导致内存泄漏

(2)strncpy

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

3、非字符空间

字符空间:可以使用ASCII码来解码的空间,其目的是让人看的(%s看字符空间的内容,人可以看懂)

字符空间有字符串和结束标志(\0)。

非字符空间

数据采集,是8bit或其倍数为单位来进行采集的。
所以需要开辟一个存储这些数据的空间。

char buf[10];     // 看到char,第一反映就是:一定是个string(字符串) 
unsigned char buf[10];    // 看到unsigned char,一定是个data(普通数据,非字符串) 

注意:非字符空间的第二次赋值,不能用strcpy和strncpy:

unsigned char *p = sensor_base;   // sensor的数据
strcpy(buf, p);
// strcpy遇到\0才会结束。sensor_base里很可能没有\0,或者刚开始就是低电平(0)

问题:
字符空间的拷贝,结束标志为\0;那么非字符空间拷贝的结束标志是什么?

非字符空间没有结束标志,只能定义拷贝的个数。
因此其拷贝的三要素:

  • src
  • dest
  • 拷贝的个数

4、memcpy

// man memcpy:
#include <string.h>

void *memcpy(void *dest, const void *src, size_t n);
// void *:非字符空间标识符
// n:非字符空间的大小

例1:

int buf[10];
int sensor_buf[100];
// memcpy(buf, sensor_buf, 10);  错误!!  10是10个字节(char)
memcpy(buf, sensor_buf, 10 * sizeof(int));  // 拷贝的是10个int大小

例2:

unsigned char buf[10];
unsigned char sensor_buf[100];
memcpy(buf, sensor_buf, 10 * sizeof(unsigned char));

三、用指针表示数组

1、一维数组与一级指针

int b[100];    
int *p1 = b;    // 数组名就是数组首地址,可以赋给一个指针

2、指针数组与多级指针(二级指针)

int b[100];    // 100个空间都存的是int整型值

// 指针数组
char *a[100];    // 100个空间都存的是*(指针),指针对取的方式是char(一个字节一个字节地操作)
sizeof(a) = 100 * 4;    // 1个地址4个字节

// 二级指针
char **a ;

// 指针数组和二级指针,都通过a[0]、a[1]这样的方式访问
char *a[100]首地址被指针指向,就成了char **a

3、多维数组(二维数组)

(1)Q:二维数组与二级指针有关吗?

// 定义一个指针,指向int b[5][6]的首地址

// 错误!:
int **p2 = b;

例1:

// 001.c
#include <stdio.h>
int main()
{
    int a[5][6];    // 二维数组

    int **p2 = a;    // 二级指针
    return 0;
}
无效的指针类型:p2读内存的方式,和a读内存的方式是不一样的
二维数组与二级指针没有一点关系,完全不一样

二维数组与二级指针没有一点关系,完全不一样。
二级指针:只是描述地址的线性关系,是地址的一个存储器。
二维数组:一块一块地读内存。

(2)Q:二维数组该如何用指针表示?

int a;    // 一个int型变量a
int *p;    // 一个int型指针p

int a[5];    
int *p[5];    
int (*p)[5]; 

例2:

// 002.c
#include <stdio.h>
int main()
{
    int a[5][6];
    int (*p2)[6] = a;    // 指针表示二维数组
    return 0;
}
这次编译没有警告了,说明a[5][6]读内存的方式,和(*p2)[6]读内存的方式是一样的

(3)多维数组

int b[2][3][4];    // 多维数组
int (*p)[3][4] = b;    // 指针表示多维数组
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,864评论 6 494
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,175评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,401评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,170评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,276评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,364评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,401评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,179评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,604评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,902评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,070评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,751评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,380评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,077评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,312评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,924评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,957评论 2 351

推荐阅读更多精彩内容