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函数根据传进去的地址直接操作内存,改变内存中的值,完成了对变量的赋值。

posted @ 2021-02-25 22:14  初晨~  阅读(297)  评论(0编辑  收藏  举报