c语言指针与数组的深入理解
引言:本篇再一次写到指针,学过c语言的都知道,指针是初学c语言时候遇到的一个比较难搞的知识点。你尽管可以想的简单,但是其实如果去用的话,没有一个更加深入的理解,那么后续的学习到数据结构就会艰难无比。
指针?c语言的灵魂所在。本篇还是会结合数组来讲,当指针和数据结合起来的时候就会变得奇妙无比。提高一点点的难度,记得第一次也写过c语言的指针,只不过相对简单。第一篇c语言指针的链接如下
深入理解c语言指针与数组
c语言指针与数组
一: 指针的理解与操作
1:指针与指针变量?
指针是什么?以及指针地址的概念?
指针就是地址,地址就是指针。指针变量可用于存放地址。
在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。意思是通过它能找到以它为地址的内存单元。
存放地址的变量称为指针变量。指针变量是一种特殊的变量,它不同于一般的变量,一般变量存放的是数据本身,而指针变量存放的是数据的地址。
将指针的等同于指针变量是不严格的说法,指针并不是存放地址,指针变量才可以存放地址。我们从概念上区分。但是可能会通常会把指针变量也简化称之为指针了,但是我们需要知道,实际上并不等同。
然后我们简单写代码也可以验证这个说法
#include<stdio.h>
#include<windows.h>
int main()
{
int a = 0;
int *p = &a;//定义了指针变量存放了a的地址
printf("the address of a is %d\n",&a);
printf("the value of p is %d or %p\n",p);
system("pause");
}
2:二级指针,多级指针,指向指针的指针?
这边的指针大家在代码中就理解为指针变量就可,这样严格一些就不会弄混。
我们定义一个指针变量,既然是一个变量,那肯定需要空间,或者叫内存空间,既然是占用了内存空间,那必然会有地址,既然是有地址,我们必然可以定义另一个指针来存放该指针变量的地址。所以可以称之为双重指针。
下面展示一些 内联代码片
。
#include<stdio.h>
#include<windows.h>
int main()
{
//指向指针的指针
int *p1 = NULL;
int a1 = 6;
p1 = &a1;
int **p2 = &p1;
printf("the value of address of a is %d\n",&a1);
printf("the value of p1 is %d\n",p1);
printf("the address of p1 is %d\n",&p1);
printf("the value of p2 is :%d\n",p2);
printf("the value of *p2 is :%d\n",*p2);
printf("the value of **p2 is %d\n",**p2);
printf("the value of address of p2 is %d\n",&p2);
system("pause");
}
指针占用空间?难道会同指向的类型变量的大小一直一致吗?
当然不是啦!
加一些代码,分别定义两个指针,一个指向charl类型,一个指向int类型,下面输出两者各占的字节数。
3:定义指针的*号与后面引用p取的 *号一样吗?
不一样啊!
int *p 这边的 *号是作为了区分指针与一般变量的符号,定义这边只说明了该变量是指针。而你在后面所用到的 *p我们可以认为是取该指针指向地址处得值得含义。上边的代码也有说明打印。所以这个也算作是一个容易混淆的区分点。
下面展示一些 内联代码片
。
#include<stdio.h>
#include<windows.h>
int main()
{
//指向指针的指针
char a ='c';
char *p =&a;
int *p1 = NULL;
int a1 = 6;
p1 = &a1;
int **p2 = &p1;
printf("the value of address of a is %d\n",&a1);
printf("the value of p1 is %d\n",p1);
printf("the address of p1 is %d\n",&p1);
printf("the value of p2 is :%d\n",p2);
printf("the value of *p2 is :%d\n",*p2);
printf("the value of **p2 is %d\n",**p2);
printf("the value of address of p2 is %d\n",&p2);
printf("the size of a is %d\n",sizeof(a));
printf("the size of a1 id %d\n",sizeof(a1));
printf("the size of p is %d\n",sizeof(p));
printf("the size of p1 is %d\n",sizeof(p1));
system("pause");
}
可以看到两个指针都占四个字节,虽然指向不同的类型。所以说指针所占的内存数和指向的数据类型是没有关系的。
二: 数组理解与操作
1:定义初始化
普通的一维数组也就没什么太大的区别了,要说区别话,也可能只是类型的问题。
(1)初始化一维数组
一写可以尝试的初始化,这里就举例数值型和字符型
#include<stdio.h>
#include<windows.h>
int main()
{
int a[] = {};
int a1[] = {1,2,3,4};
int a2[5] = {};
int a3[5] = {1,2,3,4,5};
char s1[] = {"Hello Everyone"};
char s2[] = {'a','b','c','\0'};
char s3[10] = {'a','b','\0'};
char s4[10] = {"Hello "};
//printf("%d",a[0]);
system("pause");
}
(2)初始化二维数组
我们还是以数值型和字符型举例
#include<stdio.h>
#include<windows.h>>
int main()
{
int array[3][4] =
{
{0,1,2,3},
{4,5,6,7},
{8,9,10,11}
};
int array1[][4] =
{
{1,2,3,4},
{5,6,7,8},
{9,10,11,12},
{13,14,15,16},
{17,18,19,20}
};
int array2[][5] =
{
};
system("pause");
char words[][4] =
{
{'a','b','c','\0'},
{'d','e','f','\0'}
};
char words_1[][6] =
{
"boy","hello","yes","right","what"
};
char words_2[][4] =
{
};
char words_3[3][4] =
{
{'a','b','c','\0'},
{'e','f','g','\0'},
{'i','j','k','\0'}
};
}
2:给数组赋值
谈到给数组赋值,在一些老版本说明中,比如一维数组是必须要有常量来规定初始化的数组的大小的,就算是二维数组也要至少指定列。但是c语言版本c99后好像是可以动态赋值了,意思是你可以定义一个n,然后int[n],n需要输入即可,但是在我的版本里面这是万万不行的。
1:给整形数组赋值
我们还是按照常规的方法给数组赋值,举一个给整型二维数组赋值的例子。
#include<stdio.h>
#include<windows.h>
int main()
{
int arr[3][4] = {0};
for(int i =0;i<3;i++){
for(int j =0;j<4;j++){
scanf("%d",&arr[i][j]);
}
}
for(int i =0;i<3;i++){
for(int j =0;j<4;j++){
printf("%d,",arr[i][j]);
}
}
system("pause");
}
赋值的时候一定要记得记得取地址,&,不加这个符号意味着你要改变地址,这怎么能让你随便改呢?
2:给字符型数组进行赋值
当然也可以通过函数赋值,也可以手动介入。
#include<stdio.h>
#include<windows.h>
int main()
{
// int arr[3][4] = {0};
// for(int i =0;i<3;i++){
// for(int j =0;j<4;j++){
// scanf("%d",&arr[i][j]);
// }
// }
// for(int i =0;i<3;i++){
// for(int j =0;j<4;j++){
// printf("%d,",arr[i][j]);
// }
// }
char name[10];
scanf("%s",name);
printf("the value is:[%s]",name);
system("pause");
}
这个是空格结束的
但是可以呢,如果输入的大于定义的长度呢?你看竟然也输出来了。???
还可以通过gets()函数
#include<stdio.h>
#include<windows.h>
int main()
{
char name[10];
gets(name);
printf("the value of name is %s",name);
system("pause");
}
fgets()函数,这个也是赋值
#include<stdio.h>
#include<windows.h>
int main()
{
char name[10];
//name 字符串 ,10 长度, stdin默认输入设备
fgets(name,10,stdin);
printf("the value of name is %s ",name);
system("pause");
}
当然还有其它的赋值方法,就不再举例了。
当然二维字符数组也可以赋值,道理是一样的,举例一个比较简单的赋值方法,你比如。
#include<stdio.h>
#include<windows.h>
int main()
{
char str[3][20];
strcpy(str[0],"abcccc")
strcpy(str[1],"asnksnaks");
strcpy(str[2],"shdjkhdkjdk");
printf("%s\n",str[0]);
system("pause");
}
这个还是很简单的。当然你也可以使用for循环进行赋值
那么举例一个使用for循环给二维数组赋值
#include<stdio.h>
#include<windows.h>
int main()
{
char str[3][20];
// strcpy(str[0],"abcccc");//或者用sprintf(str[0],"123");
// strcpy(str[1],"asnksnaks");//或者用sprintf(str[1],"456");
// strcpy(str[2],"shdjkhdkjdk");
// printf("%s\n",str[0]);
// system("pause");
for(int i =0;i<3;i++){
scanf("%s",&str[i]);
}
printf("%s",str[0]);
system("pause");
}
2:一维数组?二维数组?三维数组?
一维数组的化我们按照抽象出来的理解就是按照线性存储的方式罢了,二维的化也就是矩形,三维的化抽象出来也就是下面的这张图
什么?还有三维数组?
对啊,还有思维数组。不过只是未来理解,我们就讲到三维。
定义什么的就不需要赘述
其实你看啊,所谓的一维二维三维等等,只不过是抽象出来的概念。在内存中其实还是线性存放的。
就比如这样,下面一个二维数组。实际的存放方式是这样的。但是可能将其抽象化为矩形也是比较形象,不过我觉得,如果知道是线性的实际存放,在后面学习指针理解的化还是很有帮助的。
所以无论是多少维的数组,其在内存中的本质还是线性存放。
三: 指针与数组的复杂纠葛
1:指针与数组
指针可以配合数组干点什么事情呢?
我们定义的指针变量可以存放地址,那就可以存放数组的地址啊!
(1)指向一维数组
一个简单的运用
#include<stdio.h>
#include<windows.h>
int main()
{
char str[] = "I love you";
char *target = str;
int count = 0;
while (*target++ !='\0')
{
count++;
/* code */
// printf("%d",count);
}
printf("the total is :%d",count);
system("pause");
}
就拿这一个说明
我们这边的target指针是指向数组的,明白了说也就是数组的首地址,就是字符I的首地址,初始化是这样,当我们给指针进行++的时候就会依次指向第二个以至于往后。取*号就是取地址处的值,ok,说明白了。
根本还有要理解指针是怎样指向的,以及怎样指向数组,这样就不会被反复套娃。
(2)指向二维数组
你看指向二维数组,我们这边形象化一下,你再理解一下数组名代表了什么?
打印输出数组名就会得到数组的首地址,也就是第一个元素的值。
要想搞清楚,那就打印验证
#include<stdio.h>
#include<windows.h>
int main(){
int array[2][3] = {0};
for(int i =0;i<2;i++){
for(int j=0;j<3;j++){
scanf("%d",&array[i][j]);
}
}
printf("size of int is %d\n",sizeof(int));
printf("the add of array pointed is %d\n",array);//我们这边按照十进制将地址打印出来,是整个数组的首地址
printf("the value of *array pointed is %d\n",*array);//可以认为取到嵌套的一维数组的地址
printf("the value of **array is %d\n",**array);//可以认为取到存放一维数组的值
printf("the value of *(array+1) pointed is %d\n",*(array+1));
printf("the add of array+1 pointed is %d\n",array+1);
system("pause");
/*
输出打印后通过分析可得,array是指向五个包含元素的数组的指针
*/
}
/*
如何理解二维数组呢?二维数组根本还是在内存中按照一维数组存放的。可以认为是嵌套。
*/
写不动了,就不再举例了。我们来看搞脑子的有趣的知识点。
2:指针数组
你可能不怎么用,我承认对大一的同学们可能就离谱。什么?还有这玩意?
必要的时候没图就理解不了啊!小甲鱼的图图,我带来了。
为什么这就是一个指针数组呢?而不是数组指针?
[]的优先级别高于*,所以先结合p后结合*。
指针数组是一个数组,每个数组元素存放一个指针变量
可以干啥?可以这样做
#include<stdio.h>
#include<windows.h>
int main()
{ //下面这个是指针数组
char *p1[5] = {
"every one can dream!",
"i think i can successful",
"dont care it ,just do it for your dream",
"how are you",
"believe yourself"
};
for (int i =0;i<5;i++){
printf("%s\n",p1[i]);
}
system("pause");
}
你可能产生一种不可理解的意识,那就是字符串怎么能赋值给指针呢。但是并不是这个意思。
我们这样理解,声明了一个字符指针后,并用字符串常量的第一个字符的地址赋值给指针变量p1[i]。
3:数组指针
继续套娃
那么数组指针是什么?
可以看到p和*加了括号,所以会优先结合
数组指针就是指向数组的指针
来一段简单的代码
//下面演示数组指针,指向数组的指针,不要认为其指向地址,而是指向整个数组
#include<stdio.h>
#include<windows.h>
int main()
{
int temp[] ={1,2,3,4,5};
int (*p2)[5] = &temp;
int i;
for(i =0;i<5;i++)
{
printf("%d\n",*(*p2+i));
}
system("pause");
}
问题来了,既然p2是指针,那么*p就·代表了取值,为什么要取 * 呢?
p2是指向整个数组,我们可以这样理解,进行一层 * 可以认为其取到数组第一个元素的首地址,再次*可认为取值。ok。
四:给你一些相关的内容以及遇到的问题
套娃
给几个代码
/*使用指针的方法将最大值保存到变量a中去,最小值保存到变量b中去*/
#include <stdio.h>
void ff(int *p1, int *p2) {//把实参的地址内容传递过去赋值的过程
int a;//和main函数中的a不一样的
a = *p1;//将p1地址上的值传给a;
// printf("%d", &a);
*p1 = *p2;
*p2 = a;
}
int main() {
int a, b;
int *p_1, *p_2; //定义指针型变量指向int类型的变量
scanf("%d %d", &a, &b);
p_1 = &a; //将地址附值给p_1,p_2;
p_2 = &b;
printf("%d,%d\n", &a, &b);
if (a < b) {
ff(p_1, p_2);
}
printf("最大值为%d,最小值为%d", a, b);
return 0;
}
#include<stdio.h>
#include<windows.h>
int main()
{ //初始化两个指针开始指向空
int *p1 =NULL;
int *p2 =NULL;
int *p3 =NULL;
int a =6,b =8,c =10;
p1 =&a;//代表p1指针指向a的地址
p2 =&b;
p3 =&c;
//你可以先打印*p1/*p2代表了什么,其实这次再取*的话,和指针定义的*不一样,代表取取指针指向地址处的内容
// eg:
printf("the value of a is %d\n",*p1);
printf("the address of a is %p\n",p1);//打印地址直接就%p
//交换值可以取值直接交换
// eg:
*p1 = *p2;
printf("this time ,the value of a is %d\n",a);
//?试试地址交换?
p1 = p3;
printf("the value of a is %d\n",a);//打印输出你只会发现值交换才是有效的,用指针只是指针的指向发生了改变
printf("the value of pointer p1 is %d",*p1);
system("pause");
}
#include<stdio.h>
#include<windows.h>
int main(){
int array[2][3] = {0};
for(int i =0;i<2;i++){
for(int j=0;j<3;j++){
scanf("%d",&array[i][j]);
}
}
printf("size of int is %d\n",sizeof(int));
printf("the add of array pointed is %d\n",array);//我们这边按照十进制将地址打印出来,是整个数组的首地址
printf("the value of *array pointed is %d\n",*array);//可以认为取到嵌套的一维数组的地址
printf("the value of **array is %d\n",**array);//可以认为取到存放一维数组的值
printf("the value of *(array+1) pointed is %d\n",*(array+1));
printf("the add of array+1 pointed is %d\n",array+1);
system("pause");
/*
输出打印后通过分析可得,array是指向五个包含元素的数组的指针
*/
}
/*
如何理解二维数组呢?二维数组根本还是在内存中按照一维数组存放的。可以认为是嵌套。
*/
#include<stdio.h>
#include<windows.h>
//指针数组
int main()
{
char *cBooks[] =
{
"Cease to struggle and you cease to live",
"Time ca heal a broken heart,but it can also break a waiting heart",
"the fox changes his skin but not his habbits",
"Time is but a river flowing from our past"
};
char **jgdabc;
char **jgdabc_loves[4];
jgdabc_loves[0] = &cBooks[0];
jgdabc_loves[1] = &cBooks[1];
jgdabc_loves[2] = &cBooks[2];
jgdabc_loves[3] = &cBooks[3];
printf("%d\n",jgdabc_loves[0]);
printf("%d\n",*jgdabc_loves);
printf("%s\n",*jgdabc_loves[0]);
printf("%c\n",**jgdabc_loves[0]);
printf("%c\n",**jgdabc_loves[1]);
system("pause");
}
这边请自行运行并思考,专注指针的要点,以及指向指针的·指针的含义。
原创不易,未经许可禁止抄袭转载。
–点击访问主页jgdabc