C语言学习笔记5

数组、指针、数组指针、指针数组

数组和指针的用法

    int num[5] = { 10, 20 ,30 ,40 ,50};
    int *p = num;
type打印时的值第二次打印的值源地址的值(数组内的值)
*p++102010
*(p++)
*和++的优先级相同,根据结合性是从右往左,所以p先和后自增运算符++相结合,++操作会在表达式完成后进行自增,也就是先取出p地址里的值,然后p的下标后移一位
type打印时的值第二次打印的值源地址的值(数组内的值)
*++p203020
*(++p)
*和++的优先级相同,根据结合性是从右往左,所以p先和前自增运算符++相结合,++操作会在表达式执行时立即完成,也就是先把p的下标后移一位,然后再取出这个下标的值
type打印时的值第二次打印的值源地址的值(数组内的值)
*(p++)101111
根据优先级,小括号优先级最高,p先和*相结合,立刻取出这个下标的值,然后这个值和后自增运算符++结合,因为是后自增,++操作将在表达式完成后进行自增,也就是先打印p下标的值,然后再将这个值自增1
type打印时的值第二次打印的值源地址的值(数组内的值)
++*p111211
++(*p)
*和++的优先级相同,根据结合性是从右往左,所以p先和取址运算符*相结合, 然后这个值和前自增运算符++相结合,也就是因为是前自增,++操作将在取值完成立即自增,也就是先取出p下标的值,然后再将这个值自增1,同时打印出来

总结
* 如果一个表达式有多个运算符,那么悠闲进行优先级判断,先执行优先级高的运算符
* 如果运算符优先级相同,那么就看结合性,根据结合方向来做运算
结合性
* 从左往右:简称左结合,变量名和表达式在运算符两侧,运算顺序是从左向右的(小括号 () 、中括号 [] 、成员选择 , 、成员选择 -> 、双目运算符 、逗号运算符)
* 从右往左:简称右结合,变量名和表达式在运算符两侧,运算顺序使从右往左的(单目运算符 、三目运算符 、赋值类运算符)

指针数组 和 数组指针

指针数组

  • 定义形式
    int *p[n] = { 0 };
根据优先级,[]的优先级高于*,所以 p 先和 [n] 相结合,说明 p 是一个数组名,然后再和 int* 结合,说明这个数组里每个元素都是一个 int 类型的指针

示例
* 使用指针数组接收多个数据的地址

#include <stdio.h>

int main(void)
{
    int a[3] = {10, 20, 30};
    int *p[3];
    for(int i = 0; i < 3; ++i)
    {
        p[i] = &a[i];
    }
    for(int i = 0; i < 3; ++i)
    {
        printf("%p\n", p[i]); // 打印数组内每个元素的值,也就是 a 的每个元素的地址
        printf("%d\n", *(p[i])); // 取出地址里的值,也就是数组 a 的每个元素的值
    }
}
  • 使用指针数组存储多个字符串
#include <stdio.h>

int main(void)
{
    char *str[5] = 
    {
        "ISO/IEC9899:2011",
        "Programming",
        "Dennis Ritchie",
        "C",
        "Beol Labs"
    };
    char *str1 = str[1]; // 定义一个字符指针,指向指针数组 str 第二个字符串的地址
    char *str2 = *(str +3); // 定义一个字符指针,指向指针数组 str 第四个字符串的地址
    char ch1 = *(*(str + 4) + 2); // 定义一个字符变量,接受了指针数组第五个字符串的第三个字符
    char ch2 = (*str + 5)[7]; // 定义一个字符变量,接收指针数组第一个字符串的第 5+7 个字符
    char ch3 = *str[0] + 6; // 定义一个字符变量,接收指针数组第一个字符串的第一个字符,ASCII再加上6

    printf("str1 = %s\n", str1);
    printf("str2 = %s\n", str2);
    printf("ch1 = %c\n", ch1);
    printf("ch2 = %c\n", ch2);
    printf("ch3 = %c\n", ch3);

    return 0;
}

输出结果为

    str1 = Programming
    str2 = C
    ch1 = o
    ch2 = 2
    ch3 = O

数组指针(行指针)

  • 定义形式
int (*p)[n];

int a[3][4] = { 0 };
int (*p)[4] = a;

int a[3][4][5] = { 0 };
int (*p)[4][5] = a;
() 和 [] 优先级相同,但是结合性是从左往右,所以 p 先和 * 相结合,说明 p 是一个指针,在和 int[] 相结合,说明这个是一个指向 int 类型数组的指针

示例

void func(int p[2][3], int n);
void func(int p[][3], int n);
void func(int (*p)[3], int n);
{
    printf("%d\n", p[1][2]); // 打印第二行第三个元素的值    : 60

    printf("%d\n", **p); // p 是行指针,*p是取出当前行当前列的地址,再通过*取值运算符,取取出这一列下标的值 : 10

    printf("%d\n", (*p + 1)[2]); // 先取出列地址,往后移动一位(一个int长度),再以这个列地址为行首,取出这一行的第三个元素的值 : 40

    printf("%d\n", *(p + 1)[0]); // 先将行指针往后移动一位(三个int长度),再得出这一行的第一列元素的地址,再通过取址运算符*,得出这一列的值: 40

    printf("%d\n", *(*p + 1)); // 先取出列地址,往后移动一位(一个int长度),再取出这一列的值: 20

    printf("%d\n", *(p[1] + 2)); // 先取出p[1]的列地址,往后移动二位(二个int长度),在取出这一列的值: 60

    printf("%d\n", *(*(p + 1)));
    // 先把行指针后移一位(三个int长度),然后取出这一列的地址,再通过这个列地址取出里面的值: 40

    printf("%d\n", *((*p + 1) + 2)); // 先取出列下标的地址,往后移动一位(一个int),再往后移动二位(二个int),再取出这个下标的值:40

    printf("%d\n", *(*p + 1) + 2); // 先取出列下标的地址,往后移动一位(一个int),再取出这个列元素的值,再把这个值加上2: 22

}

int main(void)
{
    int a[2][3] = {{10, 20, 30}, {40, 50, 60}};

    func(a, sizeof(a) / sizeof(int));

    return 0;
}

总结
* 指针数组:看后面两个字,就是一个数组,这个数组每个元素都是一个指针,这个数组在内存空间里占了 n 个指针大小的储存空间
* 数组指针:看后面两个字,就是一个指针,这个指针指向了一个数组,这个指针在内存空间里占了 1 个指针大小的存储空间
* 二级指针p 和 二维数组p 的区别:

int **p = NULL; 是一个整型的二级指针,p 是可变的变量,我们可以让它指向任何我们想让它指向的地方这个变量只占 48 个字节,所以不需要单独指定大小
int p[n][m] = {0}; 是一个整型的二维数组,p 指向一块连续的内存空间的首地址,我们可以用 p 找到这块内存空间,这块空间占用了 sizeof(int) * n * m 个字节大小
p 是一个不可变的常量,只能永远的指向这块内存
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值