C语言指针和函数学习笔记2

一、指针部分

1.指针和二维数组

二维数组的存储方式:

在内存中,二维数组是按行优先的顺序连续存储的。例如,对于一个m*n的二维数组arr[m][n],数组元素会先存储第一行的所有元素,接着存储第二行的元素,依此类推。

1.1指针访问二维数组元素

使用指向数组元素的指针:

可以定义一个指向二维数组元素类型的指针,然后通过指针的偏移来访问二维数组的元素。

语法格式:

数据类型 (*变量名)[数组中元素的个数];
如果让数组指针指向二维数组,那么[]中的数据和二维数组的列数保持一致

因为数组指针和二维数组的偏移量都是一整行元素,所以数组指针可以直接指向二维数组。 

#include <stdio.h>
int main(int argc, const char *argv[])
{
    int arr[2][3]={23,13,8,34,9,2};
    int arr1[4]={1,9,4,2};
    printf("arr=%p\tarr+1=%p\n",arr,arr+1);  //偏移量是12
    printf("arr1=%p\tarr1+1=%p\n",arr1,arr1+1); //偏移量是4
    int *p=arr;   //让int*类型的指针指向二维数组,因为偏移量不一致会报警告
    printf("p=%p\tp+1=%p\n",p,p+1);
    return 0;
}

数组指针指向二维数组

#include <stdio.h>
int main(int argc, const char *argv[])
{
    int arr[2][3]={12,90,34,
                   2,81,52};
    //定义一个数组指针指向二维数组
    int (*p)[3]=arr;

    //[i]<==>*(+i)
    printf("arr[1][2]=%d\n",arr[1][2]);
    //arr[1][2]<==>*(*(arr+1)+2)
    //arr[0][0]<==>**arr
    printf("*(arr+1)=%p\tarr[1]=%p\t&arr[1][0]=%p\n",*(arr+1),arr[1],&arr[1][0]);
    //p+1表示指向第二行一整行元素,*(p+1)表示指向第二行第一个元素
    printf("*p=%p\t*(p+1)=%p\n",*p,*(p+1));

    printf("%p\n",*p);  //第一行第一列元素的首地址
    printf("%p\n",*p+2); //*p先结合,*p表示第一行第一列元素的首地址,
    //*p+2表示对第一行第一列的列地址向后偏移两个数据单位
    //*p+2表示第一行第三个元素的首地址
    printf("%p\n",*(arr+1)+2); //arr+1行地址偏移一行元素,偏移到第二行
    //*(arr+1) --->取*降级成列地址,表示第二行第一个元素的地址
    //*(arr+1)+2 列地址向后偏移两个数据单位,表示第二行第三个元素的地址
    printf("%d\n",**p+2);  //14   **p=12, **p+2=14
    printf("%d\n",*(*p+1)); //90   *p是第一个元素的地址,向后偏移一个字节得到第二个元素的地址
    printf("%p\n",p+2);   //p是一个行指针,指向第一行元素,p+2指向第三行元素,访问越界
    printf("%d\n",*p[2]); //先执行p[2]<==>*(p+2) *p[2]<==>**(p+2)  行指针p向后偏移2行本身就是非法访问
    return 0;
}

2.行地址和列地址

行地址:

二维数组的数组名和数组指针都是行地址,偏移量为一行元素

列地址:

一维数组的数组名和一级指针都是列地址,偏移量为一个数据单位

3.指针数组

本质是一个数组,保存的是多个指针

