C语言初学者小秦的Blog(3)

本文详细介绍了C语言中的数组和指针,包括数组的定义、初始化、元素引用以及数组名的含义。内容涵盖了一维数组、字符数组、数组偏移量、字符串常量、二维数组的定义和初始化方法,以及数组在内存中的存储方式。此外,还讨论了指针解引用和数组下标的等价形式,强调了数组名在不同情况下的含义。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上周我们主要学习了数组和指针两大块重要的 知识点,下面让我们一起来研究一下吧~

学习数组首先我们要掌握的是:1.数组的定义和数组的初始化  2.数组下标的使用   3.数组名的含义

 数组基本概念

概念:

由相同类型的多个元素所组成的一种复合数据类型

在工程中同时定义多个相同类型的变量时,重复定义,可以使用数组                                 

逻辑:

  一次性定义多个相同的变量,并存储到一片连续的内存中

格式:

类型说明符 数组名[整型常量表达式];

类型说明符:指定数组元素的数据类型,任意c语言合法类型都可以

数组名 : c语言标识符,其值为该数组的首地址(常量)

整型常量表达式:指定数组元素的个数

语法释义:

a是数组名,即这片连续内存的名称

[5]代表这片连续内存总共分成5个相等的格子,每个格子称为数组的元素

int代表每个元素的类型,可以是任意基本类型,也可以是组合类型,甚至可以是数组

数组中所有元素的类型都是一致

数组申请的空间是连续的,从低地址到高地址依次连续存放数组中的每个元素

数组定义

初始化:

在定义的时候赋值,称为初始化

数组元素的引用

存储模式:

一片连续的内存,按数据类型分割成若干相同大小的格子

元素下标:

数组开头位置的偏移量

字符数组

概念:

专门存放字符的数组,称为字符数组

初始化与元素引用:

char s1[5] = {'a', 'b', 'c', 'd', 'e'};       // s1存放的是字符序列,非字符串
char s2[6] = {'a', 'b', 'c', 'd', 'e', '\0'}; // s2存放了一个字符串

char s[6] = {"abcde"}; // 使用字符串直接初始化字符数组
char s[6] =  "abcde" ; // 大括号可以省略

s[0] = 'A'; // 索引第一个元素,赋值为 'A'

 

数组偏移量

int Array[6] = {10,20,30,40,50,60};
// 数组地址偏移量
// 000000000061FE04,000000000061FE04
printf("%p,%p",&Array[1],&Array[0]+1);

 数组元素地址解引用

通过对数组元素地址解引用,可以获取地址空间里面的数据

 

int a = 10;
    printf("%d\n",a);

    printf("%p\n",&a);
    // * 表示将地址里面的内容取出,我们把它称为解引用
    printf("%d\n",*(&a));

    //--------------------------
    char Array[5] = {'j','a','c','k'};
    printf("%c\n",Array[0]);
    printf("%c,%c\n",*(Array+0),*(&Array[0]));
    printf("%c,%c\n",*(Array+1),*(&Array[1]));
    printf("%c,%c\n",*(Array+2),*(&Array[2]));
    printf("%c,%c\n",*(Array+3),*(&Array[3]));

 字符串常量

  • 字符串常量在内存中的存储,实质是一个匿名数组
  • 匿名数组,同样满足数组两种含义的规定

多维数组(二维数组)

  • 概念:若数组元素类型也是数组,则称该数组为多维数组,就是一维数组的集合
  • 二维数组在内存中的存放:先放第一行,再放第二行
  • 多维数组的语法跟普通的一维数组语法完全一致
  • 初始化:

int a[2][3] = {{1,2,3}, {4,5,6}}; // 数组的元素是另一个数组

int a[2][3] = {{1,2,3}, {4,5,6}, {7,8,9}}; // 错误,越界了
int a[2][3] = {{1,2,3}, {4,5,6,7}};        // 错误,越界了

int a[ ][3] = {{1,2,3}, {4,5,6}}; // OK,自动根据初始化列表分配数组元素个数
int a[2][3] = {{1,2,3}};          // OK,只初始化数组元素的一部分

  • 元素引用:

// a[0] 代表第一个元素,这个元素是一个具有 3 个元素的数组:{1,2,3}
// a[1] 代表第二个元素,这个元素也是一个具有 3 个元素的数组:{4,5,6}

printf("%d", a[0][0]); // 输出第一个数组的第一个元素,即1
printf("%d", a[1][2]); // 输出第二个数组的第三个元素,即6

