C语言 -- 指针
一、变量的地址
在C语言中,每定义一个变量,系统就会给变量分配一块内存,比如,定义一个 int num = 10; ,这样就会在计算机内存中开辟一块内存,来存放这个10,而这一小块内存是有地址的,我们可以通过这个地址,来找到里边的内容。如果把计算机的内存区域比喻成一个学校,那么每块内存的地址就像教室的门牌号。
C语言采用运算符&来获取变量的地址。如下:
int main()
{
int num1 = 10;
char english = 'a';
double price = 100.00;
printf("%p\n", &num1);
printf("%p\n", &english);
printf("%p\n", &price);
system("pause");
}
结果:
在printf函数中,输出内存地址的格式控制符是%p,地址采用十六进制的数字显示。
二、指针
指针是一种特别变量,全称是指针变量,专用于存放其它变量在内存中的地址编号,这样的一份数据可以是数组、字符串、函数,也可以是另外的一个普通变量或指针变量。指针在使用之前要先声明。
现在假设有一个 char 类型的变量 c,它存储了字符 'K'(ASCII码为十进制数 75),并占用了地址为 0X11A 的内存(地址通常用十六进制表示)。另外有一个指针变量 p,它存储了 0X11A,正好等于变量 c 的地址,这种情况我们就称 p 指向了 c,或者说 p 是指向变量 c 的指针。
定义指针变量的语法是:
datatype *varname;
或者
datatype *name = value;
“*”表示这是一个指针变量,datatype
表示该指针变量所指向的数据的类型 。例如:
int *count;
count 是一个指向 int 类型数据的指针变量,至于 count 究竟指向哪一份数据,应该由赋予它的值决定。再如:
int num = 100; count = &num //将num的地址给count
将变量num的地址赋予给count,此时 count就指向了 num。取地址用的就是 &
注意:不管是整型、浮点型、字符型,还是其他的数据类型的内存变量,它的地址都是一个十六进制数,可以理解为内存单元的编号。我们用整数型指针存放整数型变量的地址;用字符型指针存放字符型变量的地址;用双精度型指针存放双精度型变量的地址,用自定义数据类型指针存放自定义数据类型变量的地址。
如下:
int main()
{
int num1 = 10;
char english = 'a';
double price = 100.00;
int *num2;
num2 = &num1;
char *enlist2;
enlist2 = &english;
double *price2;
price2 = &price;
}
定义指针变量时必须带*
,给指针变量赋值时不能带*,如下
:
int main()
{
int num1 = 10;
char english = 'a';
double price = 100.00;
int *num2 = &num1;
char *enlist2 = &english;
double *price2 = &price;
}
三、通过指针操作内存变量
定义了指针变量,并指向了内存变量的地址,就可以通过指针来操作内存变量(在指针前加星号 *),效果与使用变量名相同。
格式:
*pointer;
要取得指针所指向内存中的值,需要用*号,*
称为指针运算符,用来取得某个地址上的数据,加上指针变量名称即可,如下:
int main()
{
int num1 = 10;
int *num2 = &num1;
printf("%d\n",*num2);
printf("%d\n",num1);
}
结果:
假设 num1的地址是 0X1000,num2 指向 num1 后,num2本身的值也会变为 0X1000,*num2表示获取地址 0X1000 上的数据,也即变量 num1 的值。从运行结果看,*num2和num1 是等价的。
CPU 读写数据必须要知道数据在内存中的地址,普通变量和指针变量都是地址的助记符,虽然通过 *num2 和 num1 获取到的数据一样,但它们的运行过程稍有不同:num1 只需要一次运算就能够取得数据,而 *num2 要经过两次运算,多了一层“间接”,因为*num2要先找到地址,然后取里边的值,而num1是直接取值。
假设变量 num1、num2 的地址分别为 0X1000、0XC100,它们的指向关系如下图所示:
程序被编译和链接后,num1、num1 被替换成相应的地址。使用 *num2 的话,要先通过地址 0XC100 取得变量 num2本身的值,这个值是变量 num1 的地址,然后再通过这个值取得变量num1 的数据,前后共有两次运算;而使用 num1 的话,可以通过地址 0X1000 直接取得它的数据,只需要一步运算。
也就是说,使用指针是间接获取数据,使用变量名是直接获取数据,前者比后者的代价要高。
指针除了可以获取内存上的数据,也可以修改内存上的数据,例如:
int main()
{
int num1 = 10;
int *num2 = &num1;
*num2 = 20;
printf("%d\n", *num2);
printf("%d\n", num1);
}
四、再来讨论函数的参数传递
平时在函数调用时候,一般都会传入一个普通的值,然后在函数里进行一些操作,比如传入一个整形的id,通过这个id查找对应的信息,如果我们想通过一个函数,来改变主函数的内容,就需要把地址当作参数传递,在函数里边通过地址,修改里边的内容,比如:
void updateVal(int *num)
{
*num = 20;
}
int main()
{
int num1 = 10;
updateVal(&num1);
printf("%d\n", num1);
}
主程序把变量num1的地址传递给函数updateVal,updateVal函数的参数num是一个指针,接存放变量num1的地址。在函数updateVal中,根据指针中的地址直接操作内存,从而修改了主程序中变量num1的值。
我们已经使用scanf函数很多次了,调用scanf函数的时候,需要在变量前面加符号&,其实就是把变量的地址传给scanf函数,scanf函数根据传进去的地址直接操作内存,改变内存中的值,完成了对变量的赋值。