您的位置:首页 > 娱乐 > 明星 > 东莞 网站建设企业_photoshop官方下载_app开发定制_竞价培训

东莞 网站建设企业_photoshop官方下载_app开发定制_竞价培训

2025/5/6 9:53:41 来源:https://blog.csdn.net/m0_73693552/article/details/147539798  浏览:    关键词:东莞 网站建设企业_photoshop官方下载_app开发定制_竞价培训
东莞 网站建设企业_photoshop官方下载_app开发定制_竞价培训

c语言的数组详解

  • 一维数组
    • 一维数组的声明
    • 变长数组
    • 一维数组的定义和初始化
    • 一维数组的存储和访问
    • 一维数组作为实参
    • sizeof和数组
  • 二维数组和多维数组
    • 二维数组的声明和定义
    • 多维数组的存储
    • 二维数组的访问方式和sizeof
    • 多维数组作为实参
    • 二维变长数组

一维数组

用c语言解决问题时,一些问题需要申请很多同类型的变量,这时再用int aint b这种一个变量一个变量地申请,代码会变得非常长。

因此有了数组。数组可以认为是数据结构的一种,数据结构是管理数据的一种方式,通过数据结构实现对数据的增、删、查、改,可以解决很多问题。

而且很多数据结构都是通过数组实现。

这里的干货可能有细节上的欠缺,毕竟目的是快速上手和复习使用的笔记,并不是查询用的字典。

一维数组的声明

首先是一维数组,一维数组是只用1个下标即可访问的数组。一维数组的声明:

Type array[num];

例如这个样例,声明了10个成员的数组,赋值后并输出:

#include<stdio.h>int main(){int a[10],i;for(i=0;i<10;i++){a[i]=i;printf("%d",a[i]);}return 0;
}

C语言规定:数组的每个成员都有一个下标,下标是从0开始的。

数组可以通过下标来访问的。这里int a[10]表示声明10个成员的一维数组,它的下标从{0,1,2,3,4,5,6,7,8,9}中选择。

int a[10]相当于10个成员,每个成员相当于一个独立的变量

一维数组声明后严格来说不能访问,除非使用数组存储过数据。经过声明的一维数组,访问它的成员得到的值是随机值。例如这里将字符数组最后一个成员初始化为空,然后用字符串的方式输出:

#include<stdio.h>int main() {char a[10];a[9] = '\0';printf("%s", a);return 0;
}

在vs它输出4个“烫”,在Dev-c++ 5.11输出的结果为T,但本质是为了输出未被初始化的值,这个值有的编译器会处理,而有的不会,因此会产生很多未定义行为例如程序崩溃。

未定义行为指代码执行了标准(C/C++规范)未明确定义的操作,导致程序的行为无法预测。

变长数组

数组严格来说不能用变量来声明或定义。

C99标准支持变长数组,数组的大小可以使用变量指定,但是数组不能初始化

#include<stdio.h>int main(){int num=10;int a[num],i;for(i=0;i<10;i++){a[i]=i;printf("%d",a[i]);}return 0;
}

遗憾的是,vs系列的MSVC不支持变长数组,但是gcc编译器支持,支持gcc编译器的IDE有Dev-c++、使用mingW的vscode、绝大多数OJ平台等。

但需要注意,经过const修饰的常量在c语言中不能用于声明数组。

#include<stdio.h>
const int N = 10;int main() {const int O = 9;int a[N];int b[O];return 0;
}

但是c++可以,因为c++对const做了一定的修改。

一维数组的定义和初始化

数组的初始化是指,在创建数组的同时给数组的内容一些合理初始值(初始化)。

这里就有各种初始化的技巧。

#include<stdio.h>int main() {int i;int a[10] = {1,2,3};//不完全初始化:前3个成员初始化,其余用0初始化int b[10] = { 1 };//不完全初始化:除了b[0]初始化为1,取余都初始化为0int c[5] = { 1,2,3,4,5 };//完全初始化:所有成员都初始化int d[] = { 0,0,1,-1 };//让操作系统自己去计算数组的成员个数char e[3] = { '6',54,'6' };char f[10] = "asdfghjkl";//初始化为字符串,前提是字符+\0的长度小于等于数组长度char g[10] = { "3.141" };char h[] = "qwerty";//让系统自己去为字符串分配内存return 0;
}

