(三)学习笔记 c语言基础 指针

1.定义指针变量

数据在内存中的地址也称为指针,如果一个变量存储了一份数据的指针,我们就称它为指针变量。

int a = 1;
int *p = &a;

通过指针变量p获取数据:

printf("%d", *P);

星号*主要有三种用途:

1)表示乘法,例如int a = 1, b = 2, c;  c = a * b;,这是最容易理解的
2)表示定义一个指针变量,以和普通变量区分开,例如int a = 100;  int *p = &a;
3)表示获取指针指向的数据,是一种间接操作,例如*p = 100;  b = *p;

2.指向数组的指针

Array是一系列具有相同类型的数据的集合,每一份数据叫Element(数组元素)。数组中的所有元素在内存中是连续排列的。
如:

int arr[] = {1,2,3,4,5,6};

定义数组时,要给出数组名和数组长度,数组名可以认为时一个指针,它指向数组的第0个元素。

*(arr + 1) 获取的数据和arr[1]等价

arr本身是一个指针,可以直接赋值给指针变量p:

int *p = arr;

指针疑惑

*p++ 等价于*(p++),表示先取得第n个元素的值,再将p指向下一个元素
*++P 等价于*(++p),会先进行++p运算,使得p的值增加,指向下一个元素,整体上相当于*(p+1)
(*p)++ 等价于对指针变量p获取的数值做加1操作。
3.字符串指针(指向字符串的指针)

c语言没有特定的字符串类型,我们通常时将字符串放在一个字符数组中。

int main()
{
    char str[] = "Say Hello!";
    int len = strlen(str);
    printf("%s\n", str);

    for(int i = 0; i < len; i++){
        printf("%c", str[i]);
    }
}

指针和数组的规则同样也适用于字符数组:

int main()
{
    char *str = "Say Hello!";
    int len = strllen(str);
    printf("%s\n", str);
   
    for(int i = 0; i < len; i++){
        printf("%c", str[i]);
        //使用*(str + i)
        printf("%c", *(str + i));
    }
}

C语言有两种表示字符串的方法,
1)是字符数组:字符数组存储在全局数据区或栈区,使得字符数组可以读取和修改

char str[] = "Say Hello!";
str = "you you!";        //正确
str[1] = 'A'            //正确

2)是字符串常量:字符串存储在常量区,只能读取不能修改

char *str = "Say Hello!";
str = "you you!";       //正确,可以更改指针变量本身的指向。
str[1] = 'A';           //错误,不能对常量进行写入。
*(str + 1) = 'A';       //错误,不能对常量进行写入。

4.指针变量作为函数参数

#include 
void swap(int *p1, int *p2){
    int temp;  //临时变量
    temp = *p1;
    *p1 = *p2;
    *p2 = temp;
}
int main(){
    int a = 1, b = 2;
    swap(&a, &b);
    printf("a = %d, b = %d\n", a, b);
    return 0;
}

5.指针作为函数返回值

C语言允许函数的返回值是一个指针(地址),我们将这样的函数称为指针函数。

int *max(int *a, int *b)
{
    if (*a > *b) {
        return a;
    }
    else{
        return b;
    }
}

int main()
{
    int a = 10;
    int b = 20;
    int *c = max(&a, &b);
    printf("max num is %d\n", *c);
}

6.二级指针(指向指针的指针)

指针变量也是一种变量,也会占用存储空间,也可以使用&获取它的地址。C语言不限制指针的级数,每增加一级指针,在定义指针变量时就得增加一个星号*。p1 是一级指针,指向普通类型的数据,定义时有一个*;p2 是二级指针,指向一级指针 p1,定义时有两个*。
int main()
{
    int a = 1;
    int *p1 = &a;
    int **p2 = &p1;
    printf("%d, %d, %d", a, *p1, **p2);
}

解析:*p2指向的是指针变量p1的值,**p2是指针变量p1的获取数据的值(*p1)

7.指针数组

如果一个数组中的所有元素保存的都是指针,那么我们就称它为指针数组。

int main()
{
    //整形指针数组
    int a = 1, b = 2, c = 3;
    //定义一个指针数组
    int *arr[3] = {&a, &b, &c}; //可以不指定长度
    //定义一个指向指针数组的指针
    int **parr = arr;
    printf("%d, %d, %d\n", *arr[0], *arr[1], *arr[2]);
    printf("%d, %d, %d\n", **(parr+0), **(parr+1), **(parr+2));
    
    //字符串数组和指针数组
    char *str[3] = {
        "hello one!",
        "hello two!",
        "hello three!"
    }
    printf("%s\n%s\n%s\n", str[0], str[1], str[2]);
    
    //字符串指针数组
    char *str0 = "hello one!";
    char *str1 = "hello two!";
    char *str2 = "hello three!";
    char *str[3] = {str0, str1, str2};
    printf("%s\n%s\n%s\n", str[0], str[1], str[2]);
    
    return 0;
}

8.二维数组指针

int main()
{
    int a[2][2] = {{1,2}, {3,4}};
    //定义一个指向a的指针变量p;
    int (*p)[2] = a;    //括号中*表明p是一个指针,它指向一个数组。[]的优先级高于*,所以()是必须要加的。如果写成*p[2]就成指针数组了。
    printf("%zu\n", sizeof(*(p+1)));
    
    int i,j;
    for(i=0; i<2; i++){
        for(j=0; j<2; j++) 
        printf("%2d  ",*(*(p+i)+j));
        //printf("%2d  ", (*(p + i))[j]);
        printf("\n");
    }
    return 0;
}

9.函数指针

一个函数总是占用一段连续的内存区域,函数名在表达式中有时也会被转换为该函数所在内存区域的首地址,这和数组名非常类似。我们可以把函数的这个首地址(或称入口地址)赋予一个指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数。这种指针就是函数指针。

#include 
//返回两个数中较大的一个
int max(int a, int b){
    return a>b ? a : b;
}

int main(){
    int a = 1, b = 2;
    //定义函数指针
    int (*pmax)(int a, int b) = max;
    int maxFunc = (*pmax)(a, b);
    
    printf("maxFunc: %d\n", maxFunc);
    return 0;
}

pmax 是一个函数指针,在前面加 * 就表示对它指向的函数进行调用

注意:使用指针变量之前一定要初始化,否则就不能确定指针指向哪里(野指针),如果它指向的内存没有使用权限,程序就崩溃了。对于暂时没有指向的指针,建议赋值NULL。

下一篇:(四)学习笔记 c语言基础 结构体
上一篇:(二)学习笔记 c语言基础 数据类型

你可能感兴趣的:(C学习)