//1、定义
//int arr[3][4];
//2、定义的时候初始化
//1)分行给二维数组初始化
/* int arr[3][4] = { {10,20,30,33},
                 {40,50,60,66},
                 {70,80,90,99}}; */
//2)将所有的数据全部写在一个大括号里面,按照数组的排列顺序进行赋值
//int arr[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};    
//int arr[3][4] = {{1,2,3,4},{5},6,7};    

//3)清空数组
//int arr[3][4] = {0};
//4)如果对全部元素进行赋值,则定义数组的时候可以省略第一维的长度
//int arr[][4] = {1,2,3,4,5,6,7,8,9,10,11,12};    
//int arr[][] = {1,2,3,4,5,6,7,8,9,10,11,12}; 错误
//int arr[3][] = {1,2,3,4,5,6,7,8,9,10,11,12};     错误

#include <stdio.h>

int main(int argc, char const *argv[])
{   // 只有一个[]的数组称为一维数组
    char name[5] = "jack";
    char name1[5] = "rose";
    char name2[5] = "ken";
    // 有两个[]的数组称为二维数组
    // 二维数组初始化
    // 第一个[3]表示字符串的个数,表示这个数组能存放3个字符串
    // 第二个[5]表示每个字符串最大的长度为5字节
    char Name[3][5] = {"jack","rose","ken"};
    char Name1[3][5] = {{"jack"},{"rose"},{"ken"}};
    char Name2[3][5] = {{'j','a','c','k','\0'},{"rose"},{"ken"}};
    printf("%c\n",Name[0][0]);
    printf("%c\n",Name[0][1]);
    printf("%c\n",Name[1][1]);
    printf("%c\n",Name[2][2]);

    // 初始化整数二维数组
    int nums[2][3] = {{1,2,3},{7,8,9}};
    printf("%d\n",nums[1][2]);
    int nums1[][3] = {{1,2,3},{7,8,9}};
    int num[] = {1,2,3};


    // 如果值初始化一部分,后面的所有空间都会初始化为0
    int nums2[2][3] = {{1,2,3}};
    printf("%d\n",nums2[1][1]);

    // 将num1里面的所有空间初始化为0
    int num1[5] = {0};
    printf("%d\n",num1[3]);

    // 二维数组定义
    int nums3[2][3];
    nums3[0][0] = 1;
    nums3[0][1] = 2;
    int i = 1;
    for(int row = 0; row < 2; row++)
    {
        for(int col = 0; col < 3; col++)
        {
            nums3[row][col] = i;
            i++;
        }
    }
    for(int row = 0; row < 2; row++)
    {
        for(int col = 0; col < 3; col++)
        {
            printf("%d\t",nums3[row][col]);
        }
        printf("\n");
    }
    return 0;
}

二维数组解引用

// 二维数组初始化字符串
char buf1[2][5] = {"jack","rose"};
printf("%s,%s\n",&buf1[0][0],buf[0]);
printf("%s,%s\n",&buf1[1][0],buf[1]);

// 取二维数组中的某个字符
printf("%c,%c,%c,%c\n",buf1[0][1],*(&buf1[0][1]),*(buf1[0]+1),*(*(buf1+0)+1));
printf("%c,%c,%c,%c\n",buf1[1][2],*(&buf1[1][2]),*(buf1[1]+2),*(*(buf1+1)+2));
 

 数组地址偏移

#include <stdio.h>

int main()
{
    char Names[3][5] = {"jack","rose","ken"};
    // printf("Names[0] %p,%p\n",Names[0],&Names[0][0]);
    // printf("Names[1] %p,%p\n",Names[1],&Names[1][0]);
    // printf("Names[2] %p,%p\n",Names[2],&Names[2][0]);
    
    // Names[0]相当于&Names[0][0]
    printf("Names[0]+1=%p,%p\n",Names[0]+1,&Names[0][1]);
    printf("Names[0][0]:%c,%c\n",Names[0][0],*(Names[0]+0));
    printf("Names[0][2]:%c,%c\n",Names[0][2],*(Names[0]+2));

    printf("&Names[0]+1=%p,%p\n",&Names[0]+1,&Names[1]);
    printf("*(&Names[0]+1)=%c,%c\n",*(&Names[0]+1)[0],*(&Names[1])[0]);
    printf("*(&Names[0]+1)=%c,%c,%c,%c\n",(*(&Names[0]+1))[1],(*(&Names[1]))[1], 
                                          *(*(&Names[0]+1)+1),*(*(Names+1)+1));
    
    printf("%c,%c,%c,%c\n",Names[2][2],*(Names[2]+2),(*(Names+2))[2],*((*(Names+2)+2)));

    return 0;
}

