C语言自学《七》---- 指针

指针初探

指针是C语言中最强大的工具之一
可以存储地址的变量称为指针(pointers)
存储在指针中的地址通常是另一个变量
首先看看变量在内存中是如何存储和访问的

代码示例:

<!-- lang: cpp -->

//在程序中使用变量名引用这块内存
//但是一旦编译执行程序,计算机就使用内存位置的地址来引用它(其中存储了变量值)
//这条语句会分配一块内存来存储一个整数,使用`number`名称可以访问这个整数
//值5存储在这个区域中,计算机用一个地址引用这个区域
//存储这个数据的地址取决于所使用的计算机、操作系统和编译器
//这个地址在不同的系统上是不同的
int number = 5;
//指针pnumber包含另一个变量number的地址
//变量number是一个值为5的整数变量
//存储在pnumber中的地址是number第一个字节的地址
//&是寻址运算符
int *pnumber = &number;

char类型的指针指向占有1个字节的值,long类型值的指针指向占有4个字节的值
每个指针都合某个变量类型相关联,也只能用于指向该类型的变量
所以如果指针的类型是int,它就只能指向int类型的变量
一般给定类型的指针写成type*,其中type是任意给定的类型
类型名void表示没有指定类型,所以void*类型的指针可以包含任意类型的数据项地址
void*通常用于参数和返回值类型,任意类型的指针都可以传送为void*类型的值,在使用它时,再转换为合适的类型

声明和初始化指针

代码示例:

<!-- lang: cpp -->

int *pnumber;    //它可以存储任意int类型的变量地址
int* pnumber;    //与上一句语句完全相同,要确保从一开始就使用一种写法

//没有初始化的指针是非常危险的,比没有初始化的变量要危险,所以要初始化它,哪怕它不指向任何对象
//NULL是在标准库中定义的一个常量,对于指针它表示0,NULL是一个不指向任何内容的内存位置的值
int *pnumber = NULL;

//可以用相同的语句声明一般的变量和指针
//其中只有pVal是指针
double value, *pVal, fnum;

通过指针访问值

*称为取消运算符它用于取消对指针的引用

<!-- lang: cpp -->

int number = 15;
int *pointer = &number;
int result = 0;
//由于pointer变量含有number变量的地址,所以可以在下面表达式中计算result的值
//*pointer等于存储在pointer中的地址的值,就是存储在number中的值15
result = *pointer + 5;    //计算结果:20

输出变量和指针的地址和包含的值

<!-- lang: cpp -->

#include <stdio.h>

int main(void){

    int number = 0;
    int *pnumber = NULL;

    number = 10;
    //%p输出十六进制的内存地址
    printf("number的地址为:%p\n", &number);
    printf("number的字节大小为:%zd 字节\n", sizeof(number));
    printf("number的值为:%d\n\n", number);

    //使用&寻址运算符获取变量number的地址,将该地址存储到pnumber指针中
    //pnumber只能存储地址
    pnumber = &number;

    //输出pnumber所占的内存位置的第一个字节        
    //指针本身也有一个地址
    //由于%说明符需要某种指针类型的值,但&pnumber的类型是指向int指针的指针
    //所以使用(void*)来进行转换,使它可以包含任何类型,防止编译器发出警告
    printf("pnumber的地址为:%p\n", (void*)&pnumber);
    //输出所占的字节数
    printf("pnumber的字节大小为:%zd 字节\n", sizeof(pnumber));
    //pnumber所存储的内存地址,就是&number
    printf("pnumber保存的地址值为:%p\n", pnumber);
    //在pnumber所含的地址中存储的值,就是存储在number中的值,使用*来取消对指针的引用
    printf("指针的值为: %d\n", *pnumber);
    return 0;
}

输出结果:
number的地址为:0x7fff5420dbd8
number的字节大小为:4 字节
number的值为:10

pnumber的地址为:0x7fff5420dbd0
pnumber的字节大小为:8 字节
pnumber保存的地址值为:0x7fff5420dbd8
指针的值为: 10

使用指针

使用指针修改存储在其他变量中的值,并进行运算:

<!-- lang: cpp -->

int main(void){

    long num1 = 0L;
    long num2 = 0L;
    long *pnum = NULL;

    pnum = &num1;
    //下面的语句修改了num1的值为2L
    *pnum = 2L;
    ++num2;
    //下面等于num2 = num2 + *pnum
    //num2 = 1L + 2L = 3L
    num2 += *pnum;
    //把指针存储的地址改为num2的地址
    pnum = &num2;
    //下面这句自增语句,修改了num2的值,现在为4L
    ++*pnum;

    //计算表达式*pnum + num2 = 4L + 4L = 8L
    printf("num1=%ld num2=%ld *pnum=%ld *pnum+num2=%ld\n",num1,num2,*pnum,*pnum+num2);

    return 0;
}

接受输入时使用指针:

<!-- lang: cpp -->

#include <stdio.h>

int main(void) {

    int value = 0;
    //把value的地址存储到pvalue中
    int *pvalue = &value;
    //使用指针value将value的地址传递给scant()
    //pvalue和&value是相同的,所以可以用任何一个
    printf("You entered %d.\n", value);
    scanf(" %d", pvalue);

    printf("You entered %d.\n", value);
    return 0;
}

指向常量的指针

可以使用const关键字指定常量指针,该指针指向的值不能改变
虽然指针在设置为常量后,不能改变其值,但可以改变指针指向的变量的值