数组在创建的时候如果想不指定数组的确定的大小就得初始化。数组的成员个数根据初始化的内容来确定。因此[]内的内容可不指定,但一定要给初始值。

但是对于e[10]f[],若用printf%s格式输出,因为e没有用\0结尾,会导致越界访问,f有用\0结尾可正常输出。

#include<stdio.h>int main() {char e[3] = { '6',54,'6' };char f[10] = "asdfghjkl";printf("%s\n", f);printf("%s", e);return 0;
}

输出结果之一(vs):

asdfghjkl
666烫烫甜v鳢?

因为%s会一直输出字符,直到找到\0为止。

所以e在输出前3个字符后并没有找到\0,于是继续在内存深处寻找,直到找到\0。在找到\0之前,访问到很多未被许可的空间,这些空间被初始化为特定的值,组合起来正好是某种文字的编码。比如在vs上所有空间统一初始化为0xcc,而“烫”字的GBK编码又刚好是两个字节的0xcccc,所以正好输出这个汉字。

例如通过char字符串输出烫字:

#include<stdio.h>int main() {char e[] = { 0xcc,0xcc,'\0' };//烫字的GBK编码printf("%s", e);return 0;
}

一维数组的存储和访问

sizeof(数组名)可以得到整个数组的大小,因此可以用sizeof(数组名)/sizeof(数组的某个成员)得到数组的成员数。

通过这个例子可以观察数组在计算机中的存储。其中char型变量使用的内存是 1 byte。

#include <stdio.h>
int main()
{char arr[10] = { '\0' };int i = 0;//得到数组的元素个数int sz = sizeof(arr) / sizeof(arr[0]);for (i = 0; i < sz; ++i){printf("&arr[%d] = %p\n", i, &arr[i]);}return 0;
}

输出结果之一:

&arr[0] = 00CFFCF0
&arr[1] = 00CFFCF1
&arr[2] = 00CFFCF2
&arr[3] = 00CFFCF3
&arr[4] = 00CFFCF4
&arr[5] = 00CFFCF5
&arr[6] = 00CFFCF6
&arr[7] = 00CFFCF7
&arr[8] = 00CFFCF8
&arr[9] = 00CFFCF9

这说明数组的所有成员在内存中是连续存放的。

请添加图片描述

成员间的地址之差若不经过强制转换,会返回间隔的成员数。

#include <stdio.h>
int main()
{int arr[10] = { 0 };printf("%d\n", (&arr[6]) - (&arr[4]));printf("%d", (int)(&arr[6]) - (int)(&arr[4]));return 0;
}

输出:

2
8

知道了数组的存储机制,则可以用指针去访问数组。

#include <stdio.h>
int main()
{int a[10] = { 0,1,2,3,4,5,6,7,8,9 };int i = 0;int* p = &a[0];//三种访问方式是等同的for (i = 0; i < 10; i++)printf("%d ", *(a + i));printf("\n");for (i = 0; i < 10; i++)printf("%d ", *(p + i));printf("\n");for (i = 0; i < 10; i++)printf("%d ", a[i]);return 0;
}

一维数组作为实参

例如这个例子。

#include <stdio.h>void f1(int a[10]) {int sz = sizeof(a) / sizeof(a[0]);for(int i=0;i<sz;i++)printf("%d ",a[i]);printf("\n");
}void f2(int a[]) {int sz = sizeof(a) / sizeof(a[0]);for (int i = 0; i < sz; i++)printf("%d ", a[i]);printf("\n");
}int main()
{int a[10] = { 0,1,2,3,4,5,6,7,8,9 };f1(a);f2(a);return 0;
}

输出:

0
0

可以发现,将main函数内的数组的数组名,作为实参上传给函数后,便无法通过sizeof(a) / sizeof(a[0])获得数组的成员个数。

因为形参int a[10]int a[]中的a并不代表数组,而是代表同类型的指针。所以sizeof(a)在这种情况下求的是指针本身的大小。指针本身的大小在vs的环境下根据配置是x86(32位)还是x64(64位),使用的内存大小是4和8。