数组万能拆解法

任意的数组,不管有多复杂,其定义都由两部分组成:

第一部分:说明元素的类型,可以是任意的类型

第二部分 :说明数组名和元素个数

数组名涵义

  • 数组名有两个含义:

    • 第一含义是:整个数组

    • 第二含义是:首元素地址

  • 当出现以下情形时,那么数组名就代表整个数组:

    • 在数组定义中

    • 在 sizeof 运算表达式中

    • 在取址符&中

  • 其他任何情形下,那么数组名就代表首元素地址。即:此时数组名就是一个指向首元素的指针。

  • 示例:

int a[3];                  // 此处,a 代表整个数组
printf("%d\n", sizeof(a)); // 此处,a 代表整个数组
printf("%p\n", &a);  // 此处,a 代表整个数组,此处为整个数组的地址
​
int *p = a;     // 此处,a 代表首元素 a[0] 的地址,等价于 &a[0]
p = a + 1;      // 此处,a 代表首元素 a[0] 的地址,等价于 &a[0]
function(a);    // 此处,a 代表首元素 a[0] 的地址,等价于 &a[0]
scanf("%d\n", a); // 此处,a 代表首元素 a[0] 的地址,等价 &a[0]

语言只有在第一含义的场合下表现为数组,其他大部分场合都表现为首元素的地址,当数组表现为首元素地址时,实际上就是一个指向其首元素的指针。数组运算实际上就是指针运算。

数组下标

  • 数组下标实际上是编译系统的一种简写,其等价形式是:

a[i] = 100;  等价于  *(a+i) = 100;
  • 根据加法交换律,以下的所有的语句均是等价的:

  a[i] = 100;
*(a+i) = 100;
*(i+a) = 100;
  i[a] = 100;
  • 数组运算,等价于指针运算。

字符串常量

  • 字符串常量在内存中的存储,实质是一个匿名数组

  • 匿名数组,同样满足数组两种涵义的规定

  • 示例:

printf("%d\n", sizeof("abcd")); // 此处 "abcd" 代表整个数组
printf("%p\n", &"abcd");        // 此处 "abcd" 代表整个数组
​
printf("%c\n", "abcd"[1]); // 此处 "abcd" 代表匿名数组的首元素地址
char *p1 = "abcd";         // 此处 "abcd" 代表匿名数组的首元素地址
char *p2 = "abcd" + 1;     // 此处 "abcd" 代表匿名数组的首元素地址

零长数组(预习:结构体)

  • 概念:长度为0的数组,比如 int data[0];

  • 用途:放在结构体的末尾,作为可变长度数据的入口

  • 示例:

struct node
{
    /* 结构体的其他成员 */
    // 成员1
    // 成员2
    // ... ...
    
    int   len;
    char *data[0];
};
​
// 给结构体额外分配 10 个字节的内存。
struct node *p = malloc(sizeof(struct node) + 10);
p->len = 10;
​
// 额外分配的内存可以通过 data 来使用
p->data[0] ~ p->data[9]

变长数组

  • 概念:定义时,使用变量作为元素个数的数组。

  • 要点:变长数组仅仅指元素个数在定义时是变量,而绝非指数组的长度可长可短。实际上,不管是普通数组还是所谓的变长数组,数组一旦定义完毕,其长度则不可改变。

  • 示例:

int len = 15;
int a[len];  // 数组元素个数 len 是变量,因此数组 a 是变长数组
​
int x = 2;
int y = 3;
int b[x][y]; // 数组元素个数 x、y 是变量,因此数组 b 是变长数组
int b[2][y]; // 数组元素个数 y 是变量,因此数组 b 是变长数组
int b[x][3]; // 数组元素个数 x 是变量,因此数组 b 是变长数组
  • 语法:变长数组不可初始化,即以下代码是错误的:

int len = 5;
int a[len] = {1,2,3,4,5}; // 数组 a 不可初始化
int len = 10;
// 编译的时候不能确认len的值,并且申请空间的同时直接赋值,所以不能初始化
//int buf[len] = {10};
​
// 定义的时候没有使用此空间,所以不会报错
int buf[len];
buf[1] = 10;
printf("%d\n",buf[1]);

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值