目录
一.数组
1.1数组概念
数组在内存上是连续的,它是用来存储一组类型相同的数据的,数组是一个构造类型的数据。
1.2一维数组
1.2.1一维数组定义
储存类型 数据类型 数组名[i]
存储类型: auto,const,static,extern,register,volatile
没有写存储类型的变量默认是auto
数据类型: char,short,int,long,lomg long,构造类型,float,double
注意:
1.数组名是一个常量,定义时要符合命名规范。
2.常量是不可修改的
3.i代表了一个常量,表示数组中有几个元素。数组下标的范围 0到i-1
4.在使用时特别需要注意数组下标越界的问题,数组越界访问编译器不会报错
5.数组元素必须逐个引用
例如:
int name[5]; // 这是定义了一个一维数组,数组名为name,数组名name是这个数组的首地址,
name是一个常量不可以被修改,他的含义是这个数组中有5个int类型的数据元素
注意:数组一旦定义好了 就不能整体赋值了
1.2.2一维数组内存的分配
在内存中,数组占用连续的存储空间,并且根据每个元素所占存储空间的大小来进行分配内存。
使用sizeof(数组名)可以计算出数组所占空间的大小,sizeof(数组名[下标])可以计算数组中
每个元素所占存储空间的大小。
1.2.3一维数组的使用
一维数组初始化:
1.如果数组定义完不进行初始化,那么里面存放的是一些脏数据。
例如:int name[5]; 定义完之后不对它进行初始化,那么name数组里面的值就是一些脏数据,随机值
2.在定义数组是可以进行不完全初始化,不完全初始化是按照数组下标的大小顺序来进行初始化的。
例如: int name[5]={1,2}; 这里只是对name数组下标为0,1的元素进行初始化,下标为2,3,4的元素默认使用0来进行初始化。
3.完全初始化就是在定义数组时,对数组的每一个元素都进行了初始化。
例如: int name[5]={1,2,3,4,5}; 这就是完全初始化
注意:
1.若要在定义时进行初始化那么只能把数据写在定义后面。
2.如果数组列表中的个数超出了数组元素的大小则会出错
#ifdef 1
//正确写法
int name[5]={1,2,3,4,5};
#endif
#ifdef 0
//这种初始化写法是错误的
int name[5];
name={1,2,3,4,5};
#endif
1.2.4数组元素赋值
给数组元素赋值的时候可以使用数组下标赋值。
例如:
#ifdef 1
int main(){
int name[3];
name[0]=1; //给数组下标为0的元素赋值
name[1]=2;
name[2]=3;
return 0;
}
#endif
//以下代码时错误的
#ifdef 0
int main(){
int sum[5]={1,2,3,4,5};
int num[5];
sum=num; //数组名是常量,常量不允许修改
sum[5]=num[5]; //会出现数组下标越界
sum[1]=num[1]; //‘=’两边取出的都是数组下标为1的元素,类型为int的常量,常量不可以被赋值
num[5]={6,7,8,9}; //不起作用
return 0;
}
#endif
/* 一维数组读写与遍历 */
#include<stdio.h>
int main(){
int name[5];//定义了一个int类型,数组名为name的数组,数组中最多有五个元素,注意这里没有初始化
//只是定义了一个数组。
//写入数据,需要注意在访问数组时,数组下标是否越界
for(int i=0;i<5;i++){
printf("请输入元素");
scanf("%d",&name[i]); //虽然name是数组的地址,但是name[i]代表的是第(i+1)个元素,所以需
//要取地址,在接收用户输入后把值放在这个地址的内存空间
}
for(int i;i<5;i++){ //注意数组下标越界问题,数组下标是从0开始的,这里写(%d+1)是为了方便理解
printf("第(%d+1)个元素为: %d\n",i,name[i]);
}
return 0;
}
1.2二维数组
1.2.1二维数组的定义
通俗来说二维数组就是有两个下标的数组。
格式定义:存储类型 数据类型 数组名 [下标1] [下标二]
说明:二维数组与一维数组类似,二维数组在内存上也是连续的
使用:int rain[5][3];//表示内含5个数组元素的数组,每个数组元素里面内含3个int类型的元素。
1.2.2二维数组的使用
例题:
编写一个函数,把两个数组中相对应的元素相加,然后把结果储存到第 3 个数组中。也就是说如果
数组 1中包含的值是 2、4、5、8,数组 2中包含的值是 1、0、4、6,那么该函数把 3、49、14
赋给第3 个数组。函数接受3 个数组名和一个数组大小。在一个简单的程序中测试该函数。
#include<stdio.h>
#define NUM 4
/*这一部分是函数声明,在编写函数的过程中最好每一个函数都要写上,养成良好的编程素养*/
int *array_add(int *arry_one,int *arry_two,int *result_aray,int len);
void ergodic(int *ergodic_array,int len);
int main(){
int array_1[NUM]={1,2,3,4};
int array_2[NUM]={1,2,3,4};
int result[NUM];
ergodic(array_add(array_1,array_2,result,NUM),NUM);
return 0;
}
/*这里定义了一个指针函数,它的本质还是一个函数,只不过返回值是一个地址,后续笔记中会详细说明*/
// 1.使用函数进行数组操作,这个函数的形参需要定义成指针类型的,因为传入的参数是这个数组的地址
// 2.指针讲完数组后会详细叙述
/* 第一个参数int *arry_one 是一个int类型的指针
第二个参数int *arry_two
第一个参数与第二个参数里面对应元素需要相加,把得到的结果放在第三个int *result_aray参数
所指向的内存空间里面
第四个参数是数组的元素的个数
*/
int *array_add(int *arry_one,int *arry_two,int *result_aray,int len){
for(int i=0;i<len;i++){
result_aray[i]=arry_one[i]+arry_two[i];
}
return result_aray;
}
void ergodic(int *ergodic_array,int len){
printf("数组元素为:");
for(int i=0;i<len;i++){
printf("%d ",ergodic_array[i]);
}
printf("\n");
}
二.指针
2.1指针概念
什么是指针?指针就是值为内存地址的某种数据类型的变量。
例如:
int a=10; //a这片内存空间里存的内容是10
int *p=&a; //p这片内存空间里存的内容是a的地址,也可以说p指向的是a
注意:
地址运算符 :&
如果&后面跟一个变量的话,&取出的是这个变量的地址
地址运算符: *
如果后面跟的是指针或者地址时,*给的是存储在指针指向地址的值
*p解释:获取p指向地址上的值,按照上面的例子得出的结果是 *p==a==10
2.1.1指针的声明
声明指针时必须声明指针指向变量的数据类型,不同变量的类型占用的存储空间也不同,所以操作对象
的大小也不同。
格式:类型说明符 *变量名;
注意:类型说明符表明了指针所指向对象的类型
例如:
int *p;//变量p是一个指向int类型的指针,*p是 int 类型的
2.1.2指针的使用
#include<stdio.h>
int main(){
int a=10;
int *p=&a;
printf("p指向变量a的地址:%p\n",p);
printf("*p的值 :%d\n",*p);
printf("指针p的大小:%ld\n",sizeof(p));
return 0;
}
/*p的值 :10
指针p的大小:8*/
例题:写一个函数交换变量x,y的值。
#include<stdio.h>
void interchange(int *p,int *q);
int main(){
int x=10;
int y=2;
interchange(&x,&y);
printf("x=%d\n,y=%d\n",x,y);
return 0;
}
void interchange(int *p,int *q){
int temp=*p; //将指针p所指向内存空间里的值赋值给temp
//把这个值赋给一个临时的变量temp
*p=*q; // *p = *q; 将指针 p 所指向的内存地址处的值修改为指针 q 所指向的内存地址处的值
*q=temp;
}
2.2指针与数组
2.2.1指针与一维数组
加入在函数里面我们定义了一个数组: int name[5]={0};//把数组元素都初始化为0
那么数组名name就是这个数组的首地址,&name[0]它的含义是取第一个数组元素的首地址name==&name[0],
他们两个都是常量在程序运行期间不会发生改变,可以使用指针接收地址的值,然后使用指针修改指向内存空间的
值。前面交换两个变量的值使用的就是指针
从新定义一个int类型的数组并初始化;
int name[5]={1,2,3,4,5};
int *p=name;
那么:p+2==&name[2]; //地址相同
*(p+2)==name[2]; //*(p+2)它的意思是从指针p所指向的地址向后移动2个类型说明符(这里是int)
的地址,然后检索存储在那里的值
2.2.2.1演示指针和一维数组
#include<stdio.h>
void getElement(int *p,int len); //说明:第一个参数需要传入的数据是地址,第二个是长度
int main(){
int name[10]={1,2,3,4,5,6,7,8,9,10};
//使用下标取元素
for(int i=0;i<10;i++){
printf(" %d ",name[i]);
}
printf("\n");
//使用地址取元素
for(int i=0;i<10;i++){
printf(" %d ",*(name+i));//name表示的是首地址,(name+i)表示首地址向后偏移几个字节的
//类型说明符(这里是int,所以一次偏移4个字节),
//*(name+i)检索这个地址里面的值
}
printf("\n");
getElement(name,10);
return 0;
}
void getElement(int *p,int len){
for(int i=0;i<10;i++){
printf(" %d ",p[i]);
}
printf("\n");
for(int i=0;i<10;i++){
printf(" %d ",*(p+i));
}
printf("\n");
}
//从以上代码可以得出 p[i]==name[i]==*(name+i)==*(p+i)
//指针相减
#include<stdio.h>
int main(){
int name[5]={1,5,3,4,5};
int *p1,*p2,*p3;
p1=name;
/*注意:地址应该和指针类型兼容,就是说不能把一个double类型的地址赋值给int类型的指针*/
p2=&name[3];
//一个指针减去另一个指针
//表示的是两个指针之间相差几个类型说明符(这里是int)的字节
printf("pt2=%p ptq=%p p2-p1=%ld\n",p2,p1,p2-p1);
return 0;
}
指针减去一个整数:
#include <stdio.h>
int main() {
int array[5] = {1, 2, 3, 4, 5};
int* ptr = &array[3]; // 指向数组中的第4个元素
printf("原始指针的值:%p\n", ptr);
int offset = 2;
int* newPtr = ptr - offset; // 将指针向前偏移2个单位
printf("偏移后的指针的地值:%p\n", newPtr);
printf("偏移后的指针指向的值:%d\n", *newPtr);
return 0;
}
//注意数组越界问题
//使用函数遍历数组:使用函数声明void printArray(const int arr[], int size);
#include <stdio.h>
void printArray(const int arr[], int size);
int main() {
int array[] = {1, 2, 3, 4, 5};
int size = sizeof(array) / sizeof(array[0]);
printArray(array, size);
printf("%p\n",array);
return 0;
}
/*1.第一个参数表明的是这是一个类型为const int arr[]的数组指针,
可以接收指向其他整型的指针或地址*/
void printArray(const int arr[], int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
printf("%p\n",arr);
}
2.2.1数组指针
本质是一个指针,指向一个二维数组,也叫做行指针。多用与将二维数组作为函数的参数传递时。
格式:数据类型 (*数组指针名)[列宽];
例如: int (*p)[4];//定义了一个列宽为4的的指针p ,p指向的元素为int
2.2.2指针数组
本质是一个数组,数组中每个元素都是一个指针类型。
格式:数据类型 *指针数组名[下标];
//定义了一个指针数组 数组名叫name2 数组中共有4个元素
//每个元素都是一个 char * 类型的指针
char *name2[4];
name2[0] = "zhangsan";
name2[1] = "lisi";
name2[2] = "fulajimier.fulajimiluoweiqi.pujing";
name2[3] = "zhaoliu";
printf("%s\n", name2[0]);
printf("%s\n", *(name2+1));
printf("%s\n", name2[2]);
printf("%s\n", name2[3]);
2.2.3指针与二维数组
在了解指针与数组的基础上来学习二维数组与指针.
#include<stdio.h>
int main(){
int name[4][2]={{1,2},{3,4},{5,6},{7,8}};
//这是一个数组指针,二维数组在使用时可以搭配数组指针使用,本质还是一个指针
int (*p)[2]=NULL;
//在使用指针的时候,如果在定义的时候不给他指定地址时建议赋值为NULL防止野指针。
p=name;
//1.name是二维数组首元素的地址,所以他的值和&name[0]的值相同。
//又因为name[0]是第一个数组的首地址,所以他的值和首元素&name[0][0]的值相同
//在指针变量前使用*(解引用操作)或者在数组名后使用[下标],得到引用对象的值
printf("%p\n",name);//输出的是二维数组的首地址
/*
由于name是二维数组的首地址,又由于数组在内存空间上是连续的。
那么sizeof(name)就是整个二维数组在内存空间所占的大小,sizeof(name[0])就是一维数组
所在空间的大小。sizeof(name)/sizeof(name[0])就是二维数组中一维数组的个数
*/
printf("%ld\n",sizeof(name)/sizeof(name[0]));
//求一维数组同理
printf("%ld\n",sizeof(name[0])/sizeof(name[0][0]));
printf("-------1------------\n");
//以下是几种遍历方法
for (int i = 0; i < sizeof(name)/sizeof(name[0]); i++){
for(int j=0;j<sizeof(name[0])/sizeof(name[0][0]);j++){
printf(" %d ",name[i][j]);
}
printf("\n");
}
printf("-------2------------\n");
for (int i = 0; i < sizeof(name)/sizeof(name[0]); i++){
for(int j=0;j<sizeof(name[0])/sizeof(name[0][0]);j++){
printf(" %d ",p[i][j]);
}
printf("\n");
}
printf("-------3------------\n");
for (int i = 0; i < sizeof(name)/sizeof(name[0]); i++){
for(int j=0;j<sizeof(name[0])/sizeof(name[0][0]);j++){
printf(" %d ",*(*(p+i)+j));
}
printf("\n");
}
printf("-------4------------\n");
for (int i = 0; i < sizeof(name)/sizeof(name[0]); i++){
for(int j=0;j<sizeof(name[0])/sizeof(name[0][0]);j++){
printf(" %d ",*(p[i]+j));
}
printf("\n");
}
return 0;
}
2.3二级指针
二级指针是用来保存一级指针的地址的。
一般多用于将一级指针的地址作为函数的参数传递时。
int x = 10;//变量
int *p = &x;//一级指针
int **q = &p;//二级指针
2.3.1二级指针简单的运算
#include<stdio.h>
int main(){
int a=10;
int *p=&a;
int **q=&p;
printf("a=%d , *p=%d, **q=%d\n",a,*p,**q);
printf("&a=%p , p=%p , *q=%p \n",&a,p,*q);
printf("&p=%p , q=%p\n",&p,q);
//有了上述代码 有如下的等价关系
// a <==> *p <==> **q
// &a <==> p <==> *q
// &p <==> q
return 0;
}