这里用的是x86的环境,所以sz实际等于1,也就是说只会访问a[0]

sizeof和数组

例如这个案例:

#include <stdio.h>void f(short int a[]) {printf("\n\nIn f:\n");printf("sizeof(a)=%d", sizeof(a));printf("\n");printf("sizeof(a[0])=%d", sizeof(a[0]));
}int main()
{short int a[10] = { 0,1,2,3,4,5,6,7,8,9 };printf("In main:\n");printf("sizeof(a)=%d", sizeof(a));printf("\n");printf("sizeof(a[0])=%d", sizeof(a[0]));f(a);return 0;
}

输出(vs的x86环境下):

In main:
sizeof(a)=20
sizeof(a[0])=2In f:
sizeof(a)=4
sizeof(a[0])=2

short [int]是2个字节。[int]表示可有可无。

和数组定义的作用域一样的情况下,sizeof(a)是整个数组的大小,sizeof(a[0])是成员的大小。因此输出 20 和 2 。

但到了f之后,形参不再是数组而是普通的short指针,因此sizeof(a)返回的是指针的大小,而sizeof(a[0])依旧是成员的大小。因此输出 4 和 2 。

二维数组和多维数组

多维数组是用多个个下标才可访问的数组。例如二维数组a的访问方式是a[1][2],三维数组b同理:b[1][2][3]

二维数组可以看成是一个矩阵,例如a[i][j]i表示矩阵的行,j表示矩阵的列。

请添加图片描述

多维数组和二维数组有很多相似之处,因此可以通过二维来推导多维数组的使用。

二维数组的声明和定义

例如这个例子:

#include <stdio.h>int main() {int a[2][2];//声明2*2的数组int b[2][2] = { 1 };//除b[0][0]初始化为1,其余成员均初始化为0int c[2][2] = { 1,2,3 };//4个元素,前3个初始化为{1,2,3}int d[3][3] = { {1,2,3},{4,5,6} };//初始化d[0]和d[1]的3个成员,其余用0初始化int e[3][3] = { {1,2},{4} };//除了e[0][0],e[0][1],e[1][0],其余用0初始化int f[][3] = { {2,3},{4} };//给初始化的话,行可以省略,但列不能省略,系统会自动计算行数return 0;
}

多维数组的声明和定义可以参考二维。

多维数组的存储

依旧是使用char数组。

案例:

#include <stdio.h>int main() {char a[2][2] = { '\0' };for (int i = 0; i < 2; i++)for (int j = 0; j < 2; j++)printf("&a[%d][%d]=%p\n", i, j, &a[i][j]);printf("\n");char b[3][2][2] = { '\0' };for (int i = 0; i < 3; i++)for (int j = 0; j < 2; j++)for (int k = 0; k < 2; k++)printf("&c[%d][%d][%d]=%p\n", i, j, k, &b[i][j][k]);return 0;
}

输出结果之一:

&a[0][0]=004FFEDC
&a[0][1]=004FFEDD
&a[1][0]=004FFEDE
&a[1][1]=004FFEDF&c[0][0][0]=004FFEB0
&c[0][0][1]=004FFEB1
&c[0][1][0]=004FFEB2
&c[0][1][1]=004FFEB3
&c[1][0][0]=004FFEB4
&c[1][0][1]=004FFEB5
&c[1][1][0]=004FFEB6
&c[1][1][1]=004FFEB7
&c[2][0][0]=004FFEB8
&c[2][0][1]=004FFEB9
&c[2][1][0]=004FFEBA
&c[2][1][1]=004FFEBB

这说明,无论数组的维度有多少,数组的存储方式都是连续的。

请添加图片描述

二维数组的访问方式和sizeof

同样可以根据数组连续的特性进行数组的访问。

二维数组a[i][j]中,a[i]表示第i行一维数组的首元素地址。

因此sizeof(a)表示整个数组的大小(前提是a[i][j]非形参),sizeof(a[0])表示第0行一维数组的大小,sizeof(a[0][0])才是这个数组的成员的大小。

