前言:本笔记记录于初学者在B站翁恺C语言P82-P98学习所得笔记.指针这一板块的内容的确比较抽象,初学者难以理解.但若真正学懂了指针,对后续C语言内容学习会相对容易的多.
目录
取地址符--&
首先,何为地址?
地址就是可以唯一标识某一点的一个编号,即一个数字.习惯上,在计算机里地址我们常常用其对应的十六进制数来表示。
注:
"%x"与"%X"用于输出16进制数(大小写不同),但也用于输出地址(不会可以填满16位).
用%p输出地址时,若16位未满,会用0填满左侧.
在我们的C程序中,每一个定义的变量,在内存中都占有一个内存单元,比如int类型占四个字节,char类型占一个字节等等,每个字节都在0~4,294,967,295(32位操作系统)之间都有一个对应的编号,C语言允许在程序中使用变量的地址,并可以通过地址运算符"&"得到变量的地址.
指针
1.一些基本定义
地址是内存上的编号,而指针虽然也表示一个编号,也是一个地址,但两者性质却不相同.一个代表了常量,另一个则是变量。就好比内存是一把尺子,而指针就是尺子上面的游标,可以左右移动,它某一个时刻是指向一个地方的,这就是指针变量.
对指针变量定义的一般形式为:类型说明符 + * + 变量名.
int i;
int *p = i;
其中,这里的*与前面的类型说明符共同说明这是一个指针变量,类型说明符表示该指针变量所指向的变量为何种数据类型(例:int *),变量名即为定义的指针变量名(例:p).
int *p,q;
若这样写,只有p是指针,q不是(无" * ").
如下图,i地址位于0x2000,指针p的值为2000.
记为p指向i,p表示的值为i这个变量的地址.
2.指针的用处
指针也可以作为函数的参数。操作时可以直接将指针变量声明在函数的小括号内即可。
#include <stdio.h>
void f(int *p);
void g(int k);i
nt main(void)
{
int i = 6;
printf("&i=%pln",&i);
f(&i);
g(i);
return 0;
}
void f(int *p)
{
printf(" p=%p\n", p);
}
void g(int k)
{
printf("k%d\n",k);
}
如图所示,因为将&i代入函数f中,所以函数f中的p表示i的地址70,而函数g中仅仅代入了i,所以函数g中的k表示i对应的数值6.
3.*与&
互相反作用
*&yptr ->*(&yptr) _->* (yp1tr的地址)->得到那个地址上的变量-> yptr
&*yptr -> &(*yptr) ->&(y)->得到y的地址,也就是yptr -> yptr
指针的应用
1.找数组的max与min
当我们调用一个函数的时候,如果函数的参数是一个指针变量,那么我们就必须要为这个指针传递一个和指针类型相同的普通变量的地址。
我们知道,一般普通变量作为形参的函数,在函数操作结束后,如果不返回结果值的话,函数内部的形参变量会被系统收回,不会改变实参的值。
但是,对于指针变量作为参数的函数,在函数的内部去访问参数指针指向的变量的时候,其实访问的就是实参变量。
所以这样做就可以做到在函数的内部修改实参变量的值。
那么什么时候要使用指针变量作为函数的参数呢?
我们知道,在C语言中,一个函数只能返回一个值,不能同时返回多个数据。
那么我们怎么做可以做到返回多个值呢?这个时候可以使用指针变量。
使用指针作为函数的参数,让调用者将自己的变量的地址传递到函数的内部,函数的内部通过指针就可以修改实参变量的值,达到返回多个值的效果。
例:找数组的max与min
void getMaxandMin(int arr[],int len,int* pMax,int* pMin)
{
int max = INT32_MIN;
int min = INT32_MAX;
for(int i = 0;i < 0;i++)
{
if(arr[i] > max)
{
max = arr[i];
}
if(arr[i] < min)
{
min = arr[i];
}
}
*pMax = max;
*pMin = min;
}
int main()
{
int arr[] = {20,56,23,41,14,74,89,100,120,235,41};
int max = 0;
int min = 0;
int len = sizeof(arr)/sizeof(arr[0]);
getMaxandMin(arr,len,&max,&min);
printf(“Max = %d\n Min = %d\n”,max,min);
}
例:交换变量
#include<stdio.h>
void swap (int *pa ,int *pb);
int main()
{
int a=5;
int b=6;
int *p;
*p=0;
swap(&a,&b);
printf("a=%d,b=%d",a,b);
return 0;
}
void swap (int *pa,int *pb)
{
int t=*pa;
*pa=*pb;
*pb=t;
}
2.指针使用时常见的一个错误
未初始化变量,只定义了指针变量,就开始使用指针
例:int p;
int *p = 3;
此时指针会指向不该去之地,程序则会停止。
指针与数组
1.指针指向数组的元素
我们可以声明一个指针变量指向数组的元素,通过指针间接的操作数组的元素。
int arr[] = {10,20,30,40,50,60,70};
int* p1 = &arr[0];//表示p1指针指向了数组中的第0个元素
数组的第0个元素的地址就是数组的地址,数组名就代表数组的地址,所以也可以这样定义:
int* p1 = arr;
最后请注意:数组名代表数组的地址,而数组一旦创建,数组的地址就确定了,所以我们不能为数组名赋值,也不能修改数组名的值,但是可以利用数组名的值。
指针与const
1.让指针指向一个常量对象,这样可以防止使用该指针来修改所指向的值。
即指向的内容固定,这个指针可以指向其他的地方,但是不能改变所指向的内容
例子:
int a = 1;
const int *p = &a;(初始化指针时别忘了取地址符)
int i = 1;
int j = 2;
const int *p = &i;
//int const *p = &i;
*p = 3;//错误,常量指针不可以修改指向地址的内容
p = &j;//正确,常量指针可以修改指向地址
a可以改变,p也可以改变;但是不能通过p改变a,即*p不能改变
这种指针可以指向常量对象,也可以指向不是常量的对象。但是const对象只能使用这种指向常量的指针;
将指针参数声明为指向常量数据的指针有两个优点
(1)使用const可以保护数据,避免由于无意间修改数据而导致的错误
(2)使用const使得函数能够处理const和非const实参,否则只能接收非const数据
所以如果条件允许,应该将指针形参声明为指向const的指针
2.将指针本身声明为一个常量,这样可以防止改变指针指向的位置,但是可以通过指针来改变内容的值
例子:
int a = 1;
int * const p = &a;
这里就是p不能改变,只能指向a的地址,但是a可以改变
3.两者的组合
const int* const p = &i;
1
作用:不可更改指向地址的内容,也不可修改指向地址。
int i = 1;
int j =2;
const int* const p = &i;
*p =3; //错误
p = &j; //错误
4.常量与指针
只有常量指针可以指向常量,而普通指针、指针常量不行。常量指针不可以修改常量的值。
const int i = 1;
const int* pt = &i; //正确,常量指针可以指向常量
int* pi = &i; //错误,普通指针不可以指向常量。
int* const pr = &i; //错误,指针常量不可以指向常量。
指针的运算
1,简介
指针变量保存的是地址,地址表面上是一个十六进制数,本质上是一串二进制数,
既然地址是数字,当然可以进行数学运算了。
2.运算原理
我们可以对指针进行四种算术运算:++、--、+、-。(不能乘除)
假设 p 是一个指向地址 0x1000 的整型指针,是一个 32 位的整数,让我们对该指针执行下列的算术运算:
p++;
在执行完上述的运算之后,p 将指向位置 0x1004,因为 p 每增加一次,它都将指向下一个整数位置,即当前位置往后移 4 字节。
3.步长
为了方便,我们引入步长的概念,步长等于指针所指向的变量的内存大小,步长代表着指针每走一步所跳过的字节数。
基本类型
sizeof(int) 4
sizeof(short) 2
sizeof (unsigned) 4
sizeof (char) 1
sizeof(f1oat) 4
sizeof (doub1e) 8
指针类型
sizeof(int*) 4
sizeof(short*) 4
sizeof(unsigned*)4
sizeof (char*) 4
sizeof(fioat*) 4
sizeof(double*) 4
3.指针的比较
指针可以用关系运算符进行比较,如 ==、< 和 >。如果 p1 和 p2 指向两个相关的变量,比如同一个数组中的不同元素,则可对 p1 和 p2 进行大小比较。