数组
作用:需要保存许多同种类型的数据的时候
数组就是一种数据类型,可以像基本数据类型一样定义数组变量
声明定义格式;
数据类型 数组名[表达式]
中括号中的表达式是一个常量和常量表达式,值必须是固定的
例子:int radius[201];
int result[100];
char name[5];
组成部分:
1、数据类型:用来表示数组中可以存放的数据类型
2、数组名:类似于变量名,当要使用数组内的元素的时候,需要这个变量名
3、表达式:表示定义的数组中将来最多能储存多少元素,所以必须是一个有固定值的整型数据的数组
int radius[20];
数组变量的初始化和赋值
数组的初始化
格式:数据类型 数组名[表达式] = {值};
例子:int radius[5] = {0,1,2,3,4};
其中的表达式也可以忽略:数据类型 数组名[] = {值};
例子:int radius[ ] = {0,1,2,3,4};
之所以可以忽略是因为数组保存的个数已经是大括号内值的个数了,已经确定了;
⚠️数组的两种初始化形式,只能在数组声明定义的时候和数组的声明定义一起使用,在其他地方都不对
数组的下标
作用:在给数组赋值时,需要一个一个赋值。这时利用数组的下标来找到对应的元素
格式:数组名[下标]
数组名是已经声明定义好的。
中括号是一个取数组元素的运算符,下标就是表示元素在数组中的位置。它可以是一个整型常量、整型变量或者一个具有整型值的表达式
数组名[下标] 这个表示被称为是 取数组运算表达式
⚠️C语言中数组下标是从0开始的,不是从1开始的
给数组赋值
赋值表达式:数组名[下标] = 数值;
等号前是取数组元素运算,等号后是一个具有数值的常量、变量或表达式
例子:int radius[5];
radius [1] = 3;
其实数组初始化也是一种赋值,只不过是在一开始就用大括号一次性给所有格子都放了数据
数组的引用
从数组中拿出想要的元素,就是数组元素的引用
数组元素的引用是使用取数组元素运算:数组名[下标];
radius[1];(打开储存的小格子)
#include
int main(int argc, const char * argv[]) {
int radius[5] = {1,2,3,4,5};
int sum = 0;
int i;
//将下标0-4引用数组中的元素,然后使用加后赋值运算将每个数组运算和sum相加,然后赋值给sum
for(i=0;i<5;i++)
{
sum += radius[i];
printf("sum: %d\n",sum);
}
return 0;
}
二维数组
数组可以用来存储数组类型的数据(数组套数组)
数组嵌套的深度被称为数组的维;
对于存放简单数据类型的数组,被称为一维数组
当一维数组中存放的是一个一维数组时,便变成二维
二维数组表示和含义
格式:数据类型 数组名 [表达式1][表达式2];
二维数组和一维数组的表示的唯一不同是,表达式中两个中括号和两个表达式
第一中括号表示二维数组可以存多少个一维数组
第二个中括号表示每个一维数组能出储存多少个基本数据类型的数据
例如:保存m个一维数组元素,m个一维数组中都可以保存n个整型数据。
int Matrix[m][n];
二维数组的初始化
格式:整数类型 数组名[表达式1][表达式2] = {数值};
整数类型 数组名[表达式1][表达式2] = {{数值,数值},{数值,数值}};
数值最多的是表达式1和表达式2相乘个个数
⚠️二维数组只能忽略表达式1
二维数组的赋值
取数组元素运算的表达式:
数组名[下标1][下标2];
赋值形式
数组名[下标1][下标2] = 数值;
例子: int Matrix[4][2];
#include
int main(int argc, const char * argv[]) {
int Martix[4][2];
int i,j;
int value;
value = 1;
for(i=0;i<4;i++)
{
for(j=0;j<2;j++)
{
Martix[i][j] = value++;
printf("%d\n",value);
}
}
return 0;
}
二维数组的引用
格式:数组名[下标1][下标2];
Matri [1][1];
#include
int main(int argc, const char * argv[]) {
//二维数组初始化
int Martix[4][2] = {1,2,3,4,5,6,7,8};
int i,j;
//Martix引用
for (i=0; i<4; i++) {
for(j=0;j<2;j++)
{
printf("%d",Martix[i][j]);
}
printf("\n");
}
printf("\n");
for(i=0;i<4;i++)
{
for (j=0; j<2; j++) {
Martix[i][j] = 0;
}
}
for (i=0; i<4; i++) {
for(j=0;j<2;j++)
{
printf("%d",Martix[i][j]);
}
printf("\n");
}
return 0;
}
多维数组
#include
int main(int argc, const char * argv[]) {
//二维数组初始化
int Martix[1][2][3][3] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,};
int i,j,k,l;
for (i = 0; i < 1; i++) {
for (j = 0; j < 2; j++) {
for (k = 0; k < 3; k++) {
for (l = 0; l < 3; l++) {
printf(" %d ",Martix[i][j][k][l]);
}
printf("\n");
}
}
}
for (i = 0; i < 1; i++) {
for (j = 1; j<2; j++) {
for (k=0; k<3; k++) {
for (l=0; l<3; l++) {
Martix[i][j][k][l] = 0;
printf(" %d ",Martix[i][j][k][l]);
}
}
}
printf("\n");
}
return 0;
}
字符数组——字符串
字符数组
最常用的是一维和二维字符数组
char 一维数组名[表达式]
char 二维数组名[表达式1][表达式2]
例子:char name[100];
char name[10][100];
二维字符数组的声明赋值和引用
字符串
用来表示一串字符的一种数据类型
初始化:char names[100][10] = {{"xiao ming"},{"xiaohui"},{"xiaoqiang"}};
例子:
#include
int main(int argc, const char * argv[]) {
//二维数组初始化
char name1[10] = {'x','i','a','o',' ','m','i','n','g'};
char name2[10] = "xiao ming";
int i;
int ascii_value;
for (i = 0; i<10; i++) {
ascii_value = (int)name1[i];
printf("name[%d] : %d\n",i,ascii_value);
}
return 0;
}
字符串的输入和输出——scanf()和printf()
#include
int main(int argc, const char * argv[]) {
//二维数组初始化
char name1[10];
scanf("%s",&name1);
printf("%s\n",name1);
return 0;
}
scanf的特殊处理
1.把输入的字符串中的字符一个一个保存到字符数组中,直到碰到空格或回车
2.当scanf函数碰到空格或回车以后,scanf就退出了
完整输出
#include
int main(int argc, const char * argv[]) {
char name[10];
int i;
for (i=0; i<2; i++) {
scanf("%s",name);
printf("[%d] %s\n",i,name);
}
return 0;
}
for 循环把scanf和printf执行了两次
指针
地址的概念
地址和指针是同一个东西,只是用处不同
地址的含义
我们取数组的下标,计算机看到的是不同的地址(数组的下标相当于门牌号,指定位置;而计算机看到的是快递上的通信地址)
可以简单理解成内存地址就是内存中的某个位置
为什要用地址?
当我们对数组操作的时候,我们明确知道是对哪个数组进行操作,所以用数组的下标就可以。
但是,对于计算机来说,一个程序可能有很多个数组只用下标就不够了。因此使用类似于“通信地址”地址。
(不同的数组下标可以相同)
⚠️功能类似,都是寻找具体的数据元素,只是使用的范围不同。
地址的表示和取址运算
取址运算:可以直接计算出C语言程序中任何一个变量的地址。
形式:
&变量名;
&是取址运算的运算符,“变量名”就是程序中声明定义的变量
例子:
int radius;//定义整型变量
&radius;//算出radius变量在计算机中的内存地址
内存地址就是一个数字(非负整数)
变量在计算机中的内存地址就是变量所在的内存位置的编号
例子:
#include
int main(int argc, const char * argv[]) {
//二维数组初始化
int radius;
unsigned int address;//定义了一个无符号整型变量
address = &radius;//取址运算把radius的内存地址计算出来,赋值给address
printf("%x\n",address);//由于程序中内存地址一般较大,所以使用十六进制表示进行输出
//efbff44c是radius的内存地址的16进制表示
return 0;
}
#include
int main(int argc, const char * argv[]) {
//二维数组初始化
int home[1000];
unsigned int address;//定义了一个无符号整型变量
address = &home[888];//home下标为888处元素的内存地址
printf("index = %d :: %x\n",888,address);
return 0;
}
指针和指针变量
指针的概念就是源于地址,但程序中更多用到指针的概念
指针的含义和用途
指针和地址本身是一个东西,用来指向某一个内存地址
指针表示一个位置,指针指向这个地址表示的位置
在计算机内存中,地址概念更多;在程序的时候,指针的概念更多
指针类型
用指针类型来表示指针,用指针变量来保存指针,因此指针变量也叫做指针类型变量
指针类型是类似于数组的高级数据类型,专门用来表示指针。
指针类型表示:
数据类型 *;
这里的数据类型就是数据类型的关键字;*是指针类型的标志,类似于数组的[];
⚠️不同数据类型的指针只能表示不同类型数据的内存地址
例如表示整型数据地址:
int *;
指针变量的定义和使用
定义:
指针类型 指针变量;
例如定义一个整型指针类型的指针变量address:
int *address;
#include
int main(int argc, const char * argv[]) {
int radius;
int *address;//定义一个整型指针变量
address = &radius;
printf("%x\n",address);
return 0;
}
void指针
⚠️不同数据类型的指针只能表示不同类型数据的内存地址
但是这样要求太严格,有的时候,人们在使用指针的时候不知道指针是什么类型
void指针也被称为空指针或者无类型指针
有了void指针,就可以用void指针变量随便保存任何数据类型的内存地址
void定义:
void * 变量名;
例如定义一个空指针address:
void * address;
可以用空指针变量来保存任意类型数据的内存地址:
#include
int main(int argc, const char * argv[]) {
int num;
float radius;
char name;
void * address;//定义一个空指针address
address = #//取址运算
printf("num addr = %x\n",address);
address = &radius;
printf("num addr = %x\n",address);
address = &name;
printf("name addr = %x\n",address);
return 0;
}
指针运算
取指针元素运算
就是用指针取得指针所指的内存地址处的数据:
*指针变量;
*就是取指针运算符(和基本运算乘法的运算符号*是相同的)但是含义不同
乘法运算的操作数是两个,而取指针元素运算的操作数只有一个
乘法运算的操作数是整型和浮点型,而取指针运算的操作数是指针类型
#include
int main(int argc, const char * argv[]) {
int radius = 3;
int *addr;
int value;
addr = &radius;//取址运算把radius的地址保存在整型指针变量addr中
//对radius进行取址运算,radius整型变量的地址将会保存到addr指针变量中
value = *addr;//使用指针元素运算把指针addr所指地址处的值拿出来赋值给整型变量value
printf("value = %d\n",value);
//证明取指针元素运算确实取出了radius变量的值;
return 0;
}
如果没有开始的赋值:
指针变量是随机的,每次运行存储的地点都不一样
⚠️清楚取的是什么地址的数据,这地址有没有想要的数据会导致崩溃
指针的自增自减
指针的自增自减和整型的相同
指针变量++;
++指针变量;
指针变量--;
--指针变量;
左增:先自增再赋值;右增:先自减再赋值
⚠️不同点在于1的意义
指针变量增减的1根据指针变量的类型不同而不同,对于指针变量,这个自增减的1表示的是指针所指数据在内存中所占的字节数
#include
int main() {
int radius;
int *addr;
radius = 0; //整型变量radius赋值为0
addr = &radius; //给指针addr赋值为radius的地址
printf("radius = %d addr = %d\n",radius,addr);
//分别对radius和addr分别做自增
radius++;
addr++;
printf("radius = %d addr = %d\n",radius,addr);
return 0;
}
⚠️void空指针不能做自增和自减——空指针指向的数据所占的内存字节数不知道有多少
指针的类型转换
指针同样可以进行强制类型转换和隐式类型转换,基本规则相同
1.指针的强制类型转换
如果数据类型不能确定,使用了void指针,之后又要进行自增自减?(空指针不能做自增自减)
可以使用指针类型的强制转换,把无类型的void指针强制转换成其他有类型的指针
理论上,void可以强制转换成其他任何数据类型,包括基本数据类型。但是在使用的时候都是把void指针强制转换成其他指针类型
表示形式:(其他非void指针类型)void指针变量名;
小括号里面是其他非void指针类型(int*、char*、float*)
例如将void指针转换成其他类型的指针:
#include
int main() {
int num;
float radius;
char name;
void *addr;
addr = #
printf("int [%x] ->",addr);
addr = (int*)addr+1;
printf("[%x]\n",addr);
addr = &radius;
printf("float [%x] ->",addr);
addr = (float*)addr+1;
printf("[%x]\n",addr);
addr = &name;
printf("char [%x] ->",addr);
addr = (char*)addr+1;
printf("[%x]\n",addr);
return 0;
}
2.指针的隐式类型转化
把指针类型的数据按照整型的十六进制进行输出,因为在输出前,发生了隐式的类型转换,计算机自动地根据输出格式“%x”进行输出
#include
int main() {
int radius;
int addr;
int * p_addr;
p_addr = &radius;
addr = p_addr;
printf("addr = %x\n",addr);
return 0;
}
数组和指针
数组的下标和指针有着类似的作用,但指针的适用范围比数组下标广的多。
数组名也是指针
数组名也是指针即数组中第一个元素的地址
#include
int main() {
int radius[5] = {1,2,3,4,5};
int *p_addr;
int element;
p_addr = radius;
printf("radius = 5x\n",p_addr);//这里警告把一个地址按照整型输出会有类型冲突
element = *radius;//取指针元素运算
printf("element = %d\n",element);
return 0;
}
数组名是指针常量
证明:
#include
int main() {
int radius[5] = {1,2,3,4,5};
int num;
radius = #//取址运算得到num的地址
//这里报错,警告等号左边应该是一个变量,证明radius是一个指针常量
return 0;
}
使用数组名访问数组元素
数组中的元素地址是相邻的,数组名指向的是数组中的第一个元素,因此可以用数组名加上一个数来访问数组中的元素。
数组名加减1相当于给数组名对应的指针加减一个所指元素所占的字节数的值
但是⚠️数组名不能自增自减,因为前面的已经说过了,数组名是一个指针常量。
所以只能适合常量的加法运算了
#include
int main() {
int radius[5] = {1,2,3,4,5};
int *addr;
//每次给数组名加一个值来得到相应位置元素的地址,并将这个地址赋值给一个指针变量。然后再输出使用取指针元素运算将数组中元素的值取出来输出
for(int i=0;i<5;i++)
{
addr = radius + i;
printf("radius[%d] = %d\n",i,*addr);
}
return 0;
}
三种访问数组元素的方法
1.使用数组下标
#include
int main() {
int radius[5] = {1,2,3,4,5};
int i;
int element;
for (i=0; i<5; i++)
{
element = radius[i];
printf("radius[%d] = %d\n",i,element);
}
return 0;
}
2.使用数组名
#include
int main() {
int radius[5] = {1,2,3,4,5};
int element;
int i;
for (i=0; i<5; i++) {
element = *(radius+i);//取指针元素运算
printf("radius[%d] = %d\n",i,element);
}
return 0;
}
3.使用指针变量
#include
int main() {
int radius[5] = {1,2,3,4,5};
int element;
int i;
int *addr;
addr = radius;//用指针变量addr来储存数组名
for (i=0; i<5; i++) {
element = *addr;
printf("radius[%d] = %d\n",i,element);
addr++;//地址自增,转换到下一个地址
}
return 0;
}
数组指针和指针数组
数组指针就是数组的地址。数组的地址就是数组的第一个元素的地址,也叫首地址,就是数组名
对于一个数组,最重要的是知道数组名,因为知道数组名,用下标、数组名、指针变量都可以得到数组元素。
指针数组,就是数组中的元素的数据类型是指针类型。把基本数据类型的数组中的元素换为保存指针(地址)的指针类型就可以
表示形式:
指针类型 数组名[表达式];
例如:
int *addrs[5];
可以存放整型指针的值
例如用指针数组来保存一系列地址,然后使用取指针元素运算取出每个地址保存的数组的值:
#include
int main() {
int radius[5] = {1,2,3,4,5};
int element;
int *addr[5];
int i;
for (i=0; i<5; i++)
{
addr[i] = &radius[i];
}
for (i=0; i<5; i++)
{
element = *addr[i];
printf("addr[%d] = %x element = %d\n",i,addr[i],element);
}
return 0;
}
多重指针和多维数组
多重指针
指针也是一种数据,用来保存内存地址
C语言用多重指针来存储指针数据的地址
多重指针类型表示:
指针类型 *;
多重指针变量:
多重指针类型 多重指针变量;
多重指针类型可以是int*或者int**的二重指针类型,或更高重的指针类型
#include
int main() {
int radius;
int* addr;
int** p_addr;
radius = 3;
addr = &radius;
p_addr = &addr;
printf("[%x] = %d\n",&radius,radius);
printf("[%x] = %d\n",&addr,addr);
printf("[%x] = %d\n",&p_addr,p_addr);
return 0;
}
取多重指针元素运算
取二重指针变量:
**二重指针变量;
**表示进行了两次取指针运算
#include
int main() {
int radius;
int* addr;
int** p_addr;
int element;
radius = 3;
addr = &radius;
p_addr = &addr;
element = **p_addr;
printf("element = %d\n",element);
return 0;
}
多维数组名和各维元素
多维数组的多重数组名就是一个多重指针,除了第一维是保存基本数据,其他维保存的元素都是指针
#include
int main() {
int num[3][4] = {{0,1,2,3},{4,5,6,7},{8,9,10,11}};
int i,j;
printf("[%x]:num = %x\n",num,*num);//num是一个指针
for (i=0; i<3; i++)
{
printf("[%x]:num = %x\n",num,*num);
for (j=0; j<4; j++)
{
printf("[%x]:num[%d][%d] = %d\n",&num[i][j],i,j,num[i][j]);
}
}
return 0;
}
输出的每一行中
[ ]中的是是一个内存地址
冒号和等号之间就是这块内存地址所对应的变量
等号后面的就是这块内存中所储存的数据
[efbff420]:num = efbff420
[efbff420]:num = efbff420
[efbff420]:num[0][0] = 0
[efbff424]:num[0][1] = 1
[efbff428]:num[0][2] = 2
[efbff42c]:num[0][3] = 3
[efbff420]:num = efbff420
[efbff430]:num[1][0] = 4
[efbff434]:num[1][1] = 5
[efbff438]:num[1][2] = 6
[efbff43c]:num[1][3] = 7
[efbff420]:num = efbff420
[efbff440]:num[2][0] = 8
[efbff444]:num[2][1] = 9
[efbff448]:num[2][2] = 10
[efbff44c]:num[2][3] = 11
⚠️多维数组的最后一维的元素的是数组保存的数据
剩下其他各维和多维数组名都是指针
使用指针访问多维数组
访问元素依旧可以使用下标、数组名和指针变量三个方法
数组非最后一维的元素来访问数组中的数据
#include
int main() {
int num[3][4] = {{0,1,2,3},{4,5,6,7},{8,9,10,11}};
int i,j;
int element;
for (i=0; i<3; i++)
{
for (j=0; j<4; j++)
{
element = *(num[i]+j);//将数组num中的第一维的每个元素加上一个数值,然后再取值针元素运算
printf("%d\n",element);
}
}
return 0;
}
数组名来访问数组中的数据的例子:
#include
int main() {
int num[3][4] = {{0,1,2,3},{4,5,6,7},{8,9,10,11}};
int i,j;
int element;
for (i=0; i<3; i++)
{
for (j=0; j<4; j++)
{
element = *(*(num+i)+j);//*(num+i)指针加上一个数,然后取数组元素运算
printf("%d\n",element);
}
}
return 0;
}
二维数组指针的定义:
数据类型 (*数组指针名) [表达式];
小括号必须有,用来保证定义出来的是一个指针变量
C语言中数组各维是有大小的,定义的指针必须知道数组中各维的大小。要定义一个更高维度的数组指针,就必须知道除最高维以外的其他各维的大小,形式就是
例如:
int num[2][3][3][5]的多维数组指针,形式就是int *num[3][3][5]
#include
int main() {
int num[3][4] = {{0,1,2,3},{4,5,6,7},{8,9,10,11}};
int i,j;
int element;
int (*p_addr)[4];
p_addr = num;//数组名是一个指针
for (i=0; i<3; i++)
{
for (j=0; j<4; j++)
{
element = *(*p_addr+j);//*p_addr相当于num[0]、num[1]、num[2]
printf("%d\n",element);
}
p_addr++;//将num[i]中的i不断增加1
}
return 0;
}
字符串和指针
字符串是特殊的数组
字符指针类型表示:
char*;
定义字符指针变量:
char* 字符指针名;
例如:char* p_str;
例如用p_str来保存字符character的内存地址,并使用取指针运算取出p_str所指内存中的字符:
#include
int main() {
char character;
char value;
char *p_str;//字符指针
character = 'c';
p_str = &character;//取地址运算,字符变量的地址
value = *p_str;//取指针元素运算,地址中保存的数据
printf("character = %c,[%x] = %c\n",character,p_str,value);
return 0;
}
字符指针和字符串
字符数组来表示和储存字符串
字符指针是可以保存字符数据的地址的
字符指针是可以用来访问操作字符串:
1.C语言用字符数组来表示和存储字符串的,所以可以把字符串和字符数组等价起来
2.数组名就是数组的首个元素的地址,那么字符数组名也就是字符数组中首个字符的地址。
3.字符指针可以用来保存字符的地址,因而可以用字符指针保存字符数组的首个字符的地址了。
这样就可以使用字符指针访问操作字符串了
字符指针可以访问字符串的每个元素
#include
int main() {
char name[10] = "xiao ming";//使用字符数组来保存字符串
char *p_str;
char value;
int i;
p_str = name;//字符数组的首个元素的地址赋值给字符指针变量p_str
for (i=0; i<10; i++,p_str++)
{
value = *p_str;
printf("[%x] = '%c' = %d\n",p_str,value,value);
}
return 0;
}
scanf、printf和字符指针
可以使用%s来直接输入和输入字符串,因为scanf和printf需要字符指针
scanf(“%s”,字符指针);
将输入的字符串挨着保存到“字符指针”开始的内存地址处
printf(“%s”,字符指针);
直接给字符指针变量赋值为字符串常量的表示形式。只能在初始化使用
char* 字符指针变量 = “字符串”;
例子:char* name = “xiao ming”;name中保存的是字符串中第一个字符的地址
#include
int main() {
char name[10];
char *p_name;//字符指针变量,指向的是字符数组的地址
char *p_name1 = "xiaoming";
p_name = name;
scanf("%s",p_name);
//输入保存到p_name所在的内存地址
printf("p_name[%x] = %s\n",p_name,p_name);
printf("P_name1[%x] = %s\n",p_name1,p_name1);
return 0;
}