#include <stdio.h>int main() {int a[3][3] = { 0,1,2,3,4,5,6,7,8 };printf("%d\n%d\n%d\n", sizeof(a), sizeof(a[0]), sizeof(a[0][0]));return 0;
}

输出:

36
12
4

通过非正常方式访问二维数组:

#include <stdio.h>int main() {int a[3][3] = { 0,1,2,3,4,5,6,7,8 };//正常访问for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++)printf("%d ", a[i][j]);printf("\n");}printf("\n");//二次解引用for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++)printf("%d ", *(*(a + i) + j));printf("\n");}//当成一维数组进行访问int* p = &a;printf("\n");for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++)printf("%d ", *(p + 3*i + j));printf("\n");}return 0;
}

其中二次解引用是因为二维数组的a[0]a[1]等也相当于一维数组的地址。

根据这段代码:

	//当成一维数组进行访问int* p = &a;printf("\n");for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++)printf("%d ", *(p + 3*i + j));printf("\n");}

因此多维数组可以用成员数量相等的一维数组代替。

但这样做会导致表达式变得非常长。例如一维数组实现和二维数组一样的访问方式:
*(数组名+每行列数*行+列)

#include <stdio.h>int main() {int a[9] = { 0,1,2,3,4,5,6,7,8 };for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++)printf("%d ", *(a + 3 * i + j));printf("\n");}return 0;
}

多维数组作为实参

首先想到的是作为形参的二维数组,数组名只是一个指针

其次就是,形参的二维数组,可以省略第1个[]内的数量但不能省略第2个[]的数量。而且第2个[]内需要是一个常量表达式

#include <stdio.h>void f(int a[3][3]) {printf("sizeof(a)=%d\n", sizeof(a));
}
void f2(int a[][3]) {//行可以省略printf("sizeof(a)=%d\n", sizeof(a));
}int main() {int a[3][3] = { 0,1,2,3,4,5,6,7,8 };f(a);f2(a);return 0;
}

输出:

sizeof(a)=4
sizeof(a)=4

除了这种方式,还存在一种指向多维数组的指针。例如:

int a[3][3];,指向它的指针就是int (*p)[3]。因为[]的优先级高于*,所以需要加括号调整使p先与*结合。

或者说,可以这样理解:int (*)[3] p;,即int(*)[3]表示每行有3个元素的指向二维数组的指针p

因此二维数组作为形参还能这样表示:

#include <stdio.h>void f(int (*a)[3]) {for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++)printf("%d ", a[i][j]);printf("\n");}
}int main() {int a[3][3] = { 0,1,2,3,4,5,6,7,8 };f(a);return 0;
}

同理可以推广到三维或多维。但除了第1个[],后面的[]内都不能省略。

#include <stdio.h>void f1(int a[3][3][3]) {for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {for (int k = 0; k < 3; k++) {a[i][j][k] = i * 3 * 3 + j * 3 + k * 3;printf("%d ", a[i][j][k]);}printf("\n");}printf("\n\n");}printf("\n\n\n");
}void f2(int a[][3][3]) {for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {for (int k = 0; k < 3; k++) {a[i][j][k] = i * 3 * 3 + j * 3 + k * 3;printf("%d ", a[i][j][k]);}printf("\n");}printf("\n\n");}printf("\n\n\n");
}void f3(int (*a)[3][3]) {for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {for (int k = 0; k < 3; k++) {a[i][j][k] = i * 3 * 3 + j * 3 + k * 3;printf("%d ", a[i][j][k]);}printf("\n");}printf("\n\n");}printf("\n\n\n");
}int main() {int a[3][3][3] = { 0 };f1(a);f2(a);f3(a);return 0;
}

二维变长数组

没错,二维数组也可以是变长数组,但不能初始化,vs同样不支持但gcc支持。前提是gcc支持C99。

#include <stdio.h>int main() {int r=3,c=3;int a[r][c];for(int i=0;i<3;i++){//for内的第1个表达式申请变量需要编译器支持C99 for(int j=0;j<3;j++){a[i][j]=i*3+j;printf("%d ",a[i][j]);}printf("\n");}return 0;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com