一、学习目标:
二、指针、数组的组合技能
引言
指针数组
语法
数组指针
三、勇士闯关秘籍
四、大杂脍
总结
知识点:
概念: 他是一个数组,该数组存储的多个类型相同的指针。
int * arr[ 10 ] ; // 整形指针数组
char * arr[ 10 ] ; // 字符指针数组
float ** arr[10 ] ; // 浮点型的二级指针数组
int a = 123 ;
int b = 456 ;
int c = 789 ;
int * arr[10] = { &a, &b ,&c }; // 定义了一个有10个元素的数组arr ,这10个元素都应该是 int 类型的地址
printf("*arr[0]:%d\n" , *arr[0] );
// arr === &a的地址 --> *arr === &a --> **arr === **&&a === a == 123
printf("**(arr+1):%d\n" , **(arr+1) );
printf("**(arr+2):%d\n" , **(arr+2) );
操作练习:
char * s1 = "Even";
char * s2 = "Jacy";
char * s3 = "Yilia";
char * s4 = "TieZhu";
char ** arr[4] = { &s1 , &s2 , &s3 ,&s4 };
char ***ptr = arr ;
// 使用尽可能多的方式来通过ptr 正确输出 s1 s2 s3 以及 s4
printf("s1:%s\n" , *arr[0] );
printf("s2:%s\n" , *ptr[1] );
printf("s3:%s\n" , **(ptr+2) );
printf("s4:%s\n" , **(3+ptr) );
printf("s4:%s\n" , *3[ptr] );
// 尝试输出 s1 中的 字符 ‘E’
printf("s1:%c\n" , **arr[0] );
printf("s1:%c\n" , ***arr );
printf("s1:%c\n" , ***ptr );
// 尝试输出 s3 中的 字符 ‘Y’
printf("s3:%s\n" , **(ptr+2));
printf("s3:%c\n" , ***(ptr+2));
printf("s3:%c\n" , **(ptr[2]));
// 尝试输出 s4 中的 字符 ‘Z’
printf("s4:%s\n" , *ptr[3]);
printf("s4:%c\n" , (*ptr[3])[3]);
printf("s4:%c\n" , *((*ptr[3])+3));
概念: 一个指针,该指针指向一个数组。
语法:
数组类型 (*ptr) [ 数量 ];
示例代码:
int arr[10] = {1,2,3,4,5,6,7,8,9,0};
float * f [3]; // &f 的类型是 float *(*) [3]
// int *p [10]; // 指针数组
int (*ptr) [10] ; // 数组指针
ptr = &arr ;
// int *ptr1 = &arr ; // &a 的类型是 : int (*)[10]
// 如何把3改成30
arr[3] = 30 ;
// ptr = &arr ;
(*ptr)[4]= 40 ;
*((*ptr)+5) = 50 ;
* ((*(ptr+1))-1) = 999 ;
for (int i = 0; i < 10 ; i++)
{
printf("arr[%d]:%d\n" , i , arr[i]);
}
第一关
第二关
第三关
第四关
第五关
第六关
第七关
第八关
第九关
数组
概念:使用一篇连续的内存空间来存储一组类型相同的数据。
可以使用它来定义一系列类型相同的数据。
语法
数据的类型 数组名 [ 元素的数量 ] ;
int arr_int [32] ; // 定义了一组数据 32个(连续的内存空间)并该组数据的类型是int 类型
float arr_float [32] ; // 定义了一组数据 32个(连续的内存空间)并该组数据的类型是float 类型
double arr_double [64] ; // 定义了一组数据 64个(连续的内存空间)并该组数据的类型是double 类型
char arr_str [] = "Hello" ; // 定义了一组数据(连续的内存空间)并该组数据的类型是char 类型
// 以上语句中没有直接说明数组中元素的数量,那么因此就必须有初始化动作,数组的大小就由初始化的数据量来决定
注意:
数组名字的含义:
数组名字通常有两种含义:
整个数组的地址:
在定义语句中数组名表示整个数组的地址。
在 & 取地址符中, 数组名表示整个数组的地址。
在sizeof 运算符中 数组名表示整个数组的地址。
首元素首地址:
除了以上三个情况其余的都表示首元素的首地址。
数组的下标:
概念: 数组的下标实际上是基于数组的入口地址的偏移量,偏移量的单位则是该数据则类型(指针的加减运算)。
int arr [10] = {1,2,3,4,5,6,7,8,9,0};
arr[0] -- > 0 没有做任何偏移,因此可以访问到数组中的第0个元素 --> 数组 1
arr[3] -- > 数组的第3个元素 --> 数据 4
指针:
概念
指针也是一个变量,只不过这种变量专门用于存储指定数据的内存地址。
既然指针变量用于存储的是地址数据,那么指针的大小就固定下来由系统的位数决定。
比如32位的系统他所有的地址都是32位的数据也就是4个字节
64位系统他所有的地址都是64位的数据也就是8个字节
语法:
指向的类型 (*指针的名字) ;
int a = 123;
float f = 2345.345;
long l = 3245232345 ;
int * ptr_int = &a; // 定义了一个整形指针 ptr_int 并把a的地址存入其中
float (*ptr_float) = &f ; // 定义了一个浮点指针 ptr_float 并把f的地址存入其中
long * ptr_long = &l ; // 定义了一个long指针 ptr_long 并把l的地址存入其中
指针的加减操作:
概念: 指针的加减就是基于指针当前所指向的地址进行偏移运算,而偏移量的单位则取决于指针的类型。
因此当对一个指针进行+1 或 -1 操作时加减的单位是该指针自己的类型,与它所指向的数据类型没有任何关系。
(64位系统)
char * ptr_c = 0x100000 ;
short *ptr_s = 0x100000 ;
int *ptr_i = 0x100000 ;
long * ptr_l = 0x100000 ;
ptr_c + 1 则偏移一个指针的类型char 也就是1个字节因此地址值会变成 0x100001
ptr_s + 1 则偏移一个指针的类型short 也就是2个字节因此地址值会变成 0x100002
ptr_i + 1 则偏移一个指针的类型int 也就是4个字节因此地址值会变成 0x100004
ptr_l + 1 则偏移一个指针的类型long 也就是8个字节因此地址值会变成 0x100008
int ** ptr_1 = 0x100000 ;
char ** ptr_2 = 0x100000 ;
float ** ptr_3 = 0x100000 ;
int *** ptr_4 = 0x100000 ;
char **** ptr_5 = 0x100000 ;
float ***** ptr_6 = 0x100000 ;
ptr_1 ptr_2 ptr_3 ptr_4 ptr_5 ptr_6 这些指针在+1 的时候都+ 系统的位数(64位系统) 8个字节 0x100008
不管是多少级的指针只要超过了二级都是在+1一个地址的大小
指针数组:
概念: 他是一个数组,该数组拥有一片连续的内存空间并该空间中存储了一组 指针数字据 (地址数据)。
一个存储了一组指针的数组称为指针数组。
语法:
指针的数据类型 * (数组名字 [元素的数量]) ;
int * ( arr_ptr [3] ) ={&a , &b , &c } ; // 定义了一个数组,该数组中存储了 3个元素,每一个元素都是int 类型的地址 。
注意:
指针数组的使用与数组没有任何区别。
实例代码:
int a = 123 ;
int b = 456 ;
int c = 789 ;
// 定义了一个数组,该数组中存储了 3个元素,每一个元素都是int 类型的地址 。
int * arr_int_p [3] = {&a , &b , &c} ; // 分别把 a , b , c 的地址存入数组中
// 如何访问该数组中的所有元素
printf( "arr_int_p[0]: %p:%d \n" , arr_int_p[0] , *arr_int_p[0] );
printf( "arr_int_p[0]: %p:%d \n" , *(arr_int_p + 1) , **(arr_int_p + 1) );
// ptr_int 是一个普通的整形指针,该指针存储了 &a 的地址 (二级指针)
int ** ptr_int = arr_int_p ; // 使用二级指针来访问二维数组
printf ( "*(ptr_int + 2):%p:%d\n", *(ptr_int + 2) , **(ptr_int + 2));
数组指针:
概念: 他是一个指针变量,该指针变量存储的数据是一个数组类型是地址。
语法:
数组中元素的类型 (* 指针变量名) [ 数组的元素数量 ] ;
int (*p_int) [10] ; // 【整形数组指针】 定义了一个指针变量 p_int ,它指向的数据的地址应该是一个整形数组的地址, 而且该数组中有10个元素
float (*p_float) [10] ; // 【浮点数组指针】 定义了一个指针变量 p ,它指向的数据的地址应该是一个浮点数组的地址, 而且该数组中有10个元素
实例代码:
int arr[10] = {1,2,3,4,5,6,7,8,9,0};
// &arr 表示整个数组的地址,因此他的类型是数组类型的地址
int (* ptr) [10] = &arr ;
// ptr = &arr *ptr == *&arr == arr
// arr[2]
printf ("(*ptr)[2]:%d\n" ,(*ptr)[2] ) ;
printf ("* ((*ptr) + 3):%d\n" , * ((*ptr) + 3) ) ;
// 使用一个一级指针来访问数组中的各项元素
int * p = arr ;
// p = arr
// arr[5]
printf( "p[5]:%d\n" , p[5] );
printf( "*(p + 6):%d\n" , *(p + 6) );
循环:
概念: 使得每一段代码在特定的条件满足请跨下重复地运行。
循环的类型:
for 是一个集成度比较高的循环结构,它包含的【初始化】、【判断】、【迭代语句】
while 是一个在条件满足的情况下执行的一个循环
do-while 是一个在执行一次代码后判断如果条件满足则执行重复代码的一个循环
for循环:
语法:
for ( 初始化语句可以使用,逗号一次性写多个 ; 循环的判断语句 ; 循环控制变量的更新迭代 也可以是用,逗号表达式来连接多个表达式 )
{
}
注意:
while循环:
语法:
只要布尔表达式为真(非零) ,的情况下循环体中的代码就会被不断循环
while ( 布尔表达式 )
{
// 循环体
}
do-while循环:
语法:
先执行一次循环体中的代码,然后判断布尔表达式是否为真,如果为真则重复执行循环体中的代码。
do
{
// 循环体
}while(布尔表达式) ;
逗号表达式
实例:逗号表达式会从左往右依次运算,最终的值取决于最后一个表达式的值。
int a = 123 ;
int b = 567 ;
int c = 789 ;
int d = (a++ , ++b , ++c) ;
printf("a:%d b:%d c:%c d:%d\n" , a , b, c , d );
本文介绍了C语言进阶之路上小BOSS的一些特点和打败方法,大家认真学习即可逐步突破,最终问鼎巅峰~
本文参照 粤嵌文哥 部分课件经整理和修改后发布在C站,如有转载,请联系本人