代码示例:

<!-- lang: cpp -->

long value = 999L;
const long *pvalue = &value;

//下面这句是错的,因为pvalue指针是常量,无法修改
//*pvalue = 888L;
//但可以通过修改指针所指向的变量的值
value = 777L;
//也可以改变指针所指向的变量地址,就是不能通过指针改变常量指针的值
long number = 777L;
pvalue = &number;

常量指针

常量指针可以使指针中存储的地址不能改变

代码示例:

<!-- lang: cpp -->

int count = 43;
//这句语句声明并初始化了pcount,指定该指针存储的地址不能改变   
int *const pcount = &count;
//下面的语句是错误的
//int item = 34;
//pcount = &item;
//但使用pcount,仍可以改变count指向的值
*pcount = 345;
//还可以创建一个常量指针,它指向一个常量值
int item = 25;
//pitem是一个指向常量的常量指针,所以所有的信息都是固定不变的
//不能改变存储在pitem中的地址,也不能使用pitem改变它指向的内容
//但仍可以直接修改item的值,如果希望所有的信息都固定不变,可以把item指定为const
const int *const pitem = &item;

数组和指针

数组时相同类型的对象集合,可以用一个名称引用,例如:数组scores[50]
指针式一个变量,它的值是给定类型的另一个变量或常量的地址
使用指针可以在不同的时间访问不同的变量,只要它们的类型相同即可
数组与指针的区别在于:数组可以改变指针包含的地址,但不能改变数组名称引用的地址
通过下面的示例就可以看出区别所在

代码示例1:

<!-- lang: cpp -->

/*
 &multiple[0]会产生和multiple表达式相同的值
 因为multiple等于数组第一个字节的地址
 &multiple[0]等于数组第一个元素的字节
 所以它们会相同
 */
int main(){

    char multiple[] = "My string";

    char *p = &multiple[0];
    printf("The address of the first array element:%p\n", p);

    p = multiple;
    printf("The address obtained from the array name:%p\n", multiple);
    return 0;
}

代码示例2:

<!-- lang: cpp -->

#include <stdio.h>
#include <string.h>

/*
 输出:
 multiple[0] = a * (p+0) = a &multiple[0] = 0x7fff5ebdebc3 p+0 = 0x7fff5ebdebc3
 multiple[1] =   * (p+1) =   &multiple[1] = 0x7fff5ebdebc4 p+1 = 0x7fff5ebdebc4
 multiple[2] = s * (p+2) = s &multiple[2] = 0x7fff5ebdebc5 p+2 = 0x7fff5ebdebc5
 multiple[3] = t * (p+3) = t &multiple[3] = 0x7fff5ebdebc6 p+3 = 0x7fff5ebdebc6
 multiple[4] = r * (p+4) = r &multiple[4] = 0x7fff5ebdebc7 p+4 = 0x7fff5ebdebc7
 multiple[5] = i * (p+5) = i &multiple[5] = 0x7fff5ebdebc8 p+5 = 0x7fff5ebdebc8
 multiple[6] = n * (p+6) = n &multiple[6] = 0x7fff5ebdebc9 p+6 = 0x7fff5ebdebc9
 multiple[7] = g * (p+7) = g &multiple[7] = 0x7fff5ebdebca p+7 = 0x7fff5ebdebca

 p+n就等于multiple+n,所以multiple[n]与*(multiple+n)是相同的
 对于元素占用一个字节的数组来说,上面表达式成立
 *(p+n)是给p中的地址加上整数n,在对得到的地址取消引用,就可以得到对应的元素值
 */
int main(void){

    char multiple[] = "a string";
    char *p = multiple;

    for (int i = 0; i < strnlen(multiple,sizeof(multiple)); ++i) {
        printf("multiple[%d] = %c * (p+%d) = %c &multiple[%d] = %p p+%d = %p\n",
               i, multiple[i], i, *(p+i), i, &multiple[i], i, p+i);
    }

    return 0;
}

代码示例3:

<!-- lang: cpp -->

/*
 输出:
 address p+0 (&multiple[0]):140734747675568 *(p+0) value: 15
 address p+1 (&multiple[1]):140734747675576 *(p+1) value: 25
 address p+2 (&multiple[2]):140734747675584 *(p+2) value: 35
 address p+3 (&multiple[3]):140734747675592 *(p+3) value: 45
 Type long occupies: 8 bytes


 这次得到了完全不同的结果
 指针p设置为multiple的地址
 而multiple是long类型的数组
 该指针最初包含数组中第一个字节的地址,也就是元素multiple[0]的第一个字节
 这次地址转换为unsigned long long后,用%llu转换说明符显示,所以他们是十进制的
 可以看到,每个地址的值都比上一个地址大了8
 这说明,给地址加1时,就表示要访问该类型的下一个变量
 由于将long类型强制转换为了无符号的longlong类型,所以占8个字节,就会给地址加8
*/
int main(void){

    long multiple[] = {15L, 25L, 35L, 45L};
    long *p = multiple;

    for (int i = 0; i < sizeof(multiple) / sizeof(multiple[0]); ++i) {
        printf("address p+%d (&multiple[%d]):%llu *(p+%d) value: %ld\n",
                    i, i, (unsigned long long)(p+i), i, *(p+i));
    }

    printf("\n Type long occupies: %d bytes\n", (int)sizeof(long));

    return 0;
}

你可能感兴趣的:(c,编程语言,C语言)