乐至建设局网站,广州大石附近做网站的公司哪家好,qq登录网站怎么做,中小型网站建设教程指针作为C语言中的一个部分#xff0c;可以说指针是C语言的核心#xff0c;那么它的难度肯定是不言而喻的#xff0c;总是能把人给绕得找不到方向。 今天我就好好的说一说指针这个东西。 1、何为指针#xff1f; 指针是C语言中用来存放地址的一个变量类型。我们可以将指针看… 指针作为C语言中的一个部分可以说指针是C语言的核心那么它的难度肯定是不言而喻的总是能把人给绕得找不到方向。 今天我就好好的说一说指针这个东西。 1、何为指针 指针是C语言中用来存放地址的一个变量类型。我们可以将指针看作独特的一种变量只用来存放地址。根据指针存放不同类型变量的地址也可以将指针分为int型指针char型指针等等。由于指针都是存放地址而所有类型的地址又没有本质上的差异所以无论任何类型的指针大小都是同样的。同为4个字节或者8个字节。 指针是 C 语言中的一种数据类型可以用来存储内存地址。每个变量或对象都占据着计算机内存中的某个地址指针变量可以保存这些地址并允许对这些变量或对象进行间接访问。 2、一级指针 想要了解指针咱就得从最基础的一级指针开始。 1、指针的声明 在 C 语言中指针变量声明时需要指定所指向的数据类型以便编译器能够在使用指针时自动对其进行类型转换和内存管理等操作。 例如以下为一个整型指针变量的定义
int a 10;
int* pa a; *即代表这个pa是一个int型指针指向的是a的地址。 注意声明指针之后必须对其进行初始化否则它就是一个空指针不能直接使用它对空指针进行解引用操作系统可能会崩溃后果非常严重 2、通过指针对变量或对象进行访问
int a 10;
int* pa a;
*pa 20; 这里我们解引用pa然后将pa的值赋值为20。对pa解引用后就相当于a。因为pa是a的地址解引用a的地址那就是找到了a 这里引出一个问题
int a 10, b 20;
int* pa a, * pb b;
pa pb; 此时a的值是20吗相信大部分对指针不了解的人都会认为此时a已经变成了20因为将b的地址pb赋值给了pa。但是结果是a仍然是10。首先pa存的确实是a的地址然后我们确实是对pa存放的地址进行了改变。但是a是apa是pa在这里改变pa对a有影响吗没有
a的地址并没有发生改变所以a仍然是10。但是如果我们解引用pa后会发现此时*pa的值是20因为我们改变的是pa存放的地址。 既然讲到这那我们就得提出以下指针的运算了。指针没有复杂的运算不能指针间进行运算。 3、指针的运算 赋值运算相同类型的指针才能赋值。 算术运算--- 这里的和-指的是-多少即对地址的运算指向前面的地址或者后面的地址。并不是说简单的papb其实指针间是可以进行运算的我们后面再谈论关系运算 !, ,,,。
int a[10];
int* p1 a[2];
int* p2 a[5];
printf(%d, p2 - p1);//answer 3 answer 3why 这里先不说后面再说。 1、算术运算 a *p ----结果为a *p ,p。 a (*p) ---结果为 : a *p,*p。 2、关系运算 若p1和p2指向同一数组则 p1p2 表示p1指的元素在前 。 p1p2 表示p1指的元素在后。 p1p2 表示p1与p2指向同一元素 。 若p1与p2不指向同一数组则比较无意义。 4、指针与函数 先来个最简单的交换函数Swap。 1、交换函数
void Swap(int x, int y)
{int c x;x y;y c;
}
int main()
{int a 10, b 20;printf( a %d,b %d, a, b);Swap(a, b);printf( a %d,b %d, a, b);return 0;
} 这个函数会起到交换ab的值的作用吗答案是不会这个函数我们在传参的时候只是将ab的值传过去了但没有影响地址。在Swap函数中创建临时变量来交换xy的值但是原来ab的地址并没有改变。而地址才是决定一个变量的值。故ab没有改变。 所以应该用地址即指针来修改二者的值才能改变ab的地址进而改变ab的值。
void Swap(int* x, int* y)
{int c *x;*x *y;*y c;
}
int main()
{int a 10, b 20;printf( a %d,b %d, a, b);Swap(a, b);printf( a %d,b %d, a, b);return 0;
} 因此函数参数为指针的时候可以改变实参的值。 3、指针与数组名的关系 1、数组名 对于数组名在大部分情况下数组名都代表首元素的地址。一维数组的数组名代表第一个元素的地址二维数组的数组名代表第一行元素的地址即二维数组的数组名代表的是一个一维数组的地址。那么数组名就可以看作是一个指针。 2、指针与数组访问元素的等价关系 既然说了数组名就是指针那么用数组名可以访问元素用指针也可以访问元素了。
int arr[5] { 1,2,3,4,5 };
int* parr arr;
printf(%d , *(parr 1)); // 2
printf(%d , *(arr 1)); // 2 由此可以看出来对于一维数组*(parri) 等价于arr[i]。同理对于二维数组不同的是对于二维数组需要用二级指针*(*(parri)j)就等价于arr[i][j]。为什么呢对于二维数组*(parri)得到的是第i-1行的数组而数组名又是一个首元素的地址那么*(parri)得到的就是第i-1行的首元素地址至此j再解引用就和一维数组一样了。但是如果传参二维数组名并不是以二级指针接收。二维数组名代表首行数组的地址接收数组的地址需要用数组指针接收。下面再细讲。 3、字符数组和字符指针的区别 1、字符数组
char str1[10] abcdef;
char str2[10];
str2 abcd;//错误 虽然数组名代表首元素地址“abcd”也代表首元素a的地址但是这样赋值给数组是错误的我们只能通过拷贝来赋值给数组
char str2[10];
strcpy(str2, abcd); 对于此类赋值我们是可以通过覆盖等操作来修改数组值的。下面将介绍另一种赋值法这种赋值法是不能修改数组的值的。 2、字符指针 char* str;str abcde; 通过指针来给字符数组赋值这样赋值的时候“abcde”叫做字符串常量字符串常量是不能修改的。则此时str是不能修改它的内容的。 4、数组指针 1、数组指针的定义 数组指针就是一个指向数组的指针这里的指针代表的是整个数组的地址并非数组首元素的地址。注意区分。 2、数组指针的表示
int arr[3] { 1,2,3 };
int(*p)[3] arr; 我们来解析一下数组指针p代表指针名称*p代表p是一个指针[ 10 ]代表这个指针指向一个拥有十个元素的数组int代表这个数组是int类型的。 注意 这里需要用括号将*p结合起来因为[ ]的优先级比*的优先级高。如果不用括号结合的话这其实是一个指针数组。 接下来我们将用代码来分析数组指针和数组名的差别 int arr[3] { 1,2,3 };int(*p)[3] arr;printf(p %p\n, p);printf(arr %p\n, arr);printf(p 1 %p\n, p 1);printf(arr 1 %p\n, arr 1); 可知p和arr的地址都是一样的都是指向数组的起始位置的地址。 但是p1和arr1的地址就不一样了发现p1比p的地址增加了12个字节即三个地址的大小。而arr1比arr只增加了4个字节即一个地址的大小。 由此可以看出数组指针代表整个数组的地址数组名只代表数组首元素的地址。 3、数组指针与二维数组传参 通过上面我们知道二维数组名代表首元素地址这里的首元素地址代表二维数组的第一行元素的地址即看作一个一维数组的地址。既然如此一维数组名传参我们需要用一级指针来接收地址。那么二维数组名传参我们是否需要用数组指针来接收一维数组的地址呢答案是是的要用数组指针。
void fun(int(*arr)[3], int row, int col)
{for (int i 0; i row; i){for (int j 0; j col; j){printf(%d , arr[i][j]);//或者*(*(arri)j)}printf(\n);}
}
int main()
{int arr[2][3] { {1,2,3},{4,5,6} };fun(arr, 2, 3);return 0;
} 注意这里新参的[ ]里边必须是那个一维数组的元素个数否则打印输出的时候就不会正常打印。如果小于元素个数它会打印你第二行的元素因为二维数组在内存中其实是线性连续的。如果大于数组个数它会乱输出空间中数组末尾后边的数据 但是对于二维的字符数组可能每一个一维数组的字符数是不同的那么这里使用数组指针接收[ ]里面就不知道写多少。那么对于这种情况我们需要用到二级指针但是这里的二维数组我们需要动态开辟不能直接自己简单定义二维数组。 5、二级指针 二级指针显然就是接收二维数组名的指针但是这里并不是简单的直接传入二维数组名这里二维数组名需要动态开辟。 1、二级指针与动态开辟字符二维数组 一般使用二级指针是为了来应对二维字符数组的每个一维数组元素个数不同的情况。普通情况下二维数组还是用数组指针来接收。
void fun(char** str, int row)
{for (int i 0; i row; i){printf(%s\n, str[i]);}
}
int main()
{char** str (char**)malloc(sizeof(char*) * 3);//开辟一个二维字符数组一共有三个字符串for (int i 0; i 3; i){str[i] (char*)malloc(sizeof(char) * 10);//给每个字符串开辟空间scanf(%s, str[i]);//输入}fun(str, 3);return 0;
} 6、其他指针类型 1、函数指针 函数指针顾名思义就是存放函数地址的指针咯。那么问题来了函数也有地址吗 函数当然有地址不然在调用函数的时候在内存中哪里去寻找这个函数呢 1、函数名 首先来说一下函数名地址
int Add(int x, int y)
{return x y;
}
int main()
{printf(%p\n, Add);printf(%p\n, Add);return 0;
} 由此我们可以看出对于函数名无论是对它取地址还是直接是函数名都代表的这个函数的地址。 2、函数指针的定义
int Add(int x, int y)
{return x y;
}
int main()
{int* p1 Add;printf(%p\n,p1);int (*p2)(int, int) Add;printf(%p\n, p2);return 0;
} 对于如上两种定义虽然二者打印结果相同但是第一种定义是错的。在好的编译器下会发出警告“初始化”:“int *”与“int (__cdecl *)(int,int)”的间接级别不同。这个警告就说明这种定义函数指针的方法其实是错的二者并不相同。 所以正确的函数指针定义方法应该是第二种 int (*p2)(int, int) Add; 3、解析函数指针 int (*p2)(int, int) Add; p2指针名称*p2代表是一个指针类型前面的int代表函数的返回值是intintint代表这个函数的两个参数是int和int类型的。 4、调用函数指针
int Add(int x, int y)
{return x y;
}
int main()
{int (*p2)(int, int) Add;int ret p2(1, 2);printf(%d, ret);return 0;
} 我们可以直接运用函数指针来执行该函数的功能虽然多此一举哈哈哈哈哈。 那有人会问了为什么这里p2没有解引用就可以使用前面讲了函数名取地址和不取地址的结果都是一样的那么解引用和不解引用结果也是一样的。 2、其他 对于指针我们其实可以引出很多不同种类的指针越往后指针越头大我们只需要掌握这些常用的就行了。 6、总结 写了这么久都给我写累了但是指针的奥妙远不止于此还有很多细节需要自己去琢磨。哎路漫漫其修远兮~