#include <stdio.h>
int main(int argc, const char *argv[])
{
    //如果想要用二维数组存储多个字符串时,列数需要和最长的字符串一致,会造成空间的浪费
    char str[][20]={"hello","hhhhhhhhhhhhhhhhhh","i"};
    printf("%ld\n",sizeof(str));
    //因为字符串会被在字符串常量区创建,所以可以直接用指针指向字符串常量,使用指针数组存储多个指针
    char *arr[3]={"hello","hhhhhhhhhhhhhhhhhh","i"};
    puts(arr[1]);  //通过指针数组输出
    printf("arr[1]=%p\n",arr[1]);
    printf("2\n");
    arr[1]="world";   //改变指针数组中第二个指针的指向
    printf("arr[1]=%p\n",arr[1]);
    printf("1\n");
    printf("%c\n",*arr[1]);
    *arr[1]='a';  //arr[1]是字符串常量区中字符串world的首地址]
    //*arr[1] 对world的首地址取*
    r

4.main函数的外部传参

rgc:表示外部参数的个数
argv[]:是一个指针数组,保存多个字符类型的指针,每个指针都指向一个外部参数

主函数如何进行外部传参:
./a.out  参数1  参数2  参数3···
(./a.out是第一个参数,后续的每一个参数中间以空格分隔)

5.二级指针

数据类型 **指针变量名;
保存一级指针的地址的

多级指针一定是指向上一级指针的,所以偏移量是固定的,
64位操作系统偏移量8Byte,32位操作系统偏移量4Byte
#include <stdio.h>
int main(int argc, const char *argv[])
{
    int a=90;
    int *p=&a;
    printf("%ld\n",sizeof(p));  //8
    //int *p1=&p;  一级指针的偏移量是一个基本数据类型的大小,
    //如果使用一级指针保存另一个一级指针的地址,不能保证偏移时偏移整个指针的空间
 //printf("p1=%p\tp1+1=%p\n",p1,p1+1);

    //需要使用二级指\针保存一级指针的地址
    int **p2=&p;
    printf("p2=%p\tp2+1=%p\n",p2,p2+1);
    return 0;
}

二、函数部分

函数是实现某些功能的代码

1.定义

返回值类型 函数名(参数列表)
{
    函数体;
    return 返回值;  //如果返回值类型是void,return可以不写
}

返回值:返回给主调函数处的结果,如果主调函数处不需要结果就不用写返回值(如果函数没有返回值,就写void)
参数列表:如果实现函数功能需要外部传递参数,就需要写参数列表,如果实现函数功能时不需要外部传递参数,不需要写参数列表

2.函数的分类

i)无参无返回值函数

void func()
{
    printf("hello world\n");
}

ii)有参无返回值函数

void func(int a,int b)
{
    printf("%d\n",a+b);
}

iii)无参有返回值函数

int func()
{
    int c;
    scanf("%d",&c);
    return c;
}

iv)有参有返回值函数

int func(int a,int b)
{
    return a+b;
}

3.简单函数的实现

#include <stdio.h>
//自定义函数
int add(int a,int b)
{
    return a+b;
}
int main(int argc, const char *argv[])
{
    int x=9,y=3;
    printf("%d\n",add(x,y));
    int ret = add(x,y);  //因为add函数有返回值且为int类型
    //所以可以直接使用int类型的变量接收函数的结果
    return 0;
}

4.函数声明

函数声明的作用:引导编译器正确找到调用的函数,声明了函数的返回值类型和参数类型

函数声明的时机:函数定义(实现)放在了函数调用的后面(和主调函数不在同一个文件)需要用到函数声明

5.函数的值传递和地址传递

自己定义函数实现两个数的交换

#include <stdio.h>
int main(int argc, const char *argv[])
{
    int a=12,b=34;
    printf("a=%d\tb=%d\n",a,b);
    //用算数求和的方式
    a=a+b;   //46
    b=a-b;   //12
    a=a-b;   //34
    printf("交换后\n");
    printf("a=%d\tb=%d\n",a,b);
    //用异或的方式交换两个数
    a=a^b;
    b=a^b;
    a=a^b;
    printf("交换后\n");
    printf("a=%d\tb=%d\n",a,b);
    //a=a^b
    //0110 ^ 0011  = 0101
    //b=a^b
    //0101 ^ 0110  = 0011
    //a=a^b
    //0101 ^ 0011  = 0110
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值